Something went wrong. Try again.
Dotfiles for my macos setup
Something went wrong. Try again.
4.2 kB · 121 lines
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122#!/usr/bin/env -S uv run --script --quiet# /// script# requires-python = ">=3.13"# dependencies = [# "kokoro-onnx",# "soundfile",# "numpy",# ]# ///"""kokoro — local TTS CLI wrapper around thewh1teagle/kokoro-onnx.On first run, downloads ~330 MB of model files to ~/.cache/kokoro/.Examples: echo "hello world" | kokoro -o out.wav kokoro -t "hello world" -o out.wav kokoro -f script.txt -o out.wav kokoro -t "hello" --voice af_alloy --speed 1.0 -o out.wav kokoro -t "bonjour" --lang fr-fr --voice ff_siwis -o out.wav kokoro --list-voicesPopular voices (American): af_alloy af_bella af_heart af_nova af_sarah af_sky am_adam am_michael am_onyx am_puckBritish: bf_emma bf_isabella bm_george bm_lewisSee full list: https://huggingface.co/hexgrad/Kokoro-82M/blob/main/VOICES.md"""from __future__ import annotationsimport argparseimport sysimport urllib.requestfrom pathlib import PathCACHE = Path.home() / ".cache" / "kokoro"MODEL_URL = "https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files-v1.0/kokoro-v1.0.onnx"VOICES_URL = "https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files-v1.0/voices-v1.0.bin"def _download(url: str, dest: Path) -> None: dest.parent.mkdir(parents=True, exist_ok=True) tmp = dest.with_suffix(dest.suffix + ".part") print(f"[kokoro] downloading {url} -> {dest}", file=sys.stderr) def hook(blocks: int, block_size: int, total: int) -> None: if total <= 0: return done = min(blocks * block_size, total) pct = done * 100 // total print(f"\r[kokoro] {pct:3d}% ({done // 1024 // 1024} / {total // 1024 // 1024} MiB)", end="", file=sys.stderr, flush=True) urllib.request.urlretrieve(url, tmp, reporthook=hook) print("", file=sys.stderr) tmp.rename(dest)def ensure_models() -> tuple[Path, Path]: model = CACHE / "kokoro-v1.0.onnx" voices = CACHE / "voices-v1.0.bin" if not model.exists(): _download(MODEL_URL, model) if not voices.exists(): _download(VOICES_URL, voices) return model, voicesdef read_text(args: argparse.Namespace) -> str: if args.text: return args.text if args.file: return Path(args.file).read_text(encoding="utf-8") if not sys.stdin.isatty(): return sys.stdin.read() sys.exit("error: provide --text, --file, or pipe text on stdin")def main() -> None: p = argparse.ArgumentParser(prog="kokoro", description="Local Kokoro TTS CLI", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=__doc__) p.add_argument("-t", "--text", help="text to speak") p.add_argument("-f", "--file", help="read text from file") p.add_argument("-o", "--output", default="out.wav", help="output WAV file (default: out.wav)") p.add_argument("--voice", default="af_heart", help="voice id (default: af_heart). Supports blends like 'af_bella(2)+af_heart(1)'.") p.add_argument("--speed", type=float, default=1.0, help="speech speed multiplier (default: 1.0)") p.add_argument("--lang", default="en-us", help="language code: en-us, en-gb, fr-fr, es, it, pt-br, hi, ja, zh, ko (default: en-us)") p.add_argument("--list-voices", action="store_true", help="list all available voices and exit") args = p.parse_args() model_path, voices_path = ensure_models() import soundfile as sf from kokoro_onnx import Kokoro kokoro = Kokoro(str(model_path), str(voices_path)) if args.list_voices: for v in sorted(kokoro.get_voices()): print(v) return text = read_text(args).strip() if not text: sys.exit("error: empty input text") print(f"[kokoro] voice={args.voice} lang={args.lang} speed={args.speed} out={args.output}", file=sys.stderr) samples, sr = kokoro.create(text, voice=args.voice, speed=args.speed, lang=args.lang) sf.write(args.output, samples, sr) print(f"[kokoro] wrote {args.output}", file=sys.stderr)if __name__ == "__main__": main()