#!/usr/bin/env python3 """ Generiert alle Voice-Line-MP3s via OpenAI TTS API. Liest App/sims/logistik/assets/audio/voice-lines.json und legt pro Eintrag zwei Dateien an: .mp3 (std) und _easy.mp3 (easy). Aufruf: set -a; source App/.env.local; set +a python App/sims/logistik/scripts/generate-voice-lines.py Vorhandene Dateien werden uebersprungen (FORCE=1 zum Ueberschreiben). """ import json import os import ssl import sys import time import urllib.request from pathlib import Path SCRIPT_DIR = Path(__file__).resolve().parent LINES_JSON = SCRIPT_DIR.parent / "assets" / "audio" / "voice-lines.json" OUT_DIR = SCRIPT_DIR.parent / "assets" / "audio" / "voice" / "de" API_KEY = os.environ.get("OPENAI_API_KEY") if not API_KEY: print("FEHLER: OPENAI_API_KEY muss als Env-Variable gesetzt sein.", file=sys.stderr) sys.exit(1) FORCE = bool(os.environ.get("FORCE")) SSL_CTX = ssl.create_default_context() SSL_CTX.check_hostname = False SSL_CTX.verify_mode = ssl.CERT_NONE def tts(text, voice="nova", model="tts-1", speed=1.0): """Ruft OpenAI TTS auf, gibt mp3-bytes zurueck.""" payload = json.dumps({ "model": model, "voice": voice, "input": text, "speed": speed, "response_format": "mp3", }).encode("utf-8") req = urllib.request.Request( "https://api.openai.com/v1/audio/speech", data=payload, headers={ "Authorization": "Bearer " + API_KEY, "Content-Type": "application/json", }, ) with urllib.request.urlopen(req, timeout=60, context=SSL_CTX) as resp: return resp.read() def main(): OUT_DIR.mkdir(parents=True, exist_ok=True) data = json.loads(LINES_JSON.read_text(encoding="utf-8")) voice = data.get("_voice", "nova") model = data.get("_model", "tts-1") lines = data["lines"] count_new = 0 count_skip = 0 total_chars = 0 for entry in lines: lid = entry["id"] for variant, text_key, speed in (("", "std", 1.0), ("_easy", "easy", 0.88)): text = entry.get(text_key) if not text: continue fname = f"{lid}{variant}.mp3" out_path = OUT_DIR / fname if out_path.exists() and not FORCE: count_skip += 1 print(f"ok {fname} (schon da)") continue total_chars += len(text) print(f"-> {fname}: {len(text)} Zeichen, speed={speed} ...", end="", flush=True) try: mp3 = tts(text, voice=voice, model=model, speed=speed) except Exception as e: print(f" FEHLER: {e}") continue out_path.write_bytes(mp3) size_kb = len(mp3) / 1024 print(f" {size_kb:.0f} KB") count_new += 1 time.sleep(0.35) # sanftes Rate-Limit # tts-1 Kosten: $15 / 1M Zeichen (2024) cost_eur = total_chars / 1_000_000 * 15 * 0.92 # USD -> EUR approx print(f"\nFertig: {count_new} neu, {count_skip} uebersprungen.") print(f"Zeichen neu erzeugt: {total_chars} (~{cost_eur:.3f} EUR)") if __name__ == "__main__": main()