Spaces:
Sleeping
Sleeping
Commit History
-
Pin gdown to 6.x
Current calls are already gdown-6 compatible, but the dependency was unpinned and gdown 6.0 broke sibling apps (removed download()'s fuzzy kwarg). Cap at the major to avoid the next break landing silently. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>cadb433
-
Document Backblaze keys in the droplet env file
/root/archiveai-tts.env was missing BACKBLAZE_KEY_ID / BACKBLAZE_APPLICATION_KEY, so both "Load from Backblaze" pickers would have warned that they were not configured. The keys have been added on the droplet; this records them alongside the other env-file contents, plus the gotcha that an env-file edit only takes effect when the container is recreated — a plain restart and a no-op nightly both miss it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>b6bbd57
-
Add "Load from Backblaze" pickers for turns/captions and text input
Two independent browsers: one for the turns.json + captions pair, one for the general text/subtitle source — mirroring the two existing Drive-link entry points. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>7eb9f97
-
Add per-container memory limits and OOM-kill priority
Contain a runaway app to its own container instead of letting a global OOM event pick any process on the droplet (see 2026-08-14 incident: archiveai-tts got OOM-killed with zero swap configured, driving load average to ~40 while the whole box stalled). Content apps get --memory ceilings sized to real observed usage plus 2-4x headroom, and a positive --oom-score-adj so they're sacrificed before Orchestrator, which is biased negative to stay up as the last line of visibility.f1056c7
-
Point at Orchestrator's new self-service allowlist admin panel
3500184 -
Stagger nightly deploy cron to :20 UTC, avoid prune-lock collisions
All four DO apps' deploy-do.yml ran docker image prune -af on the same 00:00 UTC schedule, sharing one droplet. On 2026-08-13 two prunes overlapped and the losing workflow died with "a prune operation is already running" before it ever reached the container restart step. Each app now runs 10 minutes apart (:00/:10/:20/:30).88f8f15 -
Rewrite docs for terser, more austere style (no content changes)
255731f -
Add AGENTS.md: agent operating guardrails (tests, deploy, secrets)
8d2eda8
-
Document the REST API and the security model
docs/api.md is the full reference for the job API, including the synthesis parameters that were previously only discoverable by reading the handler signature. Two things it makes explicit: - Cost control. A job with synthesize_audio=true costs one Gemini call plus one ElevenLabs call per non-mantra segment, which on a full teaching is real money. The documented workflow is synthesize_audio=false first, review the turn plan, then resubmit with turns_file to skip Gemini entirely. - The concurrency caveat, which has a security consequence and so appears in both docs: unlike TranslationUI and ArchiveAI-STT, this app's UI Generate handlers are NOT wrapped through the job-store semaphore. So active_jobs undercounts real work, and /api/drain does not stop a UI batch already running — "drained" is not a guarantee that nothing is writing. docs/security.md covers what's particular here: this is the one droplet app that writes to HuggingFace in normal operation, …b2d53f4 -
Fail closed on API auth, and compare tokens in constant time
Found in the 2026-08-11 security review of the DO droplet. _check_api_token was `if expected and x_api_token != expected` — it skipped the check entirely whenever the token env var was unset or empty. That is fail-open, and it matters here specifically because Caddy exempts /api/* from the Google OAuth gate (Miscellaneous/droplet-infra/Caddyfile in the monorepo), making this check the *entire* perimeter for those routes. One typo'd env file, or a container recreated without --env-file, would have published the whole API to the internet with no error anywhere — and a missing env var on a redeploy is not hypothetical, it already happened once to TranslationUI's HF_TOKEN. It now returns 503 when unconfigured, matching what _check_hf_token next to it already did; the inconsistency between the two is what gave the bug away. Also switched both token comparisons to hmac.compare_digest so they don't leak length/prefix information through timing. The test fixtures relied on the fail-open pat…f54c348
-
Run container as non-root
A code-execution bug in the app shouldn't hand out root inside the container for free — matches the pattern the sibling ArchiveAI-STT repo already uses. UID 1000, chosen to match the other two apps sharing the same DO droplet's data volume (the host directory was chowned to 1000:1000 separately, on the droplet, not via this commit). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>e63b7a0 -
Fix path-traversal via project_id in the new job API
Unlike the UI, which only ever generates project_id itself (slugify + uuid hex, always safe), the new POST /api/jobs and GET /api/projects/{id} endpoints accept it directly from an external caller so an existing project can be targeted. tts/core/project_store.py's local (TTS_DATA_DIR) backend builds a real filesystem path directly from it — _project_path(project_id) = "projects/{project_id}/project.json", joined onto the data root — with no sanitization. An authenticated caller (holding a valid X-API-Token) could pass e.g. project_id=../../../../tmp/pwned for arbitrary file write/read inside the container, which runs as root (no USER directive in this repo's Dockerfile). project_id is now validated against the same safe-slug shape new_project_id() already produces before it reaches the store, on both the job submission and project-read endpoints. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>021451f -
Add async REST job API alongside the existing Gradio UI
Mirrors HuggingFaceSpaces/ArchiveAI-STT's server/api_jobs.py pattern: POST /api/jobs, GET /api/jobs/{id}, GET /api/jobs, GET /api/status, POST /api/drain, GET /api/ping, plus GET /api/projects and GET /api/projects/{id} for an external caller to poll for edits made through the UI (project updated_at changes on every UI edit). Prerequisite refactor: extracted the actual ElevenLabs call (_synthesize_turn_and_store) out of app.py into tts/core/turn_audio.py (synthesize_turn) and tts/core/turns.py (build_project_payload), so both the Gradio UI and the new API can call the same pure functions without either module importing from the other. app.py's version is now a thin wrapper that adds its Gradio-state-specific segment mutation on top. Existing tests patching the old app.synthesize_turn_audio location were updated to patch tts.core.turn_audio.synthesize_turn_audio instead; all 443 pre-existing tests still pass unchanged in behavior. A submitted job runs the same pipeline Turn Review's U…ab0a768 -
Prune old images before pulling on DO droplet deploy
Nightly deploys never removed old images, filling the shared droplet's disk and breaking last night's deploy (no space left on device). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>1a3d259
-
Document container egress restrictions and second-pass security gaps
Adds the DOCKER-USER egress rules (VPC + cloud metadata blocked, with the mandatory DNS carve-out called out prominently since removing it breaks all container DNS), plus the gaps that pass left open: root containers, container-to-container reachability, and the asymmetric volume mount between the two apps. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>b34b6d7 -
Add .dockerignore so .git isn't baked into deployed images
Found in a security review: with no .dockerignore, `COPY . .` copied the full commit history into every image pushed to the registry. History was verified clean of secrets, so this is defense-in-depth rather than an active leak. Conservative exclusions only. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>73e1012 -
Document HF_TOKEN over-scoping as a known gap (security review)
Same finding as TranslationUI's docs: the HF_TOKEN in use (needed here for glossary sync) is a personal full-access account token, not scoped to this app's actual need. Deferred deliberately. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>62f46b4 -
Add docs/ covering configuration, storage, deployment, and operations
Mirrors TranslationUI's docs structure. Documents the shared-droplet architecture specifically (same Caddy/oauth2-proxy as TranslationUI, cookie-domain widened to cover both apps' subdomains) since that's the part most likely to confuse someone debugging this app in isolation. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>fc33d54 -
Add DO deploy path with local-file project storage backend
project_store.py now supports two backends, auto-selected by environment: local files on a mounted volume (TTS_DATA_DIR set, e.g. DO) or the original HF dataset (HF_TOKEN set, no TTS_DATA_DIR, e.g. the HF Space). Necessary because both deploy targets build from the same main branch — a local-only rewrite would have broken persistence on the still-live HF Space on its next deploy. Local files rather than a database, since most of what's stored here is generated audio, which fits a filesystem more naturally than DB rows. Function signatures are unchanged, so app.py needs no changes. glossary.py is untouched — its existing /data-if-present cache plus best-effort HF sync already does the right thing once a volume is mounted at /data, and the shared glossary dataset is intentionally kept shared between HF and DO (see deploy-do.yml). deploy-do.yml is additive, doesn't touch deploy.yml, and deploys to a droplet shared with TranslationUI: same Caddy instance and oauth2-proxy auth gate (cooki…07e532f
-
Bump actions/checkout and actions/setup-python to v7
Clears the Node.js 20 deprecation warning GitHub Actions attaches to every run (both actions now ship Node 24-native releases). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>d51261b
-
Fix turn-audio splice drift; harden Turn Review; add Quick Synthesize tab
Splice/pause (tts/core/pause_synthesis.py): insert_pauses and splice_sentence_audio now shift/space timestamps by the crossfade-adjusted audio length, not the nominal pause/clip length, so regenerated selections splice at the right position instead of drifting later through a turn. Multi-word selections keep internal comma/period pauses (synthesize_clip internal_only). Sentence pauses now fire on ? and ! and before closing quotes (."), not only bare periods; decimals still excluded. Turn Review robustness (app.py, project_store.py): reload with a changed glossary drops the offset map instead of splicing the wrong region; a failed audio download on load no longer lets autosave orphan stored audio+alignment; the sweeper refreshes mtime on render so live-session audio isn't reclaimed, and Download All/Accept guard against a missing file; Create Turns no longer mints a duplicate project (reload on upload/clear, not programmatic .change); per-turn Generate resets the stale regenerate banne…c198355 -
Add per-slot regen settings, enlarge Regenerate Selection button, full test coverage
Each turn's regen box now has its own Model/Speed/Stability/Similarity/Style controls (above the "Regenerate: <selection>" banner, advanced ones tucked into an accordion) instead of sharing the Turn Review tab's global sliders — so adjusting speed for one regeneration no longer silently affects Generate/Generate All. Regenerate Selection is now a full-size ("lg") button instead of "sm", to be easier to see and hit. Verified the rendered layout and event wiring by launching the app and driving it with Playwright, and added a Blocks-config-introspection test (test_app_regen_settings_wiring.py, following the existing test_app_tabs_isolation.py pattern) asserting each slot's regen/retry click events read that slot's own controls, not the shared globals. Also added tests/test_tts_voices.py — validate_speed() (added earlier this session) had no direct unit tests, only indirect coverage through its call sites. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>2172eca -
Reject out-of-range speed before calling ElevenLabs, and clamp speed sliders
ElevenLabs rejects voice_settings.speed outside 0.7-1.2 with a bare 400 — validate it up front (in generate_with_timestamps and _call_el, the two call sites used by turn generation, selection regeneration, and Quick Synthesize) and raise a clear error instead. Also narrow the Speed/Prose Speed/Mantra Speed sliders to that same range so the invalid value can't be picked in the UI in the first place. Also commit tts/utils/net.py and its tests — call_with_retries has been imported and relied on by pause_synthesis.py already, but the file itself was never actually committed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>c1429dc -
Stop preview from disappearing when Play is clicked
Any successful selection event unconditionally hid the open preview group, including a redundant re-fire of the exact same span — which happens when clicking Play on the preview player (the browser appears to re-report the textbox's unchanged selection at that point). Treat a re-fire of the same span with a preview already open as a no-op instead of collapsing the preview back to just the banner. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>ee39309 -
Add project nickname + explicit Save button; fix shallow-push deploy failure
Turn Review now mirrors TranslationUI's project management: a Project Name field (renames + autosaves on submit) and a Save button (flushes the current page's text edits and autosaves) alongside the existing autosave-on-blur/generate behavior. Also fix the deploy workflow: actions/checkout@v4 defaults to a shallow clone, which Hugging Face's git server rejects when pushed ("shallow update not allowed") — add fetch-depth: 0. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>93c910b -
Fix selection-regeneration blocked by text edits and preview disappearing on play
Allow regenerating a highlighted selection after a small text edit by locating the edit via common prefix/suffix and remapping it into the old word map, instead of requiring an exact match to the last-synthesized text. Also stop an empty/collapsed text selection (which also fires when clicking Play collapses a highlight) from hiding an open preview player. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>9aad88b -
Add nightly automated deploy, skipped when main is unchanged
deploy.yml now also triggers on a 00:00 UTC schedule. A check-for-changes job runs first on the scheduled trigger only (manual runs always deploy) and compares HEAD against hf-tts/main directly — if they're the same commit, the deploy job is skipped, so a quiet night doesn't rebuild the Space. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>2e993b5 -
ci: install ffmpeg before running tests
pydub-backed tests (turn audio synthesis, pause insertion, clip splicing) need ffmpeg on PATH; the runner never installed it, so ~40 unrelated tests failed on every CI run regardless of what changed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>c81718a -
Add Gemini-powered "Create Turns" for VTT/SRT uploads without turns.json
Turn Review previously required both turns.json and translated captions. Now, uploading just captions (VTT or SRT) surfaces a "Create Turns" button that generates turns.json via Gemini, mirroring the July Launch pipeline's speaker_turns.py/gemini_client.py model chain and validation, then saves and loads it through the existing project-persistence path. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>1a24191
-
Add GitHub Actions CI + HF deploy workflows
CI runs unit tests on every PR/push. Production deploy moves to a manual workflow_dispatch that pushes straight to the HF Space instead of a local `git push hf-tts`. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>556c1c6 -
Add Turn Review project persistence with autosave, Generate All, and SRT captions
Every turns.json + translated-captions upload now becomes a saved project on the private GarchenArchive/tts-turn-projects dataset, mirroring TranslationUI's project_store pattern: raw source files, settings, and per-turn text/alignment/audio are autosaved as the user works (on generate/regenerate/accept, textbox blur, and page navigation), so nothing is lost on a closed browser tab and a project can be reopened later from any computer via the new project picker. Also adds a sequential "Generate All" batch action, and SRT support for the translated-captions upload (previously VTT-only in Turn Review). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>a69c6ab
-
Add word-level highlight-to-regenerate for Turn Review audio
Highlighting any text in a turn's textbox (down to part of a single word) snaps to the whole word(s) touched and offers to regenerate just that span via ElevenLabs, previewing the take before splicing it into the turn's existing audio at the exact time offset from its original character-time alignment, with a crossfade at each cut and downstream timestamps shifted to stay correct for further edits. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>bd5fe5b -
Merge remote-tracking branch 'hf-tts/main'
c43ffcc -
Add sample SRT/text fixtures for manual TTS testing
Kept as reference examples for exercising the synthesis pipeline by hand. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>6ac04bc
-
Make ElevenLabs pause/audio tags model-safe, add turn-audio temp file cleanup
Protect <break .../> (v2) and [pause]-style (v3) tags from mantra-detection, glossary substitution, and the Turn Review pause-splicing so they always reach the API unmangled, and document the v2 break-tag syntax in the app description. Turn Review's generated mp3s now live in a dedicated temp dir with a background sweep plus per-regenerate cleanup, so they no longer accumulate indefinitely. Also verified (with a regression-guard test against the real Gradio event graph) that Glossary-tab actions and tab switching never touch Turn Review's audio or state. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>7e54bd7
-
Default model to eleven_multilingual_v2 to match actual pipeline workflow
Every other Turn Review default (speed, comma/period pause, stability, similarity boost, style) already matched the July Launch pipeline's Step 8 config; the model dropdown was the one outlier, defaulting to v3 instead of the multilingual_v2 model actually used in production. Also add test-sample/ — a real 2-turn excerpt from STT_GR_0037-5's actual pipeline output (turns.json + translated VTT), for trying out Turn Review without needing to run the full pipeline first. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>9b45f04 -
Add advanced voice setting controls (stability, similarity boost, style)
Surfaced in a collapsed accordion with a note that the production-validated defaults shouldn't normally be changed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>794b80f -
Add Turn Review tab for editor approval of AI-drafted interpreter turns
Loads a chunk's turns.json + translated VTT, windows captions per turn (mirroring the July Launch pipeline's step 8), and lets an editor adjust text and generate/listen/download interpreter audio per turn with real pause insertion (room-tone splicing after commas/periods via ElevenLabs' with-timestamps endpoint, ported from tts_insert_pauses.py) instead of plain single-shot synthesis. Moves the pronunciation dictionary to a HuggingFace dataset-backed glossary (tts-glossary) so entries persist across Space rebuilds and are shared, seeded with the pipeline's validated digraph respellings. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>be2032d
-
fix: wrap plain text box in gr.Group to avoid stuck loading indicator
Gradio 6 gets stuck when a leaf component (Textbox) transitions from visible=False to visible=True in an event response. Wrapping it in a gr.Group mirrors the slot-group pattern that works correctly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>9b8bf39 -
fix: update gdown calls for v6 API (remove fuzzy, use id= kwarg)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>ba7cc52 -
feat: add Google Drive link input
Accepts Google Docs, and Drive-hosted SRT/VTT/TXT via a URL textbox. - Google Docs exported as plain text via the export API - Drive files downloaded with gdown (fuzzy=True handles share URLs) - Shared _load_path() helper routes TXT to the plain-text editor and SRT/VTT to the segment editor, same as file upload - Adds gdown to requirements.txt Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>61c9083 -
feat: support plain text file upload
TXT files now show a single editable text area instead of the segment editor. SRT/VTT continues to show the paginated segment editor. Synthesize reads from whichever input is active. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>979cf49
-
feat: add model selection dropdown (v3 default)
Adds a Model dropdown to the settings panel with three options: - v3 (best quality, default) — eleven_v3 - Multilingual v2 — eleven_multilingual_v2 - Flash v2.5 (fastest) — eleven_flash_v2_5 Removes deprecated eleven_turbo_v2_5 (superseded by Flash v2.5). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>fa6ab8b -
feat: segment editor with timestamps, editable text, pagination
- Parsers now return {timestamp, text} dicts instead of plain strings - Segment editor shows one slot per subtitle block with timestamp chip and editable textbox; slots paginate in groups of 20 - Fixed persistent "processing" overlay caused by gr.Column visibility toggle in Gradio 6: editor column is now always rendered, slots control their own visibility - Upgraded Gradio requirement to >=6.9.0; launch with ssr_mode=False - Updated parser tests for new dict return format Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>5adf3e7
-
refactor: simplify UI to single hardcoded voice (David-Test)
Remove Voices tab, voice cloning, voice selection dropdown, and API key input field. Voice is now fixed to David-Test (7Oj8ZD5BDEDKYENop6zW) and the key is read exclusively from ELEVENLABS_API_KEY env var. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>175c2ba
-
Show API key field immediately in Voices tab
Removes the collapsed accordion wrapper so the ElevenLabs API key textbox is visible as soon as the Voices tab is opened. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>6b8a4e4
-
Replace Kokoro TTS with ElevenLabs API
Removes the local Kokoro ML model (~1.5GB Docker image) in favour of the ElevenLabs REST API. Key changes: - tts/core/synthesis.py: rewritten around _call_el() using pcm_24000 output format; two synthesis paths toggled by mantra_mode flag - tts/core/voices.py: new module for EL client construction, voice listing, and instant voice cloning - tts/core/pipeline.py: deleted (Kokoro KPipeline, g2p, IPA lexicon) - tts/lexicon/phoneme_lexicon.py: deleted (IPA corrections dropped) - app.py: API key accordion, dynamic voice dropdown, mantra mode toggle, Voices tab with Available Voices + Clone a Voice sub-tabs - voices/: new directory for pre-placed voice sample files - Dockerfile: stripped to plain pip install (~200MB vs ~1.5GB) - requirements.txt: torch/kokoro/misaki replaced by elevenlabs>=1.0.0 - tests: 90 tests passing; synthesis tests rewritten for EL mocks The user pronunciation dictionary (plain-English text substitutions) is unchanged — it is applied at the text level before an…7b3dc39 -
Switch base image to python:3.11 to match main ArchiveAI repo
kokoro/misaki don't support Python 3.13; 3.11 is the tested baseline. Also adds cmake to system deps (present in main repo Dockerfile). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>635aaac -
Switch to Docker SDK to install CPU-only torch and avoid 888MB CUDA wheel
Adds a Dockerfile mirroring the main ArchiveAI repo: installs torch/torchaudio from the PyTorch CPU wheel index before the rest of requirements.txt, eliminating the CUDA-bundled download that was causing the build to stall. Updates README frontmatter to sdk: docker and app.py launch to bind on 0.0.0.0:7860. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>aca2544 -
Fix kokoro/misaki/numpy version pins for HF Space
kokoro >0.7.4 requires misaki[en] >=its own version number, but misaki tops out at 0.7.4 on PyPI. Pin both to 0.7.4. All kokoro 0.7.x releases also require numpy==1.26.4 exactly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>6903e08