Spaces:
Sleeping
Sleeping
| #!/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) | |