On Windows with a non-UTF-8 system code page, a venv path containing non-English characters (commonly a CJK username) killed the interpreter during startup, before any VoiceStudio code ran — Python reads .pth files in the active code page, and uv's editable install writes the project path there in UTF-8. The backend could never start, and the setup screen only said it had stalled.
A new or genuinely broken environment now builds at an ASCII-safe short path; an existing environment that starts cleanly is never relocated. When no safe path can be produced, the app says so before downloading rather than after. The failure message names the cause and a remedy that works for the install mode in use, since portable installs ignore the setting managed installs use.
Fixes#1783.
The compute-time error told users to raise a generation timeout that had no control anywhere in the app — the only knob was an environment variable, and on Windows the docs explicitly warn against the usual way of setting one. Both budgets are now editable in Settings under Performance & Device, persisted and applied on the next start.
Two defects found in review and fixed here rather than shipped: an explicit universal budget silently overrode a separately saved CPU budget, so the CPU row would have looked like it worked and done nothing; and a value already set in the environment shadowed the saved preference while the panel still reported success. A shadowed row now says so instead. Long-input warnings also fire on Apple Silicon, which gets the accelerated budget and was the device in one of the duplicate reports.
Fixes#1787. Closes the reports tracked in #1774 and #1778.
Opt-in live preview for dub segments: edits debounce into a streamed /ws/tts synthesis played through the chunk player, with cancellation preserved through buffered playback. Maintainer fixes: /ws/tts added to the backend ticket allowlist (feature was dead off-loopback), handshake failures surface a toast, loopback-only plaintext refusal reverted to keep the documented remote-GPU setup working, PCM16 decode hardened. Thanks @mvanhorn!
* feat(dub): project-level drag casting board
Adds an expandable Casting Board to the dub editor's CAST strip: speaker
rows (with the auto-clone chip when the extractor found a usable passage)
and draggable voice chips — Default, clone profiles, design presets.
Dropping a chip on a speaker writes the exact fields the CAST <select>
always has (profile_id + merge_parts/merge_parts_original attribution),
via a shared assignSpeakerProfile helper both views now call, so the
dropdowns stay in sync and job persistence is unchanged. Keyboard path:
focus a speaker row, pick from a listbox (arrows/Enter/Escape).
The pre-existing CAST dropdown strip moves verbatim into the new
CastingBoard.jsx (DubLeftColumn shrinks below 800 lines; the new file
holds the 300-line soft cap). Styles extend the .dub-cast-* cluster in
index.css. Six new i18n keys translated in all 21 locales.
Co-authored-by: Matt Van Horn <mvanhorn@users.noreply.github.com>
* docs: changelog + roadmap entries for the casting board (#1767)
Co-authored-by: Matt Van Horn <mvanhorn@users.noreply.github.com>
* fix(casting): validate and preserve speaker assignments
* fix(casting): recover cleared merged assignments
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Palash Debnath <4178343+debpalash@users.noreply.github.com>
Synchronize VoiceStudio release metadata, lockfiles, installers, container references, documentation, and the dated v0.5.2 changelog after all planned fixes landed.
Adds opt-in word-timed ASS karaoke captions while preserving the existing line-caption default.\n\nCo-authored-by: Matt Van Horn <mvanhorn@users.noreply.github.com>
An LLM agent pays for every byte it receives, and generate_speech returned
each WAV as base64 inline - a short clip already brushed per-result limits.
This adds two knobs in the OMNIVOICE_* family, the pattern the ElevenLabs
MCP settled on (OUTPUT_MODE + a BASE_PATH security boundary):
- OMNIVOICE_MCP_OUTPUT_MODE = resources (default, the original contract) |
files | both. In files mode generate_speech returns audio_url (the render
the backend already keeps, served at /audio/<id>.wav) and, when a base
path is set, output_path - the WAV written into that directory.
- OMNIVOICE_MCP_BASE_PATH: the one directory agents may read from and
receive files in. transcribe(audio_path=) and clone_voice(ref_audio_path=)
read only inside it (relative paths resolve against it, absolute ones must
lie within it, symlinks resolved before the check); with no base path,
path arguments are refused with a reason.
- OMNIVOICE_MCP_TIMEOUT_S (default 120): the tools' backend timeout, since a
CPU host serializes generations and an agent queued behind another render
outlasted the fixed budget with an empty-message ToolError.
Also: transcribe and clone_voice share one input helper (data-URI tolerance
now covers transcribe too), the upload filename carries the sniffed
extension, and the reply is built with json.dumps instead of hand-rolled
JSON. Tests cover the mode parsing, the boundary (escape and missing-base
refusals), both input lanes, all four reply shapes, and the timeout knob.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Refreshes README and linked docs with accurate installation, platform, privacy, API, and model-license guidance; documents local gigastt; and pins OpenAI-compatible ASR traffic to the configured secure origin. Closes#1736.
Closes #1716.\n\nDefines supported, best-effort override, unverified, and unsupported architecture evidence; marks RX 6700 XT/gfx1031 over WSL2 ROCDXG as unverified; and requires routing, utilization, and CPU-fallback evidence before claiming acceleration.
Closes#1718.
Adds structured pre-install and measured post-install disk costs, complete sidecar preflight accounting, strict authorization for recursive scans, and localized catalogue details with confidence and deduplication context.
Closes#1713
Adds a separately identified per-user MSI and updater channel, non-administrator install/uninstall verification, and fail-closed WebView2 handling for current-user installs.
* docs: add private production API deployment
* docs: harden private API deployment guidance
* docs: make proxy trust configuration executable
* docs: clarify private API trust boundaries
* docs: persist private service application data
Run uvicorn directly under the dev wrapper so a worker crash cannot hide behind a live reload parent. Preserve Python source reloads, restart isolated crashes with bounded diagnostics, and keep persistent crash loops loud.
Closes#1690.
Prepare the tested main branch for the v0.5.1 patch release with synchronized version sources, mirrors, lockfiles, release notes, and install guidance.
Closes#1687.
Fail before backend/window startup when Linux source hosts lack Enigo’s libxdo linker input or WebKit’s GStreamer audio sink. Print an exact distro package command, sync source-build docs, and lock the probes with deterministic tests.
Closes#1680Closes#1682
Fix fresh-clone desktop development by creating the required dist placeholder before Tauri starts, and make source setup install the selected CUDA or ROCm PyTorch stack consistently. Adds behavior-level cross-platform regression coverage.\n\nFixes #1664.\nFixes #1665.\n\nThanks @uberclokr for the contribution.
Move unbounded Stories and Audiobook project data to revisioned IndexedDB storage with bounded local fallback, durable clear tombstones, migration/recovery safeguards, and deadline-safe persistence before exits and relaunches.
Closes#1636.
* feat(install): one-command installer URL (.sh + .ps1) + 3-OS install smoke
- scripts/install.ps1: Windows source installer (winget deps, uv, bun,
clone, uv sync, frontend build); honors OMNIVOICE_PYTHON/OMNIVOICE_REGION
- infra/install-redirect: Cloudflare Worker serving /install with
User-Agent sniffing (curl -> sh, PowerShell -> ps1, browser -> landing
page); proxies live from main; /install.sh + /install.ps1 aliases
- scripts/install.sh: fix stale advertised URL (main/install.sh never
existed) and repo-root resolution so a local run no longer clones a
duplicate repo into ~/VoiceStudio (verified on macOS arm64)
- .github/workflows/install-smoke.yml: run both installers end-to-end on
ubuntu/macos/windows when they change
- docs-sync: install one-liners lead each platform guide; STRUCTURE.md
(#1626)
* fix(install): don't let a failed bun download pass silently
curl | sh runs an empty script and exits 0 when the download fails, so
a bun.sh hiccup surfaced much later as 'bun: command not found' (seen
on the macos-latest smoke runner). Fetch to a temp file, verify, fall
back to npm -g bun when node exists, and die with the manual command.
Same post-install verification for uv.
* fix(install): UTF-8 BOM for install.ps1 + quiet-style changelog entry
- tests/scripts/test_uninstall_ping.py requires shipped PowerShell
scripts with non-ASCII text to carry a UTF-8 BOM (Windows PowerShell
5.1 mis-decodes otherwise); same treatment uninstall.ps1 already gets
- test_changelog_style caps entries at ~400 chars
* fix(indextts): accept the config name upstream ships, and keep long text alive
Two independent defects, both reported on a working IndexTTS 2.5 install.
Install always failed. IndexTeam/IndexTTS-2.5 ships the model config as
config.yaml — at the pinned revision d0aa86e7 and at HEAD; config_v2_5.yaml
exists in no upstream revision. VoiceStudio demanded that name, so
_weights_floor_ok never found it and the install died claiming 'the download
was likely interrupted' when the download had been perfect. The only way
through was to hand-rename the file. Both names are accepted now, in the
installer and on the load path, so installs created with the workaround keep
working without a reinstall.
Long text was killed at 60s. infer() is one blocking upstream call that puts
nothing on the wire, and IndexTTS was the only sidecar still on the 60s
recv_timeout_s class default while pockettts and omnivoice-subprocess had both
raised theirs. Raising the default alone does not fix it — which is why the
reporter's RECV_TIMEOUT_S=3600 edit didn't help: progress frames are also what
report activity to the GPU pool's execution clock (#1367), so a silent sidecar
still trips the outer generate budget. The sidecar now heartbeats every 5s
while infer() runs (and during the cold model construction), _send takes a
lock so the beat thread can't interleave framing, and the deadline rises to
900s via OMNIVOICE_INDEXTTS_RECV_TIMEOUT_S.
test_indextts25_health_requires_25_config_name asserted the bug — that a
checkout holding only config.yaml is unhealthy — so it is rewritten to the
corrected contract, including that a genuinely truncated download is still
caught.
Fixes#1611
* test(indextts): follow the installed config name in the sidecar loader tests
Two more tests encoded the config_v2_5.yaml assumption, both asserting
cfg_path against a directory where no config existed at all — so they were
pinning the literal name rather than the resolution. They now lay down a real
checkpoints/ tree and assert the resolved path, including that a checkout
carrying the pre-fix hand-renamed config still resolves.
Caught by the full suite; the targeted runs during development did not reach
tests/backend/services/.
* test(indextts): event-driven heartbeat tests, real interleaving proof, precedence pin
Review round on #1619 — all four findings taken.
- The docs line naming 0.5.1 is version-neutral now ('Earlier installs') —
version labels are the owner's call.
- The heartbeat tests waited on wall-clock sleeps; they now block on a
per-write Event with a bounded deadline, so scheduler load can't flake them.
- The _send test asserted the lock EXISTS — a tautology. It now drives four
concurrent writers through a stream that yields between every byte and
asserts every frame decodes; verified fail-before by removing the lock
(torn frame) and pass-after.
- The precedence test deleted config.yaml before creating the renamed one, so
reversed precedence still passed. Both files now coexist for the assertion;
verified fail-before by reversing _CFG_NAMES.
* fix(setup): tolerate reserved memory in the RAM preflight, add OMNIVOICE_RAM_PREFLIGHT=0 escape hatch (#1618)
An "8 GB" machine reports ~7.8 GB usable (firmware/iGPU/kernel
reservations), so comparing OS-reported RAM against the marketing-size
8 GB threshold hard-blocked exactly the boundary hardware the minimum is
meant to admit — with no way past the wizard. Both thresholds are now
compared with a 7% reserved-memory allowance, and
OMNIVOICE_RAM_PREFLIGHT=0 downgrades a genuine fail to a warning for
users who accept the OOM risk (same opt-out shape as
OMNIVOICE_ASR_VRAM_PREFLIGHT).
Regression tests: backend/tests/test_ram_preflight_1618.py.
Docs: troubleshooting §1c.
* review: hermetic preflight stubs in tests; correct the escape-hatch doc
Greptile P1: the Settings panel can't set OMNIVOICE_RAM_PREFLIGHT (and the
blocker appears before setup completes anyway) — the doc now points at
PowerShell / shell env only.
CodeRabbit: stub _network_check and media_tools.summary so each RAM
assertion stays fast and offline (26s -> 6s locally).
* fix(dictation): refresh accessibility blocker
* docs(changelog): note accessibility refresh
* test(dictation): assert the native widget hide on accessibility grant
The recheck regression asserted only that the Accessibility pill text left
the DOM, so it still passed with hideWidgetWindow() removed and the native
capsule stranded on screen. Hold one stable getCurrentWindow().hide spy and
assert it after the poll (fails before the fix, passes after).