#!/usr/bin/env python3 """Voice generation via Pocket-TTS API. Uses POST for long text (no URL length limits), falls back to GET for short text. """ import sys, urllib.parse, subprocess, os, hashlib, time, json TTS_URL = "https://hf4uwho-pocket-tts.hf.space/tts" OUTDIR = "/tmp/tts" os.makedirs(OUTDIR, exist_ok=True) def generate(text, voice="af_alloy", fmt="ogg", timeout=None): if timeout is None: timeout = max(120, int(len(text) / 35)) # ~35 chars/sec on CPU-basic h = hashlib.md5(f"{text}{voice}{time.time()}".encode()).hexdigest()[:12] outfile = f"{OUTDIR}/voice_{h}.{fmt}" if len(text) < 1800: # Short text: GET (URL-safe) encoded = urllib.parse.quote(text) url = f"{TTS_URL}?text={encoded}&voice={voice}&format={fmt}" r = subprocess.run( ["curl", "-s", "-o", outfile, "-w", "%{http_code}\n%{time_total}\n%{size_download}", "--max-time", str(timeout), "--connect-timeout", "15", url], capture_output=True, text=True, timeout=timeout+10 ) else: # Long text: POST (no URL length limit) payload = json.dumps({"text": text, "voice": voice, "temperature": 0.7, "format": fmt}) r = subprocess.run( ["curl", "-s", "-o", outfile, "-w", "%{http_code}\n%{time_total}\n%{size_download}", "--max-time", str(timeout), "--connect-timeout", "15", "-X", "POST", "-H", "Content-Type: application/json", "-d", payload, TTS_URL], capture_output=True, text=True, timeout=timeout+10 ) lines = r.stdout.strip().split("\n") code = lines[0] if lines else "?" t = lines[1] if len(lines) > 1 else "?" sz = lines[2] if len(lines) > 2 else "?" if code == "200" and os.path.exists(outfile) and os.path.getsize(outfile) > 100: actual = os.path.getsize(outfile) print(f"HTTP:{code} TIME:{t}s SIZE:{actual}bytes FILE:{outfile}", file=sys.stderr) return outfile else: raise RuntimeError(f"Failed: HTTP {code}, size={sz}") if __name__ == "__main__": if len(sys.argv) < 2: print('Usage: voice.py "text" [voice] [format]', file=sys.stderr) print(' voice.py --file input.txt [voice] [format]', file=sys.stderr) sys.exit(1) if sys.argv[1] == "--file": with open(sys.argv[2]) as f: text = f.read().strip() voice = sys.argv[3] if len(sys.argv) > 3 else "af_alloy" fmt = sys.argv[4] if len(sys.argv) > 4 else "ogg" else: text = sys.argv[1] voice = sys.argv[2] if len(sys.argv) > 2 else "af_alloy" fmt = sys.argv[3] if len(sys.argv) > 3 else "ogg" try: outfile = generate(text, voice, fmt) print(outfile) except Exception as e: print(f"ERROR: {e}", file=sys.stderr) sys.exit(1)