Synchronize VoiceStudio release metadata, lockfiles, installers, container references, documentation, and the dated v0.5.2 changelog after all planned fixes landed.
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.
* feat(install): prebuilt-app installs by default, --source opt-in, --version picker
- install.sh: default mode now downloads the verified release asset
(dmg/AppImage + SHA256SUMS check) instead of cloning and building;
--source keeps the previous clone-and-build flow; --version pins a release
- install.ps1: same split — msi download with checksum verification and
setup wizard by default; -Source (or VOICESTUDIO_INSTALL_MODE) for the
source build; VOICESTUDIO_VERSION picks a release
- worker landing page documents the modes
* fix(install): CI smoke covers binary + source modes; hdiutil output parse
- drop -quiet from hdiutil attach (it suppresses the mount-point line the
script parses — caught by the macOS smoke)
- install.ps1 runs msiexec silently under CI, wizard interactively
- smoke verifies binary installs per OS (app bundle / AppImage / MSI
registry entry) and keeps full source coverage behind --source
* ci(install): check HKCU/WOW6432Node too — Tauri MSI registers per-user
* fix(install): rename $version — collides with bun installer's $Version under iex
* 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
Renames what users see. The app, the installers, the window title, the
docs and all 21 locales now say VoiceStudio, with "(previously
OmniVoice-Studio)" noted near the title of each doc surface so people
recognise it.
Deliberately NOT renamed, because renaming any of them silently breaks
an existing install — there is no legacy-path fallback anywhere in this
codebase:
- bundle identifier com.debpalash.omnivoice-studio (MSI UpgradeCode,
macOS TCC grants, managed venv, WebView localStorage, the
single-instance lock)
- data directories OmniVoice / .omnivoice and omnivoice.db
- the ~150 OMNIVOICE_* environment variables
- the X-OmniVoice-* HTTP headers (a wire protocol)
- the published Docker image paths
- the OmniVoice ENGINE, which is a model name and not this product
tests/test_identity_paths_survive_the_rename.py pins every one of those
so a future well-meaning sweep cannot orphan a user's library.
Linux .deb users install a new package name and should apt remove
omnivoice-studio; that note is in the changelog.
The repository was renamed. 724 references across 59 files now point at the new URL — README badges, docs, install guides, the updater's releases API call, CONTRIBUTING, the Colab link and the probe harness. GitHub redirects the old URLs, so nothing was broken in the meantime.
Deliberately NOT renamed, because each breaks something on a user's machine: the Tauri bundle identifier (the path to every existing user's data), /usr/lib/omnivoice-studio and the compose container names, and the published Docker image paths.
The image path needed a code change to STAY still: docker.yml derived it from github.repository, so the next build would have published to ghcr.io/debpalash/voicestudio while Docker Hub, a hardcoded literal, stayed put — everyone pulling the documented GHCR path would have kept receiving the last pre-rename image forever. It is now pinned, with a test that fails if it ever derives from the repo name again.
Also makes the probe's repo-name assertion shape-based: it hardcoded the old name and failed on every PR after the rename while the code it tests worked perfectly.
Follow-up to #165 — the same uname Darwin/Linux-only pattern in the other
dev scripts:
- smoke-test.sh: add MINGW*/MSYS*/CYGWIN* detection + Windows data paths
(%APPDATA%/%LOCALAPPDATA%, per backend/core/config.py) + .exe binary suffix.
- run.sh: Windows detection + %LOCALAPPDATA% log dir; backend launch already
uses 'uv run' which is cross-platform.
- install.sh: detect Windows and print a clear 'use the .msi / WSL' message
(the brew/apt system-dep installer can't run on native Windows) instead of
silently treating it as Linux.
build-omnivoice-tts.sh already handled windows-x86_64; record-reference.sh
already guards macOS-only with a clear message.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat: onboarding demos, opt-in bug reporting, error-docs deeplinks + issue triage
Working-tree snapshot bundling several in-flight workstreams (v0.3.0):
- Onboarding/demo system: DemoPresetGrid, DictationDemo, DubbingDemo components
+ tests, render scripts (render_demos_omnivoice.py, build_demos.sh,
build_dub_demo.sh), personalities preview URLs, alembic 0002 voice-profile
demo fields.
- Opt-in bug reporting: ReportBugButton (prefilled GitHub-issue URL path).
- Error transparency UX: errorDocsMap deeplinks + BootstrapSplash/error wiring.
- Dub workspace: DubSegmentRow/Table, WaveformTimeline, dubSlice tweaks.
- Issue triage: .planning/issue-clusters/ (plan-01..05 root-cause masters,
GH #128-#132).
- CLAUDE.md: hard rule — everything ships on v0.3.0, no version bumps.
KNOWN GAP (why this is a draft): the generated demo audio assets are NOT in
this tree, and backend/assets/samples/demo_voice.wav is deleted. onboarding.py
guards the missing file (skips seeding the demo profile with a warning), so no
crash — but first-run Launchpad will be empty and /demo_audio/ preview URLs
404 until assets are regenerated via scripts/build_demos.sh. Do not merge
before regenerating + committing the demo assets.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(dub): timing strategies — kill audio compression, add Concise + Stretch Video
Replaces the current audio time-compression default (atempo squeeze to fit
slot) that produced chipmunk/alien output on high-density target languages
like Bengali. Two new user-selectable modes; legacy behaviour kept behind
an explicit "Strict slot" choice.
New `DubRequest.timing_strategy` enum (default "concise"):
- "concise" Translator trims text to fit at natural rate; if it
still overflows, hard-trim at slot with a fade so we
never overlap the next speaker. Surface overflow_s
per segment so the user can shorten the text.
- "stretch_video" Audio plays at natural 1.0× rate. Backend computes a
per-segment new timeline; persists a video_stretch_plan
on the job. Mux step (dub_export) builds an ffmpeg
trim+setpts+concat filter graph that stretches each
segment's video portion to match the natural-rate dub
audio. Gaps/pre-roll/tail pass through at 1.0×.
Sub burn under stretch_video is skipped in one pass
(cues would drift).
- "strict_slot" Legacy atempo squeeze. Retained for back-compat.
Director rate-bias side-effect (seg_speed *= bias) now gated on strict_slot
only, so "urgent"/"slow" direction tokens keep their instruct effect in
the new modes without chipmunking.
Per-segment fit_status emitted in the SSE done event:
{status: "fits" | "overflows" | "video_stretched", overflow_s?, stretch_ratio?}
DubSegmentRow's "Sync: 100%" badge (which was lying — sync_ratio was always
~1.0 because the TTS loop pre-trimmed to slot) is replaced with a truthful
"Fits / Overflows +Ns / Video 1.18×" label.
Frontend:
- prefsSlice.timingStrategy (persisted, store v3→v4 with safe migrate).
- DubTab footer Segmented control: "Concise · Stretch Video · Strict slot".
- useDubWorkflow passes timing_strategy on /dub/generate; consumes fit_status.
Tests: tests/test_dub_timing_strategy.py — 13 cases covering schema
defaults/validation, _build_video_stretch_filter_graph (pre-roll, gap,
tail, empty-plan early return, post-subtitle chain-in), and
_video_stretch_plan_for guards. 30/30 existing dub tests still pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(waveform): surface missing source as "Source media missing" instead of code-4 black box
When a project's underlying media file is gone (moved or deleted between
save and reload) the <video> element fires MediaError code 4 and the
companion audio fetch returns HTTP 404 — both were silently warned to
the console while the user stared at an unresponsive black panel and an
empty waveform.
- WaveformTimeline now flips loadError when the video element rejects
code 3 (decode) or 4 (src not supported), and tracks `sourceMissing`
separately so the error UI can name the actual problem.
- The audio decode fallback chain catches HTTP 404 specifically and
treats it as source-missing instead of loading silent empty peaks —
an empty waveform on a deleted source is more confusing than a clear
"Re-upload the video to continue" message.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(tray): "Show OmniVoice" reloads when the webview is blank
When the dev Vite server restarts (or the main window is created before
the backend is ready), the webview load fails and the window is left
with `<body></body>` plus a "Could not connect to the server" console
error. Clicking "Show OmniVoice" from the tray menu just re-showed the
broken window — there was no recovery path short of quit+relaunch.
Now the show handler runs a tiny eval after `show()`/`set_focus()` that
calls `location.reload()` only when `document.body.childElementCount === 0`.
A healthy window doesn't blink (body is non-empty); a blank one
self-recovers as soon as the user clicks Show.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(#133): bug-report diagnostics field mapping + drop unused imports
Address PR #133 review:
- ReportBugButton: /system/info exposes `platform` + `device`, not
`os`/`torch_device`/`gpu` — those reads silently dropped OS/GPU from every
bug report. Map to the real fields (CodeRabbit). Also remove the dead
`home` local in stripHome (CodeQL unused-variable).
- DictationDemo: drop unused `Loader` import (CodeQL unused-import).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>