Quality-first multi-pass audiobook generation from English manuscripts. Includes: - umbraaudio/ core package (pipeline, config, voices, web review UI) - scripts/ CLI utilities + model download script - tts_server/ remote TTS server - specs/ design docs, tests/, pyproject.toml + uv.lock - Qwen3-TTS git submodule (pinned 022e286) with local demo.py UI patch saved at scripts/qwen3_tts_demo_umbra_patch.patch - .env with runtime config/secrets Excluded (see .gitignore): models/ (downloaded via scripts/download_models.sh), output/, logs/, .venv, .umbra runtime data, and book projects (DealHunterShip_*), which are committed separately.
179 lines
7.5 KiB
Python
179 lines
7.5 KiB
Python
#!/usr/bin/env python3
|
|
"""Re-index utterance_index values and fix remaining issues for Chapter 6."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import sqlite3
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
|
|
from umbraaudio.config import load_config
|
|
|
|
CHAPTER_ID = 6
|
|
|
|
|
|
def reindex_and_fix(db_path: Path) -> None:
|
|
conn = sqlite3.connect(str(db_path))
|
|
conn.row_factory = sqlite3.Row
|
|
|
|
# Get all utterances for chapter 6 ordered by current utterance_index
|
|
utts = conn.execute(
|
|
"SELECT id, utterance_index, text, type, speaker_name, emotion, instruct "
|
|
"FROM utterances WHERE chapter_id=? ORDER BY utterance_index",
|
|
(CHAPTER_ID,),
|
|
).fetchall()
|
|
|
|
print(f"Found {len(utts)} utterances before re-indexing")
|
|
|
|
# Re-index sequentially from 0
|
|
for new_idx, u in enumerate(utts):
|
|
conn.execute(
|
|
"UPDATE utterances SET utterance_index=? WHERE id=?",
|
|
(new_idx, u["id"]),
|
|
)
|
|
|
|
conn.commit()
|
|
print("Re-indexed utterances 0 to", len(utts) - 1)
|
|
|
|
# Now fix specific issues with correct new indices
|
|
# Get fresh data
|
|
utts = conn.execute(
|
|
"SELECT id, utterance_index, text, type, speaker_name, emotion, instruct "
|
|
"FROM utterances WHERE chapter_id=? ORDER BY utterance_index",
|
|
(CHAPTER_ID,),
|
|
).fetchall()
|
|
|
|
print("\n=== After re-indexing ===")
|
|
for u in utts:
|
|
if u["utterance_index"] in range(50, 70) or u["utterance_index"] in range(140, 160) or \
|
|
u["utterance_index"] in range(180, 200) or u["utterance_index"] in range(210, 230) or \
|
|
u["utterance_index"] in range(350, 370):
|
|
print(f" utt{u['utterance_index']:03d} [{u['type']}] {u['speaker_name']}: {u['text'][:80]}...")
|
|
|
|
# Fix: "What ship is this?" should be Kainda (not Nanny)
|
|
# Find the utterance with this text
|
|
for u in utts:
|
|
if u["text"] == "What ship is this?" and u["speaker_name"] == "Nanny":
|
|
conn.execute(
|
|
"UPDATE utterances SET speaker_name=?, emotion=?, instruct=? WHERE id=?",
|
|
("Kainda Walters Foster Murphy", "confused", "Speak with confusion and disorientation. Trembling, uncertain delivery.",
|
|
u["id"]),
|
|
)
|
|
print(f"\nFixed utt{u['utterance_index']}: 'What ship is this?' -> Kainda Walters Foster Murphy (confused)")
|
|
|
|
# Verify "Hello." is Nanny (the robot)
|
|
for u in utts:
|
|
if u["text"] == "Hello." and u["utterance_index"] > 180 and u["utterance_index"] < 190:
|
|
if u["speaker_name"] != "Nanny":
|
|
conn.execute(
|
|
"UPDATE utterances SET speaker_name=? WHERE id=?",
|
|
("Nanny", u["id"]),
|
|
)
|
|
print(f"Fixed utt{u['utterance_index']}: 'Hello.' -> Nanny")
|
|
|
|
# Verify "You are on the Deal Hunter." is Nanny
|
|
for u in utts:
|
|
if u["text"] == "You are on the Deal Hunter." and u["speaker_name"] != "Nanny":
|
|
conn.execute(
|
|
"UPDATE utterances SET speaker_name=? WHERE id=?",
|
|
("Nanny", u["id"]),
|
|
)
|
|
print(f"Fixed utt{u['utterance_index']}: 'You are on the Deal Hunter.' -> Nanny")
|
|
|
|
# Verify "There are no rescue ships..." is Nanny
|
|
for u in utts:
|
|
if "no rescue ships" in u["text"] and u["speaker_name"] != "Nanny":
|
|
conn.execute(
|
|
"UPDATE utterances SET speaker_name=? WHERE id=?",
|
|
("Nanny", u["id"]),
|
|
)
|
|
print(f"Fixed utt{u['utterance_index']}: 'There are no rescue ships...' -> Nanny")
|
|
|
|
# Verify utt 211 (now reindexed) "That was a missile..." is Nanny
|
|
for u in utts:
|
|
if "missile trying to disable" in u["text"] and u["speaker_name"] != "Nanny":
|
|
conn.execute(
|
|
"UPDATE utterances SET speaker_name=? WHERE id=?",
|
|
("Nanny", u["id"]),
|
|
)
|
|
print(f"Fixed utt{u['utterance_index']}: 'That was a missile...' -> Nanny")
|
|
|
|
# Verify the scene break was removed (should be no <<<>>> utterances)
|
|
scene_breaks = conn.execute(
|
|
"SELECT id, utterance_index FROM utterances WHERE chapter_id=? AND text='<<<>>>'",
|
|
(CHAPTER_ID,),
|
|
).fetchall()
|
|
if scene_breaks:
|
|
print(f"\nWARNING: {len(scene_breaks)} scene break utterances still exist!")
|
|
for sb in scene_breaks:
|
|
print(f" utt{sb['utterance_index']}: {sb['id']}")
|
|
else:
|
|
print("\nScene breaks removed successfully.")
|
|
|
|
# Verify Willamina Wake yelling at "Get the princess on board!"
|
|
for u in utts:
|
|
if "Get the princess on board" in u["text"]:
|
|
emotion = u["emotion"] if u["emotion"] else ""
|
|
if u["speaker_name"] != "Willamina Wake" or emotion != "yelling":
|
|
conn.execute(
|
|
"UPDATE utterances SET speaker_name=?, emotion=?, instruct=? WHERE id=?",
|
|
("Willamina Wake", "yelling", "Speak with authority and command. Firm, unwavering tone. LOUD.",
|
|
u["id"]),
|
|
)
|
|
print(f"Fixed utt{u['utterance_index']}: 'Get the princess on board!' -> Willamina Wake (yelling)")
|
|
else:
|
|
print(f"utt{u['utterance_index']}: Willamina Wake yelling - already correct")
|
|
|
|
# Verify Kainda pleading at "Help me, please." (the first one, utt ~147)
|
|
for u in utts:
|
|
if u["text"] == "Help me, please." and u["utterance_index"] < 160 and u["speaker_name"] != "Kainda Walters Foster Murphy":
|
|
conn.execute(
|
|
"UPDATE utterances SET speaker_name=?, emotion=?, instruct=? WHERE id=?",
|
|
("Kainda Walters Foster Murphy", "pleading", "Speak with desperate pleading. Urgent, emotional.",
|
|
u["id"]),
|
|
)
|
|
print(f"Fixed utt{u['utterance_index']}: 'Help me, please.' -> Kainda (pleading)")
|
|
elif u["text"] == "Help me, please." and u["utterance_index"] < 160:
|
|
# Check emotion is set
|
|
if u.get("emotion") != "pleading":
|
|
conn.execute(
|
|
"UPDATE utterances SET emotion=?, instruct=? WHERE id=?",
|
|
("pleading", "Speak with desperate pleading. Urgent, emotional.", u["id"]),
|
|
)
|
|
print(f"Fixed utt{u['utterance_index']}: Added pleading emotion")
|
|
|
|
conn.commit()
|
|
|
|
# Final verification
|
|
print("\n=== Final verification of key utterances ===")
|
|
utts = conn.execute(
|
|
"SELECT id, utterance_index, text, type, speaker_name, emotion, instruct "
|
|
"FROM utterances WHERE chapter_id=? ORDER BY utterance_index",
|
|
(CHAPTER_ID,),
|
|
).fetchall()
|
|
|
|
key_indices = [56, 57, 58, 147, 183, 188, 190, 191, 192, 213, 224, 352]
|
|
for u in utts:
|
|
if u["utterance_index"] in key_indices:
|
|
emotion = u["emotion"] if u["emotion"] else ""
|
|
emo = f" [{emotion}]" if emotion else ""
|
|
instruct = u["instruct"] if u["instruct"] else ""
|
|
inst = f" ({instruct[:40]}...)" if instruct else ""
|
|
print(f" utt{u['utterance_index']:03d} [{u['type']}] {u['speaker_name']}{emo}{inst}: {u['text'][:80]}...")
|
|
|
|
conn.close()
|
|
print("\n=== Done ===")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(description="Re-index and fix Chapter 6 utterances")
|
|
parser.add_argument(
|
|
"--project", type=str, default=None, help="Project name or path"
|
|
)
|
|
args = parser.parse_args()
|
|
cfg = load_config(args.project)
|
|
reindex_and_fix(cfg.db_path)
|