593 KiB
593 KiB
Changelog
All notable changes to TTS Voice Creator — Clone and Design are documented here.
Follows Keep a Changelog · versioned with Semantic Versioning.
Unreleased
Added
npm run qa: central QA command (scripts/qa.mjs) with timed stages (lint, python/node unit tests, Playwright, security scans, bundle freshness) and an opt-in SonarQube stage (--sonar, token from the environment only). Seearchitecture/qa.md.
Changed
- Voice list rows no longer show the "Source unknown" badge (it crowded the play buttons). The badge stays in the voice inspector, next to the Source & consent panel.
Fixed
- Voice archetype gallery broke
npm run check:frontend: itssayhelper collided withmagazine.js; renamed toarchSay. - Export failed with HTTP 500 when provenance tagging failed (EPUB/ZIP/article MP3 with audio ffmpeg cannot tag); it now ships the untagged file and logs a warning.
- Archetype
languagefilter accepted only full names.GET /api/archetypes?language=dereturned 0 items; it now accepts ISO codes and names, case-insensitive (de,DE,German); unknown values return an empty list. - LibriVox search returned 502 "unavailable" after 20-30 s. Cause: the LibriVox API is erratic (10-40 s for any request, measured from host and container; egress/DNS/TLS fine) and the old call asked for
extended=1(all chapters of 15 books) with a 30 s timeout, mapping every failure to one generic message. Now the list is light (noextended), the first chapter is fetched per book on demand (GET /api/voice-sources/librivox/book/{id}, UI "Load preview" / on import), timeout 25 s with one retry and backoff, failures cached 30 s, distinct messages (timeout / unreachable / HTTP status / unreadable) and a privacy-safe log line (exception class and query length only). Host/redirect rules unchanged. export_provenance_defaultmissing from settings defaults. It is now in the defaults (true), soGET /api/settingsexposes it.
Added
- Voice archetype gallery (own, German-first). "Voice gallery" card in Design a Voice: 40 hand-authored named voices (mostly German, a few English) in 7 categories, each a free-text Qwen3 VoiceDesign description with facets (gender, age, pitch, accent, tone) from the design-chip taxonomy. Search, facet chips, category list, favourites (browser storage), 24 cards per page rendered only after opening, labelled Preview (aria-live status) and "Use voice" (then "Open in Design" prefills the editor).
GET /api/archetypes,POST /api/archetypes/{id}/preview(cached by sha256 of instruct, language, engine and seed 42; timeouts, blank-audio guard) andPOST /api/archetypes/{id}/use(saves voicearchetype_<id>, origin designed, provenance basisunknown, sourcearchetype/<id>, note "synthetic designed voice"; idempotent). Own catalog, nothing copied from other projects; no network. Optionalscripts/render_archetype_previews.pypre-renders all previews. Tests:tests/test_voice_archetypes.py,tests/archetype-gallery.spec.js. Seearchitecture/voice-archetypes.md. - Audiobook emotion tests. Playwright
tests/audiobook-emotion.spec.js(mocked routes) covers span-cast emotion ->instructin/api/tts-preview(dialogue only, English tag, narration empty) and the line cache key;tests/test_tts_instruct_payload.pycovers the instruct passthrough. - LibriVox and Wikimedia Commons voice sources. New tab in Get Voices Online searches the official LibriVox feed and the Commons MediaWiki API through
GET /api/voice-sources/{librivox|wikimedia}?q=&lang=(fixed hosts, query cap 80, 10 results, 30 s timeout, 10 min cache, 1 s throttle, descriptive User-Agent, result URLs host-checked). Results show licence and a preview; "Import clip" trims (start/length) and stores provenance (LibriVox: public domain + check-the-reader note; Wikimedia: licence + attribution).archive.organdupload.wikimedia.orgadded to the audio proxy allowlist. Tests:tests/test_voice_sources_new.py,tests/voice-sources-new.spec.js. Seearchitecture/voice-sources.md. - Provenance tags on exported audio. Magazine MP3 downloads,
audio.zipand the MP3s inside the EPUB get ID3 tags (synthetic_speech=true,generator,generator_version,generated_date, comment; voice name only on opt-in) via one ffmpeg stream copy of a temp copy; stored audio is untouched. Per-export checkboxes on the magazine export row ("Mark as synthetic speech", "include voice name"), default from new settingexport_provenance_default(on).POST /api/export/provenanceandpython -m core.export_provenance file.mp3read the tags back. Inaudible AudioSeal watermarking is researched (MIT code and weights, torch needed, weak message recovery after MP3/AAC) but only a stubwatermark(path)that reports unavailable. Spoken disclosure is not implemented yet. Tests:tests/test_watermark_tags.py,tests/magazine.spec.js. Seearchitecture/watermarking.md. - Inline text tags for TTS (setting
inline_tags_enabled, default OFF).[pause 500ms]/[pause 1.2s](max 10 s each, 60 s per text),[spell]USA[/spell](German letter names for German text),[slow]..[/slow]/[fast]..[/fast](0.85 / 1.15 via ffmpeg atempo, innermost wins, unclosed ends at paragraph). Known control tags (e.g.[laugh]) are stripped, never spoken;[1]and[sic]stay. Rendered inPOST /api/tts-preview(also used by audiobook line previews),/v1/audio/speech(non-streaming),/speakand the MCP speak tool through one sharedssml_lite.render_with_tags; stripped only (never spoken) in streaming, spoken chat replies and magazine MP3 (timings stay exact). Settings -> General switch (accessible, DE/EN) and a syntax hint under the Try It Out and Read Aloud text boxes. Tests:tests/test_ssml_lite.py,tests/test_inline_tags_paths.py,tests/inline-tags.spec.js. - Optional vocal isolation for URL/YouTube imports (default OFF).
POST /api/isolate-vocals {id}runs the Demucs CLI (htdemucs, two stems) on a registered temp audio and registers the vocals as a new id; the original stays valid. NeedsVOCAL_ISOLATION=1, ademucsbinary (orVOCAL_ISOLATION_CMD) and the weights already on disk; the app never downloads anything and nothing is added to the main image. Limits: 15 min input, 15 min runtime (VOCAL_ISOLATION_MAX_SECONDS/_TIMEOUT), one job at a time, cancelled on client disconnect, temp files always removed.GET /api/versionreportsfeatures.vocal_isolation; the "Remove background music" button (clone page URL card and voice library add-URL) appears only then, with an aria-live status and a play original / play without music toggle. Tests:tests/test_vocal_isolation.py,tests/vocal-isolation.spec.js. Seearchitecture/vocal-isolation.md. - Voice export/import bundles.
GET /api/voices/{id}/exportstreams a<id>.voice.zip(audio,reference.txt,meta.jsonwith seed, design prompt, rating/tags,provenance,audio_problems; optional picture and consent recording;manifest.jsonwith format version, app version and sha256 per file; no temp files).POST /api/voices/importvalidates at the boundary: 150 MB upload cap, fixed flat member names only (zip-slip/path traversal rejected), per-file, total and compression-ratio caps, hashes verified, audio decoded, meta and provenance re-validated; a taken name gets a_2suffix, an identical voice is a no-op, nothing is overwritten. Voice library toolbar: "Export selected" and "Import bundle" (44px). Tests:tests/test_voice_bundle.py,tests/voice-bundle.spec.js. Seearchitecture/voice-bundle.md. - Consent/provenance field per voice. Each voice gets
provenancein its.meta.json(basis: own_voice, consented, licensed, public_domain or unknown;source,licence,note, optionalrecorded_consent,set_at). Existing voices count asunknown;quick-import-voiceprefills the source URL (basis staysunknown). NewPUT /api/voices/{id}/provenance(enum, length caps, control characters stripped, http(s) URLs only, no credentials) andPOST/GET /api/voices/{id}/consent-recording(audio only, 10 MB cap, stored as<voice>.consent, moves/deletes with the voice). The voice inspector has a "Source & consent" section (typed note or MediaRecorder recording) and voices without a recorded basis show a "Source unknown" badge. Generation is not blocked. Data stays local. Tests:tests/test_voice_provenance.py,tests/voice-provenance.spec.js. - Seed Finder: Auto-pick best.
POST /api/seed-autopick {voice, text, seeds|count}(default 8, max 16) generates candidates sequentially through the existing temporary-voice path (voices.json is always restored), scores each 0-100 (ASR word accuracy, loudness/clipping,audio_problems) and returns all sub-scores plus the winner. Degrades to loudness/problems when STT is unavailable. The UI highlights the winner; saving stays a click on "Use seed". Tests:tests/test_seed_autopick.py,tests/seed-autopick.spec.js. - Voice Design: attribute chips. Toggle chips (gender, age, pitch, pace, accent/region, tone/timbre, emotion; German + English labels) above the design prompt add or remove only their own phrase in the free-text description. The textarea stays fully free-form. Test:
tests/design-chips.spec.js. - Voice health: timestamped audio problems.
core.audio.audio_problemsflags long interior silence, clipping and sudden level jumps (problemslist withkind,start_s,end_s,detail, plus one warning per kind) in the route-test voice health; existing keys unchanged. Computed on demand and cached by file mtime, not in the voice list. Shown in the routing test result and in the Try-out voice panel (newGET /api/voices/{id}/health); imports store the result in meta (audio_problems). Tests:tests/test_audio_problems.py,tests/voice-health.spec.js.
Docs
- Audiobook emotions section in
Manual.md(filling, editing, voice types that honour them, limits) and an Emotions section inarchitecture/audiobook-attribution.md.
Changed
- Magazine interviews: journalist/guest voices. Interview labels such as
c't:(alsoc’t,ct, OCRCc't),Redaktion,Interviewer,Frageare the interviewing side and are read in the narrator voice; every other recurring label is a guest and gets the second voice. Unlabelled blocks continue the current speaker's turn; headings, decks and bylines stay narrator. Detection stays strict (label starts a block and recurs at least twice). Audio-timing records now store each segment's voice and speaker. Tests:tests/test_magazine.py(InterviewJournalistTests).
1.59.0 — 2026-10-04
Changed
- Characters: global cast view in the sidebar, per-book characters inside Studio. Library > Characters / Cast is now titled "All characters (all books)": one collapsible band per book (heading, per-book count,
aria-expanded), first book open and the rest closed and not rendered until opened (DOM budget), and a Search all books field. Studio > Voices lists only the book picked in Source, under a "Book: