You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
77 lines
2.6 KiB
77 lines
2.6 KiB
#!/usr/bin/env python3
|
|
"""Phase 3 Step 3.2: Print audios_generation calls for voice preview samples.
|
|
|
|
Usage: python3 scripts/print_preview_calls.py
|
|
|
|
Reads preview_plan.json, auto-assigns normalized filenames if missing,
|
|
writes them back, and prints grouped audios_generation calls (max 3 per wave).
|
|
"""
|
|
import json, os, re
|
|
from pathlib import Path
|
|
|
|
PROJECT_DIR = Path(os.getcwd())
|
|
AUDIOBOOK_ROOT = PROJECT_DIR / ".audiobook"
|
|
BOOK_DIR = next(d for d in sorted(AUDIOBOOK_ROOT.iterdir()) if d.is_dir() and (d / ".audiobook-state.json").exists())
|
|
|
|
plan_path = BOOK_DIR / "voice_samples" / "preview_plan.json"
|
|
plan = json.loads(plan_path.read_text(encoding="utf-8"))
|
|
|
|
|
|
def normalize_role(role_name: str) -> str:
|
|
"""Convert role name to ASCII-safe filename component."""
|
|
ascii_only = re.sub(r'[^\x00-\x7F]', '', role_name)
|
|
if not ascii_only.strip():
|
|
return f"role{abs(hash(role_name)) % 10000:04d}"
|
|
normalized = re.sub(r'[^a-zA-Z0-9]+', '_', ascii_only).strip('_').lower()
|
|
return normalized or f"role{abs(hash(role_name)) % 10000:04d}"
|
|
|
|
|
|
# Auto-assign filenames if missing
|
|
used_filenames = set()
|
|
updated = False
|
|
global_idx = 0 # collision fallback counter
|
|
|
|
for role in plan:
|
|
role_tag = normalize_role(role["role"])
|
|
for ci, c in enumerate(role["candidates"]):
|
|
if not c.get("filename"):
|
|
fn = f"preview_{role_tag}_{ci}"
|
|
# Handle collisions
|
|
if fn in used_filenames:
|
|
fn = f"preview_{global_idx}"
|
|
global_idx += 1
|
|
c["filename"] = fn
|
|
updated = True
|
|
used_filenames.add(c["filename"])
|
|
|
|
if updated:
|
|
plan_path.write_text(json.dumps(plan, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
print(f"Auto-assigned filenames written back to {plan_path.name}\n")
|
|
|
|
|
|
# Flatten all calls: 1 call per candidate
|
|
calls = []
|
|
for role in plan:
|
|
for c in role["candidates"]:
|
|
calls.append({
|
|
"text": role["sample_text"],
|
|
"voice_id": c["voice_id"],
|
|
"speed": role["speed"],
|
|
"filename": c["filename"],
|
|
})
|
|
|
|
# Group into waves of 3
|
|
MAX_PER_WAVE = 3
|
|
waves = [calls[i:i+MAX_PER_WAVE] for i in range(0, len(calls), MAX_PER_WAVE)]
|
|
|
|
print(f"=== Preview Plan: {len(calls)} samples, {len(waves)} waves ===\n")
|
|
for w_i, wave in enumerate(waves):
|
|
print(f"--- Wave {w_i+1} ({len(wave)} calls) ---")
|
|
for c in wave:
|
|
print(f" audios_generation(")
|
|
print(f" texts=[{repr(c['text'])}],")
|
|
print(f" voice_id={repr(c['voice_id'])},")
|
|
print(f" speed={c['speed']},")
|
|
print(f" filenames=[{repr(c['filename'])}]")
|
|
print(f" )")
|
|
print()
|
|
|