The review UI's language dropdown did nothing: every selection kept rendering the English text. Two independent causes, both fixed. 1. The translations were never being read. The pipeline's `utterance_translations` table holds one row in Deal Hunter, and that row stores the *English* text under a `zh-Hans` code. The real translations live in the on-disk sample files under `samples/translations/`. `umbraaudio/translations.py` now resolves both sources and merges them by (chapter_id, utterance_index): the table wins where it holds a real translation, the sample files fill the gaps. A candidate whose text is identical to the source is a placeholder, not a translation, so it is ignored - otherwise the page would show English under a Chinese heading and claim it was translated. 2. The query that read them was broken. `language_routes.py` selected `u.index` and `u.speaker`; the columns are `utterance_index` and `speaker_name`, so every read raised `sqlite3.OperationalError: near "index": syntax error` and `/language-mockup` returned 500. It also opened `<project>/project.db` - a 0-byte decoy - instead of `<project>/.umbra/project.db`, the file that actually holds the data. Also in this commit: * The chapter review page honours `?lang=`, remembers the choice in a cookie (set by a middleware, so it survives on every page), and shows the selected language's *name* in the "has not been translated yet" message. * The page now shows the translation, not the source, and the edit box is bound to the source text so saving cannot overwrite the English with the Chinese. * The stray language selector that sat after `</html>` in base.html is gone; the chapters-list page's dropdown had zero options because the route never passed the language list. * The language tag is wired into generation: `utterance.language_code` (NULL = project source language) reaches `engine.generate()` and the staleness marker, so a retagged line is re-rendered instead of keeping English phonology behind an unchanged text. The marker records the language; a marker written before the tag existed means English, so the ~4,300 existing clips are not invalidated. * `project.source_language` (spec 31 section 13.1) records the book's own language, with a migration for existing project DBs. * A scene-break marker deliberately records no language: it is silence, identical in every language, and tagging it would claim a phonology the audio does not have. Tests: 883 passed / 8 failed - the same 8 pre-existing failures (2 test_pronunciation, 6 voice_profile_manager accent). 26 new tests cover the resolver and the generation wiring. ruff introduces no new findings.
139 lines
3.0 KiB
JSON
139 lines
3.0 KiB
JSON
{
|
|
"speakers": [
|
|
{
|
|
"name": "Kainda Walters Foster Murphy",
|
|
"type": "narration",
|
|
"voice_ref": "kainda_walters_foster_murphy_ref.wav"
|
|
},
|
|
{
|
|
"name": "narrator",
|
|
"type": "narration",
|
|
"voice_ref": "narrator_ref.wav"
|
|
},
|
|
{
|
|
"name": "Deal Hunter",
|
|
"type": "narration",
|
|
"voice_ref": "deal_hunter_ref.wav"
|
|
},
|
|
{
|
|
"name": "Nanny",
|
|
"type": "narration",
|
|
"voice_ref": "nanny_ref.wav"
|
|
},
|
|
{
|
|
"name": "Princess Kainda",
|
|
"type": "dialogue",
|
|
"voice_ref": "princess_kainda_ref.wav"
|
|
},
|
|
{
|
|
"name": "Duchess Victoria",
|
|
"type": "narration",
|
|
"voice_ref": "duchess_victoria_ref.wav"
|
|
},
|
|
{
|
|
"name": "Adric",
|
|
"type": "dialogue",
|
|
"voice_ref": "adric_ref.wav"
|
|
},
|
|
{
|
|
"name": "Kabir",
|
|
"type": "dialogue",
|
|
"voice_ref": "kabir_ref.wav"
|
|
},
|
|
{
|
|
"name": "Adonai Maul",
|
|
"type": "dialogue",
|
|
"voice_ref": "adonai_maul_ref.wav"
|
|
},
|
|
{
|
|
"name": "Xu Ming",
|
|
"type": "dialogue",
|
|
"voice_ref": "xu_ming_ref.wav"
|
|
},
|
|
{
|
|
"name": "Willamina Wake",
|
|
"type": "dialogue",
|
|
"voice_ref": "willamina_wake_ref.wav"
|
|
},
|
|
{
|
|
"name": "Distorted Voice",
|
|
"type": "dialogue",
|
|
"voice_ref": "distorted_voice_ref.wav"
|
|
},
|
|
{
|
|
"name": "Worker Robot (Bill)",
|
|
"type": "dialogue",
|
|
"voice_ref": "worker_robot_(bill)_ref.wav"
|
|
},
|
|
{
|
|
"name": "Captain Weston",
|
|
"type": "dialogue",
|
|
"voice_ref": "captain_weston_ref.wav"
|
|
},
|
|
{
|
|
"name": "Security Administrator",
|
|
"type": "dialogue",
|
|
"voice_ref": "security_administrator_ref.wav"
|
|
},
|
|
{
|
|
"name": "Pirate Clan Leader",
|
|
"type": "dialogue",
|
|
"voice_ref": "pirate_clan_leader_ref.wav"
|
|
},
|
|
{
|
|
"name": "Narrator",
|
|
"type": "dialogue",
|
|
"voice_ref": "narrator_ref.wav"
|
|
},
|
|
{
|
|
"name": "Trader Wakam",
|
|
"type": "dialogue",
|
|
"voice_ref": "trader_wakam_ref.wav"
|
|
},
|
|
{
|
|
"name": "Service Robot",
|
|
"type": "dialogue",
|
|
"voice_ref": "service_robot_ref.wav"
|
|
},
|
|
{
|
|
"name": "Admiral Wilbur Wake",
|
|
"type": "dialogue",
|
|
"voice_ref": "admiral_wilbur_wake_ref.wav"
|
|
},
|
|
{
|
|
"name": "Pilot (Freighter)",
|
|
"type": "dialogue",
|
|
"voice_ref": "pilot_(freighter)_ref.wav"
|
|
},
|
|
{
|
|
"name": "Trader Romano",
|
|
"type": "narration",
|
|
"voice_ref": "trader_romano_ref.wav"
|
|
},
|
|
{
|
|
"name": "Frigate Captain",
|
|
"type": "dialogue",
|
|
"voice_ref": "frigate_captain_ref.wav"
|
|
},
|
|
{
|
|
"name": "Crew Member",
|
|
"type": "dialogue",
|
|
"voice_ref": "crew_member_ref.wav"
|
|
},
|
|
{
|
|
"name": "Brutus",
|
|
"type": "dialogue",
|
|
"voice_ref": "brutus_ref.wav"
|
|
},
|
|
{
|
|
"name": "Guard Detail Speaker 1",
|
|
"type": "dialogue",
|
|
"voice_ref": "guard_detail_speaker_1_ref.wav"
|
|
},
|
|
{
|
|
"name": "Guard Detail Speaker 2",
|
|
"type": "dialogue",
|
|
"voice_ref": "guard_detail_speaker_2_ref.wav"
|
|
}
|
|
]
|
|
} |