Spaces:
Sleeping
Sleeping
| #!/usr/bin/env python3 | |
| """Render every spoken line of the pre-authored packs into the audio cache. | |
| python scripts/prebuild_audio.py # tất cả pack, tốc độ 1.0 | |
| python scripts/prebuild_audio.py --pack bai-10-tu-giac | |
| python scripts/prebuild_audio.py --speeds 0.9 1.0 1.1 | |
| python scripts/prebuild_audio.py --force # dựng lại kể cả đã có | |
| Run this once after editing a pack, or as a build step before deploying. The | |
| lines are known in advance, so there is no reason for a student to wait on the | |
| model: the first click should start talking immediately. | |
| The cache key covers provider, voice, speed and format, so changing | |
| SPEECH_PROVIDER or VIENEU_VOICE means re-running this — the old clips stay put | |
| but will never be read again. Delete content/.audio-cache to reclaim the space. | |
| """ | |
| import argparse | |
| import os | |
| import sys | |
| import time | |
| sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "backend")) | |
| from app.config import settings # noqa: E402 | |
| from app.services import audio_cache, packs, speech # noqa: E402 | |
| def main() -> int: | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument("--pack", help="chỉ dựng một pack") | |
| parser.add_argument("--speeds", nargs="+", type=float, default=[1.0]) | |
| parser.add_argument("--voice", help="ghi đè giọng mặc định") | |
| parser.add_argument("--force", action="store_true", help="dựng lại cả câu đã có") | |
| args = parser.parse_args() | |
| if settings.speech_provider == "browser": | |
| print( | |
| "SPEECH_PROVIDER=browser nên máy chủ không đọc hộ — trình duyệt tự tổng hợp\n" | |
| "bằng Web Speech API và không có gì để dựng sẵn cả.\n" | |
| "Đặt SPEECH_PROVIDER=vieneu (hoặc google) rồi chạy lại.", | |
| file=sys.stderr, | |
| ) | |
| return 1 | |
| chosen = [packs.PACKS[args.pack]] if args.pack else list(packs.PACKS.values()) | |
| if args.pack and args.pack not in packs.PACKS: | |
| print(f"Không có pack '{args.pack}'. Hiện có: {', '.join(packs.PACKS)}", file=sys.stderr) | |
| return 1 | |
| print(f"provider: {settings.speech_provider}") | |
| print(f"giọng: {args.voice or settings.vieneu_voice}") | |
| speech.warm_up() | |
| total = built = skipped = failed = 0 | |
| started = time.time() | |
| for pack in chosen: | |
| lines = pack.spoken_lines() | |
| print(f"\n{pack.title} — {len(lines)} câu × {len(args.speeds)} tốc độ") | |
| for speed in args.speeds: | |
| for i, line in enumerate(lines, 1): | |
| total += 1 | |
| if not args.force and audio_cache.get(line, speed, args.voice) is not None: | |
| skipped += 1 | |
| continue | |
| try: | |
| clip = speech.synthesize(line, speed=speed, voice=args.voice, use_cache=False) | |
| audio_cache.put(line, speed, args.voice, clip) | |
| built += 1 | |
| print(f" [{i:3d}/{len(lines)}] {speed:.2f}× {line[:58]}…") | |
| except Exception as exc: # noqa: BLE001 - một câu hỏng không dừng cả mẻ | |
| failed += 1 | |
| print(f" [{i:3d}/{len(lines)}] LỖI: {exc}", file=sys.stderr) | |
| stats = audio_cache.stats() | |
| print( | |
| f"\nXong sau {time.time() - started:.1f}s — dựng mới {built}, bỏ qua {skipped}, " | |
| f"lỗi {failed}, tổng {total}.\n" | |
| f"Cache hiện có {stats['clips']} clip, {stats['size_mb']} MB." | |
| ) | |
| return 1 if failed else 0 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |