← back to AgentAbrams

video/tts_engine.py

77 lines

#!/usr/bin/env python3
"""
tts_engine.py — Text-to-speech using ElevenLabs API.

Generates a 55-year-old male voiceover from script text.

Usage:
    python3 video/tts_engine.py "Text to speak" output.mp3

Environment variables:
    ELEVEN_API_KEY
    ELEVEN_VOICE_ID (optional, defaults to a preset)
"""

import os
import sys

import requests

DEFAULT_VOICE_ID = "pNInz6obpgDQGcFmaJgB"  # Adam — deep male voice


def generate_audio(text: str, output_file: str, voice_id: str | None = None):
    api_key = os.environ.get("ELEVEN_API_KEY")
    if not api_key:
        print("ERROR: ELEVEN_API_KEY not set", file=sys.stderr)
        sys.exit(1)

    vid = voice_id or os.environ.get("ELEVEN_VOICE_ID", DEFAULT_VOICE_ID)
    url = f"https://api.elevenlabs.io/v1/text-to-speech/{vid}"

    headers = {
        "xi-api-key": api_key,
        "Content-Type": "application/json",
    }

    payload = {
        "text": text,
        "model_id": "eleven_monolingual_v1",
        "voice_settings": {
            "stability": 0.65,
            "similarity_boost": 0.70,
            "style": 0.3,
        },
    }

    resp = requests.post(url, json=payload, headers=headers, timeout=120)
    resp.raise_for_status()

    with open(output_file, "wb") as f:
        f.write(resp.content)

    size_kb = len(resp.content) / 1024
    print(f"Audio generated: {output_file} ({size_kb:.0f} KB)")


def main():
    if len(sys.argv) < 3:
        print("Usage: tts_engine.py <text_or_file> <output.mp3>", file=sys.stderr)
        sys.exit(2)

    source = sys.argv[1]
    output = sys.argv[2]

    from pathlib import Path

    if Path(source).is_file():
        text = Path(source).read_text(encoding="utf-8")
    else:
        text = source

    generate_audio(text, output)


if __name__ == "__main__":
    main()