Download client.py from ajh-code/Fish-Audio-S2-Pro-NVFP4-Balanced: direct link, hf CLI and curl.
- Browser
- Download file 3.64 kB
-
https://huggingface.co/ajh-code/Fish-Audio-S2-Pro-NVFP4-Balanced/resolve/main/client.py
- Command line
-
hf download hf://ajh-code/Fish-Audio-S2-Pro-NVFP4-Balanced/client.py
-
curl -L -o client.py https://huggingface.co/ajh-code/Fish-Audio-S2-Pro-NVFP4-Balanced/resolve/main/client.py
3.64 kB
| #!/usr/bin/env python | |
| """Call a project Fish TTS API with a zero-shot voice or saved reference ID.""" | |
| from __future__ import annotations | |
| import argparse | |
| import base64 | |
| from pathlib import Path | |
| import requests | |
| def parse_args() -> argparse.Namespace: | |
| parser = argparse.ArgumentParser(description=__doc__) | |
| parser.add_argument("--url", default="http://127.0.0.1:8080/v1/tts") | |
| parser.add_argument("--text", required=True) | |
| voice = parser.add_mutually_exclusive_group() | |
| voice.add_argument("--reference-audio", type=Path) | |
| voice.add_argument("--reference-id") | |
| parser.add_argument("--reference-text") | |
| parser.add_argument("--output", type=Path, default=Path("output.wav")) | |
| parser.add_argument("--api-key") | |
| parser.add_argument("--seed", type=int) | |
| parser.add_argument("--max-new-tokens", type=int, default=1024) | |
| parser.add_argument("--chunk-length", type=int, default=200) | |
| parser.add_argument("--top-p", type=float, default=0.9) | |
| parser.add_argument("--temperature", type=float, default=0.9) | |
| parser.add_argument("--repetition-penalty", type=float, default=1.1) | |
| parser.add_argument( | |
| "--normalize", | |
| action=argparse.BooleanOptionalAction, | |
| default=True, | |
| help="Enable or disable API loudness normalization", | |
| ) | |
| parser.add_argument( | |
| "--use-memory-cache", | |
| choices=("on", "off"), | |
| default="on", | |
| help="Reuse cached reference codes/text, or force a fresh reference encode", | |
| ) | |
| return parser.parse_args() | |
| def main() -> int: | |
| args = parse_args() | |
| references = [] | |
| if args.reference_audio is not None: | |
| if not args.reference_audio.is_file(): | |
| raise SystemExit(f"Reference audio is missing: {args.reference_audio}") | |
| if not args.reference_text or not args.reference_text.strip(): | |
| raise SystemExit("--reference-text is required with --reference-audio") | |
| references.append( | |
| { | |
| "audio": base64.b64encode(args.reference_audio.read_bytes()).decode( | |
| "ascii" | |
| ), | |
| "text": args.reference_text, | |
| } | |
| ) | |
| elif args.reference_text: | |
| raise SystemExit("--reference-text requires --reference-audio") | |
| payload = { | |
| "text": args.text, | |
| "references": references, | |
| "reference_id": args.reference_id, | |
| "format": "wav", | |
| "streaming": False, | |
| "normalize": args.normalize, | |
| "max_new_tokens": args.max_new_tokens, | |
| "chunk_length": args.chunk_length, | |
| "top_p": args.top_p, | |
| "temperature": args.temperature, | |
| "repetition_penalty": args.repetition_penalty, | |
| "seed": args.seed, | |
| "use_memory_cache": args.use_memory_cache, | |
| } | |
| headers = {"Accept": "audio/wav"} | |
| if args.api_key: | |
| headers["Authorization"] = f"Bearer {args.api_key}" | |
| response = requests.post( | |
| args.url, | |
| json=payload, | |
| headers=headers, | |
| timeout=600, | |
| ) | |
| if response.status_code != 200: | |
| raise SystemExit( | |
| f"TTS request failed ({response.status_code}): {response.text[:2000]}" | |
| ) | |
| content_type = response.headers.get("content-type", "") | |
| if "audio/" not in content_type and not response.content.startswith(b"RIFF"): | |
| raise SystemExit(f"Unexpected response content type: {content_type}") | |
| args.output.parent.mkdir(parents=True, exist_ok=True) | |
| args.output.write_bytes(response.content) | |
| print(f"audio={args.output}") | |
| print(f"bytes={len(response.content)}") | |
| return 0 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |