Commit Graph
31 Commits
Author SHA1 Message Date
basil-k-aji-dev a628ba161e fix: install cuDNN 8 compat libraries in the CUDA Docker image
The CUDA base image is pytorch/pytorch:2.8.0-cuda12.8-cudnn9-runtime, so it
ships cuDNN 9. CTranslate2 — WhisperX and faster-whisper — links cuDNN 8, and
its absence aborts the backend process outright rather than raising (#1371).

scripts/setup.py side-loads the cuDNN 8 libraries for source installs, but the
Dockerfile never did, so every CTranslate2 ASR engine was unavailable in Docker
and the demo synthesis timed out with libcudnn_ops_infer.so.8 missing.

Install the same nvidia-cudnn-cu12==8.9.7.29 shim during the image build,
deriving the target from sys.prefix so it matches where backend/core/cudnn8.py
searches rather than hardcoding the conda path — sys.prefix differs between the
conda-based CUDA image and the ROCm venv. Guarded to GPU_FLAVOR=cuda, since
ROCm does not use cuDNN, and --no-deps keeps the base image's torch stack
untouched. A post-install assert fails the build if no .so.8 libraries landed,
rather than letting it resurface as the same runtime warning.

Fixes #2050
2026-09-13 23:51:57 +05:30
Yang Fan ae25a6a594 docs: clarify Docker image architecture requirements 2026-09-10 13:51:58 +08:00
Palash Debnath 0ed7d2ec22 chore(release): prepare v0.5.2 (#1761)
Synchronize VoiceStudio release metadata, lockfiles, installers, container references, documentation, and the dated v0.5.2 changelog after all planned fixes landed.
2026-09-02 10:26:14 +05:30
Palash Debnath 97bfbfecd1 chore(release): prepare v0.5.1 (#1688)
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.
2026-08-28 11:27:59 +05:30
Palash Debnath eaa87ff041 fix(docker): require the Studio administrator key 2026-08-27 20:21:42 +05:30
Palash Debnath 9dc01d4b8b fix(docker): require admin key in quick starts 2026-08-27 19:31:55 +05:30
Palash Debnath 5a615d2c66 feat(workers): package headless GPU nodes (#1638) (#1648)
Closes #1638.\n\nPackages headless GPU workers with durable enrollment, bounded artifact handling, cross-platform lifecycle cleanup, and regression coverage. Incorporates CodeRabbit, Greptile, CodeQL, and platform-CI findings before merge.
2026-08-24 16:32:56 +05:30
Palash Debnath 6e1bb44e0d docs(docker): add product media to Docker Hub overview (#1593)
Add the current v0.5 engine-switching GIF plus Model Catalogue and gallery-save screenshots to the canonical Docker Hub overview using absolute raw GitHub asset URLs. Includes a changelog entry.
2026-08-20 01:10:21 +00:00
Palash Debnath 4dc90a7f4f docs(docker): refresh Docker Hub overview for v0.5 authentication (#1592)
Refresh current v0.5.0/0.5 tag examples, document API-key and share-PIN behavior, and require encrypted private-overlay access for remote deployments. Keeps the Docker install guide and changelog synchronized.
2026-08-20 00:40:18 +00:00
Palash Debnath 04410a458d Release VoiceStudio 5.0.0 (#1487)
Complete the VoiceStudio identity, release documentation, assets, version mirrors, and safe cross-platform development startup.
2026-08-11 04:05:45 +00:00
debpalash b991e59a0f fix(docker): keep ROCm runtime on guarded Python 2026-08-09 20:39:46 +00:00
Palash Debnath 93025d9a81 fix(dictation): stop the widget stranding an empty square, repair the swept data dirs (#1398)
The dictation hotkey could leave a blank dark square stuck on the desktop with no way to dismiss it. Three defects compounded: the tray listener's effect depended on [state], so it detached across an await on every state change and a press landing in that gap was lost; an idle pill renders null, so the window Rust had already shown was empty; and the opaque chrome background made that empty window a hard-edged square. Nothing could hide it — dismiss() is only reachable from the X button, Esc, or a post-session timer, none of which exist for a session that never started.

Fixed at the invariant rather than the call sites: the listener subscribes once for the component's lifetime, the widget window's chrome background is transparent, and an idle-but-visible window reconciles itself to hidden. The reconcile is polled (a dropped press changes no React state, so there is nothing to key an effect off) and aborts if its effect is torn down mid-check, so it can never hide a dictation that has just started.

Also in scope:

- The rename sweep had repointed three data-dir literals at a brand-named directory that does not exist, so smoke-test.sh verified a directory the backend never writes and desktop-prod.sh silently stopped clearing backend state on Windows. Both invisible on macOS, where they are usually run. A guard test now pins the assignments specifically.
- The dictation model picker's download sizes were wrong for all seven models, in both directions — Parakeet TDT v3 (the recommended default) understated 180 MB against an actual 670 MB, while the low-RAM fallbacks were overstated threefold, discouraging exactly the choice that would have helped. Measured from the published repos and pinned by a test.
- The 0.6B Parakeet models now decode on more threads, capped by host cores and still overridable.
- uninstall.ps1 gained a UTF-8 BOM (Windows PowerShell 5.1 mis-decodes its non-ASCII output without one), and sponsor.yml lost its last OmniVoice references.
2026-08-07 05:39:41 +05:30
Palash Debnath 5cab8e0149 feat: rename the product to VoiceStudio (previously OmniVoice-Studio)
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.
2026-08-07 01:30:58 +05:30
Palash Debnath a23e69d014 chore: point every repo reference at github.com/debpalash/VoiceStudio (#1394)
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.
2026-08-06 18:15:56 +05:30
c819372449 fix(install): make the torch pin reach the Colab and Docker installs (#1357) (#1369)
* fix(install): make the torch pin reach the Colab and Docker installs (#1357)

#1358 pinned the trio in `[tool.uv] constraint-dependencies`, which fixes
`uv sync` / `uv lock` / `uv run`. It does nothing for `uv pip install` --
that is the pip-compatible interface and ignores project-level uv
settings -- and `uv pip install --system --no-cache .` is exactly what
both the Colab notebook and deploy/Dockerfile run.

Measured on one Python 3.12 environment, same command, pin present:

    without --constraint:  torch 2.13.0  torchaudio 2.11.0  torchvision 0.28.0
    with    --constraint:  torch 2.8.0   torchaudio 2.8.0   torchvision 0.23.0

So the reported install path was still resolving the three on their bare
lower bounds (`torch>=2.4`, `torchvision>=0.19`), free to move torch past
a torchvision built for an older ABI -- which is the reported failure,
`operator torchvision::nms does not exist`, against the preinstalled
torchvision in Colab's /usr/local/lib/python3.12/dist-packages/.

The pins move to deploy/torch-constraints.txt and are passed explicitly
at both call sites. No local version segment, so PEP 440 matches the
base images' +cu128 and +rocm6.4 builds instead of replacing them -- the
property the ROCm image depends on.

Also extends the Docker guard, which asserted on torch and torchaudio
only, omitting the one package that actually broke. It now imports
torchvision.ops and touches nms, so an ABI mismatch fails the build
rather than shipping.

Recurrence: docker.yml builds only on push to main, never on a PR, so
nothing would have caught a silent regression here before it shipped.
tests/test_torch_constraints_are_applied.py fails if the file drifts from
pyproject, if either call site drops --constraint, if the Dockerfile stops
COPYing the file, or if the guard stops covering torchvision.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* test(install): assert the constraint in the argv, not the cell text (#1357)

CodeRabbit on #1369, both findings valid.

The notebook check scanned the whole cell, so it passed when --constraint
was deleted from the run([...]) list but its explanatory comment
survived -- exactly the "the pin looks present but does not apply" shape
this PR exists to fix. It now parses the cell with ast and asserts
--constraint is in the argument list AND immediately followed by the
constraints file. Verified by deleting the flag from the argv while
keeping the comment: the test fails.

Also drops test_the_notebook_is_still_valid_json_and_has_its_cells --
it passed before the change and duplicated JSON parsing the constraint
test already does. A tautology.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: debpalash <nizam4103@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 02:03:57 +05:30
Palash Debnath 9736fd4859 release: v0.4.1 (#1239)
* release: v0.4.1

Seven user-reported issues fixed since v0.4.0 (#1221–#1229). Version bumped
across the single source of truth (frontend/package.json) and its three
toolchain mirrors; [Unreleased] renamed to the release section that
release.yml extracts verbatim as the GitHub Release body.

Docker tag examples in docs/install/docker.md and deploy/dockerhub-overview.md
updated to 0.4.1 (docs-sync rule).

* release: #1239 review — sync Cargo.lock to 0.4.1

Greptile: the manifest said 0.4.1 while Cargo.lock still recorded 0.4.0, so a
`cargo build --locked` (and the Tauri bundler's own locked build) would fail
on the mismatch. Regenerating locally updated it but it was never staged.

* release: re-sync [0.4.1] after the fix merges, date it 2026-07-27

Picks up everything merged since the section was first written: the first-run
wizard chrome (#1241), the MCP host allowlist (#1249), the macOS 12 startup
crash (#1245), and the six error-message fixes (#1247, #1251, #1254, #1256,
#1257, #1262).

Deliberately NOT included:
  - the dub delete-resurrection fix (#1252, #1253) — split to #1270 after it
    needed six rounds of correction, the last two finding that the fix did not
    close the reported case and that its own bound reintroduced it;
  - the Linux AppImage WebKit fix (#1258, #1244) — held on #1265 pending
    confirmation on a Mesa 26.1 host, which nobody has run.
2026-07-26 14:48:05 -07:00
debpalashandClaude Opus 4.8 f92e4223db docs(docker): refresh registry tag examples 0.3.22 → 0.4.0
Update the exact-version / minor / ROCm pin examples in the Docker Hub
overview (deploy/dockerhub-overview.md — source of the hub.docker.com page,
re-synced on this main push) and docs/install/docker.md to the v0.4.0
release. GHCR's package page inherits the repo README and the current
org.opencontainers.image.description label, both already accurate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 12:29:20 +05:30
debpalash 3aecca1759 docs: add Trendshift badge to the GitHub and Docker Hub readmes 2026-07-16 19:14:35 +05:30
mergetestandClaude Fable 5 b17df5c7a3 docs(docker): refresh the Docker Hub overview and docker guide
- Add a what-you-need line (RAM/disk/GPU from the README requirements
  table, compressed pull sizes measured from the registry) so homelab
  users can size the deployment before pulling.
- Update stale version examples (:0.3.6 / :0.3.17 -> :0.3.22).
- Fix the 'main is always one patch ahead' claim — with
  AUTO_VERSION_BUMP off, main can equal the released version; say
  'at or ahead of the last release', which is true in both modes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 16:41:40 +05:30
99e01610bb feat(docker): publish ROCm/AMD GPU image variant (#1165) (#1166)
The Docker image was CUDA-only, so AMD GPUs (e.g. RX 7900 XTX under
Podman) silently ran on CPU. Every preview and release now also ships a
ROCm variant built from the same Dockerfile:

- deploy/Dockerfile: parameterize the runtime base with a BASE_IMAGE
  build-arg (default unchanged: pytorch/pytorch 2.8.0 CUDA). Add
  PIP/UV_BREAK_SYSTEM_PACKAGES for the ROCm base's PEP-668-marked
  Ubuntu 24.04 Python (no-op on the conda CUDA base), and a build-time
  GPU_FLAVOR guard asserting the dependency install did not clobber the
  base image's GPU torch/torchaudio — a future dep bump that forces a
  torch reinstall now fails the build instead of shipping a CPU-only
  "ROCm" image.
- .github/workflows/docker.yml: new build-and-push-rocm job (separate
  job for runner disk — the ROCm base is ~25 GB unpacked, so it frees
  the preinstalled toolchains first). Tags mirror the CUDA semantics
  with a -rocm suffix (:rocm rolling preview, :stable-rocm, :X.Y.Z-rocm,
  :X.Y-rocm, :sha-xxxx-rocm) on both GHCR and Docker Hub, same secret
  gating. flavor latest=false so release tags can't clobber :latest.
  No cache-to: the ROCm layers would blow the 10 GB GHA cache budget.
- deploy/docker-compose.yml: new opt-in 'rocm' profile passing the GPU
  through via /dev/kfd + /dev/dri, with HSA_OVERRIDE_GFX_VERSION=11.0.0
  documented (user-set, not baked in — backend auto-sets it for known
  consumer GFX IDs).
- Docs-sync: docker.md (ROCm quick start incl. Podman/Quadlet, tag
  table, troubleshooting), dockerhub-overview.md, README AMD note,
  linux.md ROCm section cross-link, CHANGELOG [Unreleased].

Base image: rocm/pytorch:rocm7.2.4_ubuntu24.04_py3.12_pytorch_release_2.8.0
— torch 2.8.0 exactly matches the CUDA image (identical resolution, so
uv keeps it), py3.12 satisfies requires-python >=3.11 (the ubuntu22.04
variants are py3.10 and do not).

Closes #1165

Co-authored-by: mergetest <nizam4103@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 15:34:28 +05:30
254f071b45 fix(docker): ship alembic.ini in the image, add an image-level HEALTHCHECK (#1080)
Migrations in Docker fell back to the additive-column self-heal because
alembic.ini was never copied; the real migration chain now runs. The
HEALTHCHECK covers plain docker-run (compose files keep their own), with
a start period sized for first-boot schema creation. Docs example tag
freshened.

Co-authored-by: mergetest <test@local>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 22:17:57 +05:30
Palash DebnathandClaude Opus 4.8 bd62659b9f docs(docker): maintain Docker Hub overview in-repo + auto-sync on main (#410)
The hub.docker.com/r/palashdeb/omnivoice-studio overview was managed by
hand and had gone stale (stuck at the sha-f86beb0 era, missing the tag
table, audiobook/long-form, Supertonic-3, server-mode networking notes).

Add deploy/dockerhub-overview.md as the source of truth and a
peter-evans/dockerhub-description step in docker.yml that pushes it to
Docker Hub on main pushes. Gated identically to the image push: only when
DOCKERHUB_TOKEN is set, so forks / GHCR-only runs are unaffected.

Overview adds the :latest=preview / :stable=release tag semantics (matching
docs/install/docker.md), the current feature set, server-mode + LAN
networking notes, and shields badges. Short description is 98/100 chars.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 14:26:40 +05:30
Palash DebnathandClaude Opus 4.8 b4f1fe18d7 fix(server): relax loopback gate in headless server mode so Docker admin UI works (#261) (#263)
In Docker the loopback origin gate (`require_loopback`) is unenforceable:
Docker's NAT rewrites `request.client.host` to the bridge gateway (e.g.
172.17.0.1) even for a localhost-only `-p 127.0.0.1:3900:3900` mapping, so every
request looks non-loopback. The gate then 403s the operator out of the routes
the web UI needs — `/system/*` (incl. `/system/info`, which left the version
blank, re-breaking #249 in Docker) and `/api/settings/*` (HF-token entry) —
surfacing as "Loopback origin required" all over the UI.

Fix: add an explicit, opt-in `OMNIVOICE_SERVER_MODE` flag. When set,
`require_loopback` becomes a no-op; exposure is then governed by the operator's
port mapping plus the optional share PIN (NetworkAccessMiddleware still 401s
unauthenticated non-loopback clients whenever a PIN is set). The Docker image
sets `OMNIVOICE_SERVER_MODE=1` (Dockerfile + documented in compose).

Security: the desktop build NEVER sets this, so its loopback boundary is
unchanged — LAN share guests are still denied the admin/system routes. New
unit tests lock the contract (strict 403 by default incl. the PR #81 vectors;
relaxed only under the flag). Existing non-loopback 403 tests still pass.

Docs: docker.md troubleshooting entry for "Loopback origin required".

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 06:22:25 +05:30
Palash Debnath e4dbf4c8c0 P0: release.yml typecheck + bind audit + loopback middleware (#84)
Three P0 fixes bundled — foundation cleanup before v0.3.0 phase work. Closes release.yml drift (PR #51's tabs broke v0.3.0 tag releases), production bind exposure (Critic F1), and 9-endpoint LAN gap on /system/* (Critic F2+F3). 5 new tests; 243 full pass.
2026-05-18 22:00:59 +05:30
QinShower 6fd9b139a3 fix: add PYTHONPATH to docker-compose for pre-built images (#77)
Community contribution from @fishandsheep.

Adds PYTHONPATH=/app/backend to docker-compose env for both omnivoice (CPU) and omnivoice-gpu service blocks, so the pre-built Docker image can import backend modules correctly on first boot.

Complements PR #74 (Docker GPU detection) — different sections of docker-compose.yml.

Thanks @fishandsheep!
2026-05-18 16:31:41 +05:30
Palash Debnath 1941e3fcc0 fix(docker): GPU detection in containers + compose profiles + sonitranslate cuDNN sub-repo (#74)
Docker GPU support hardening + documentation.

- Restores docker compose --profile gpu up path; documents NVIDIA Container Toolkit setup in README
- Splits CPU vs GPU compose services cleanly (deploy/docker-compose.yml)
- backend/api/routers/setup/wizard.py: GPU detection in containerized environments uses torch.cuda fallback
- New scripts/setup.py replaces deleted scripts/setup_cudnn.py
- New test: tests/test_setup_preflight.py
- CHANGELOG.md + README.md updated

Complementary to PR #77 (community PYTHONPATH fix) — different sections of docker-compose.yml.
2026-05-18 16:31:01 +05:30
Palash Debnath 6825b8b0a9 Cross-platform bug bash + Stories tab + VRAM-aware GPU pool (#51)
First v0.3.x release on the Phase 0 cross-platform CI baseline.

## Cross-platform bug fixes (375ea4e)

User-reported bugs from a Pinokio/Windows session:
- Docker `compose --profile gpu up` no longer port-conflicts on 3900 — restored `profiles: ["cpu"]` that #49 wrongly reverted on CodeRabbit's advice.
- Argos / pip install from the UI now works inside Docker — added `_in_virtualenv()` runtime check; `run_pip` injects `--system` automatically when on system Python.
- Speaker diarization warning toast — when pyannote silently falls back to the silence-gap heuristic (missing HF_TOKEN, license not accepted, network blocked), `_diarize()` now returns `(segments, warning)`; `useDubWorkflow` renders an 8-second toast.

## Dub editor UX (d5df454)

Six fixes per annotated screenshots:
- Editable segment start times (`m:ss.s` or raw seconds; Esc reverts, Enter commits; rejects overlap with end).
- Click a transcript row → seek the waveform/video (`WaveformTimeline` now forwardRef's `seekTo(time)`).
- Speaker is datalist-backed (pulls from detected speaker clones; free text still allowed).
- Scissors menu splits at cursor — uses live caret, then last caret, then sentence-boundary fallback.
- Mouse-wheel scrolls the waveform; Cmd/Ctrl left alone for browser pinch-zoom.
- Menu popover collision: added `avoidCollisions` + `collisionPadding=8` to Radix Content; removed `position: fixed` from `.ui-menu`.

## VRAM-aware GPU pool (73dbe18)

`_gpu_pool` was hardcoded `ThreadPoolExecutor(max_workers=1)` since introduction — every TTS forward serialized through one thread.
- CUDA / ROCm: `workers = clamp(1, free_GB // 2.5, 4)`. 16 GB card with ~14 GB free → 4 workers → ~4× throughput on multi-segment dubs.
- MPS / CPU / unknown: 1 worker.
- `OMNIVOICE_GPU_WORKERS` env var override (clamped 1..16).
- Module `__getattr__` preserves the public `_gpu_pool` symbol for existing callers.

## Stories tab — wire-up + UX (f6bbc7a)

The 264-line `StoriesEditor` component existed but was mounted nowhere. Now wired into NavRail + lazy-loaded on `mode === 'stories'`. Added Paste & Split panel (sentence-boundary chunking) and per-track `[pause 0.5s]` insertion.

## Stories — pauses + inline voice (edd3a1d)

`frontend/src/utils/storyTokens.js` — tokenizer for `[pause X.Ys]` and `[voice:X]…[voice:default]` markers. Voice switches are stateful (carry forward). 13 new vitest cases (vitest now 24/24).

## Verified

- 214 backend tests pass (3 skipped, 10 xfailed, 3 xpassed)
- 23 router-smoke tests pass
- 24/24 vitest cases pass (13 new)
- All 7 Phase 0 CI checks green (Tauri shell + Smoke on macOS/Windows/Linux + Tests)

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-05-18 15:57:42 +05:30
Palash DebnathandClaude Opus 4.7 a1ef66c321 Stability pass: DB leaks, App.jsx hooks refactor, desktop bootstrap (#49)
* fix: eliminate DB connection leaks, race conditions, and deprecated asyncio API

## DB Connection Leaks (P0)
- Convert 38 raw get_db() calls to db_conn() context manager across 14 router files
- Connections are now guaranteed to close even when exceptions are raised
- profiles.py create_profile: clean up orphaned audio file if DB insert fails
- profiles.py lock_profile: consolidate 3 separate conn.close() error paths

## Race Condition (P1)
- Add _dub_jobs_lock (threading.Lock) to protect _dub_jobs dict in dub_pipeline.py
- get_job/put_job now thread-safe for concurrent dub sessions

## asyncio Deprecation (P2)
- Replace 23 asyncio.get_event_loop() calls with asyncio.get_running_loop()
- Prevents DeprecationWarning on Python 3.12+ and future breakage on 3.14

## Quick Fixes
- gallery.py preview_voice: remove filesystem path from error response (P2)
- dub_pipeline.py parse_vtt_segments: remove redundant `import re` inside loop (P3)
- gallery.py _init_gallery_db: use db_conn() context manager (P2)

* refactor: extract hooks, centralize isTauri, add pytest-cov

## Frontend
- Extract useTTS hook (150 LOC) — TTS generation, streaming, audio ingestion
- Extract useProfiles hook (219 LOC) — voice profile CRUD, lock/unlock, preview
- Centralize isTauri detection: dialog.js, VoiceGallery.jsx, Settings.jsx
  now import from utils/media.js instead of 4 different detection patterns

## Backend
- Add pytest-cov to dev dependencies
- Baseline coverage: 39% across backend/ (214 tests pass)
- Add .coverage to .gitignore

* feat: add Vitest + checkJs, extract useDubWorkflow + useAppData hooks

## Frontend Testing (new)
- Set up Vitest with jsdom environment + @testing-library/react
- 11 tests: utils (isTauri, formatTime, constants) + Zustand store (mode, text, dubStep, pill)
- Scripts: 'test' (vitest run), 'test:watch' (vitest), 'test:legacy' (node runner)

## App.jsx Decomposition (continued)
- Extract useDubWorkflow hook (387 LOC) — upload, ingest, transcribe SSE,
  translate, generate SSE, abort, stop, cleanup
- Extract useAppData hook (181 LOC) — data loading, localStorage persistence,
  WebSocket real-time updates, model-status pill management

## TypeScript checkJs
- Enable checkJs: true in tsconfig.json for IDE-level type checking
- 947 existing errors (informational, not blocking builds)
- noImplicitAny remains false to avoid blocking

* ci: add Vitest step, fix useProfiles duplicate state

## CI
- Add 'Run Vitest (frontend)' step — runs 11 unit tests
- Override --checkJs false in CI typecheck to avoid 947 pre-existing errors
- Rename legacy test step for clarity

## Hooks
- Fix useProfiles to accept loadProfiles from parent (useAppData)
  instead of managing its own duplicate profiles array

* refactor: wire hooks into App.jsx — 2067 → 1129 LOC (-45%)

App.jsx now delegates to extracted hooks instead of inline logic:
- useAppData: data loading, localStorage, WebSocket, model pill
- useProfiles: voice profile CRUD, lock/unlock, preview
- useTTS: generation, streaming, audio ingestion
- useDubWorkflow: upload, transcribe SSE, translate, generate SSE

988 lines removed. All handler logic lives in focused,
independently testable hooks. Store selectors and render
JSX stay in App.jsx as the shell.

Verified: vite build clean, 11 frontend + 214 backend tests pass.

* feat: show real-time percentage on model loading pill

Backend: register hf_progress listener during _load_model_sync()
so download/weight-loading tqdm events update _loading_detail with
a progress percentage (0-99%). get_model_status() now includes a
'progress' field that the frontend polls.

Frontend: useAppData reads msQuery.data.progress and calls
setPillProgress() — the FloatingPill already renders the percentage
text and progress bar width from this value.

* fix: prevent FileNotFoundError in desktop bundle during model init

transformers >=4.52 calls _can_set_experts_implementation() and
_can_set_attn_implementation() during PreTrainedModel.__init__,
which open the class source file via open(class_file). In a Tauri
desktop bundle, module.__file__ points to a path that doesn't
exist on disk, causing:

  FileNotFoundError: .../omnivoice/models/omnivoice.py

Override both classmethods on OmniVoice to return static values
without filesystem access. OmniVoice doesn't use MoE experts
(return False), but does support flex/flash attn (return True).

* fix: sync source dirs on every bootstrap, not just first run

The Tauri bootstrap previously only copied omnivoice/ and backend/
to Application Support on the first run. Subsequent app updates
kept using stale source files, preventing bug fixes from landing.

Now ensure_venv_ready() always syncs both directories from the
bundle resources before returning, even when the venv is healthy.
This fixes the FileNotFoundError crash where the old omnivoice.py
lacked the _can_set_experts_implementation override.

* ui: premium setup wizard polish

- Primary button: solid gradient fill with hover glow + lift + press
- Stepper nav: connected pills with glow ring on active step
- Welcome cards: glassmorphism with stagger-in animations, lucide icons,
  left-border accent strip, hover translate
- Preflight panel: colored icon pill backgrounds, stagger-slide entrance
- Step transitions: fade+slide animation via keyed wrapper
- Footnote: shortened paths (~/ notation), Reveal in Finder button
- Recommendation banner: gradient background with accent glow
- Compact spacing throughout for denser, professional layout

* fix: kill zombie backend on clean+retry bootstrap

When clean_and_retry_bootstrap removes the project dir, any old
uvicorn process still running from the deleted paths remains alive
on port 3900. The subsequent retry_bootstrap sees the port is
healthy and attaches to the zombie instead of re-bootstrapping.

Now explicitly kill any process on the backend port after cleaning,
before calling retry_bootstrap.

* feat: integrate speaker clones into dubbing interface, sanitize system environment variables for subprocesses, and improve FFMPEG binary path resolution.

* fix: restore docker compose default + drop dead setSeed call

- deploy/docker-compose.yml: remove profiles: ["cpu"] from the default
  service so `docker compose up` matches the comment on line 5. With the
  profile present, no service auto-started.

- frontend/src/App.jsx: drop the setSeed call in restoreHistory. The
  selector was never reintroduced after the App.jsx hooks split, and
  there is no seed state in the store — seeds are generated fresh per
  call in useTTS and only read from history items for display.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix: address CodeRabbit review — async detection, dub stream, bootstrap fail-fast

- backend/services/tts_backend.py: invert async-context detection in
  _ensure_loaded. The previous code unconditionally caught its own
  diagnostic RuntimeError and then called asyncio.run() inside a
  running loop, masking the intended error message.

- frontend/src/hooks/useDubWorkflow.js: require a terminal `done` event
  before reporting dub success. Without this, a dropped stream after
  partial progress would flip the UI to `done`, refresh history, and
  play the completion ping as if generation finished.

- frontend/src/hooks/useDubWorkflow.js: restore the previous step when
  tasksCancel() fails. The UI was getting stuck in `stopping` forever
  on cancel errors.

- frontend/src-tauri/src/bootstrap.rs: fail-fast when source sync fails
  after the existing directory has already been removed. The previous
  warn-and-continue path could leave the install with no backend/ or
  omnivoice/ sources and defer the failure to backend startup with a
  cryptic error.

- backend/api/routers/generation.py: add `from e` to the ValueError →
  HTTPException re-raise (Ruff B904).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix: preserve % suffix in TTS generation timer

The 100ms timer in useTTS was rewriting generationTime to a plain
elapsed-seconds string, which immediately wiped the "(xx%)" download
suffix written on the next iteration of the response-body loop. The
real-time percentage was flickering on/off as a result.

Read the previous value inside the setter and reattach any existing
percent suffix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 21:49:04 +05:30
Palash Debnath 20ade687f6 fix: resolve open issues — Discord link, Docker crash, IndexTTS compat, engine tooltips (#47)
* fix: resolve 7 open GitHub issues (#46 #43 #42 #45 #44 #35 #4)

#46 — Discord invite expired:
  - Replace discord.gg/aRRdVj3de7 with discord.gg/bzQavDfVV9 across
    README, CONTRIBUTING, EnterprisePage, LogsFooter

#43 — Docker image crashes with 'No module named core':
  - Add PYTHONPATH=/app/backend to Dockerfile so bare imports resolve
  - Add sys.path safety net in backend/main.py (belt-and-suspenders)

#42 — IndexTTS not compatible (transformers version conflict):
  - Catch ImportError + generic Exception in IndexTTS2Backend.is_available()
  - Return actionable error explaining transformers<5 vs >=5.3 conflict
  - Update install docs: recommend 'uv pip install -e .' not 'uv sync --all-extras'

#45 — Improve pip install tooltips:
  - Add install_hint field to list_backends() API response
  - Show hints as tooltips on engine rows in Settings > Engines
  - Add models-row__hint CSS with hover reveal

#44, #35, #4 — Response-only issues (need GitHub comments)

* test: add 20 unit tests for issue batch fixes (#46 #43 #42 #45)

Coverage:
- Discord link sweep: parametrized per-file + repo-wide glob
- Docker fix: sys.path insertion in main.py, PYTHONPATH in Dockerfile
- IndexTTS: is_available() tuple shape, conflict detection mock, docstring
- install_hint: presence, non-empty, registry coverage, backward compat
- Regression: minimum engine count, all backends return (bool, str)

* fix: address CodeRabbit review — voxcpm package name, bootstrap test isolation

- Fix _INSTALL_HINTS: 'pip install voxcpm2' → 'pip install voxcpm' (correct PyPI name)
- Replace test_core_config_importable with test_main_py_bootstrap_adds_backend_dir
  that validates main.py's preamble directly instead of relying on conftest.py
- Add test_voxcpm_install_hint_uses_correct_package_name regression guard

* fix: align install hints with backend reality (MOSS not on PyPI, VoxCPM supports CPU/MPS)

- MOSS-TTS-Nano: not on PyPI, must install from GitHub repo
- VoxCPM2: CPU/MPS supported, CUDA recommended (not required)
2026-05-11 06:53:22 +05:30
debpalash cfab2a500a feat: add GHCR Docker workflow, update README with container registry instructions
- New .github/workflows/docker.yml publishes images to ghcr.io on tag push
- README Docker section now leads with 'docker pull' from GHCR
- docker-compose.yml defaults to GHCR image with build-from-source fallback
- Dockerfile: copy README.md for hatchling metadata resolution
2026-05-03 09:04:49 +05:30
Palash Debnath 0ecbf136e7 refactor: codebase cleanup & root folder reorganization (#38)
refactor: codebase cleanup & root folder reorganization
2026-05-03 03:12:07 +05:30