DEV Community

Leanvox
Leanvox

Posted on

Automate YouTube Shorts with AI Voice: Generate 50 Videos a Week with Python

YouTube Shorts rewards volume. Channels that post 5–10 times a day grow faster than those that post once a week.

Manual production at that volume is impossible. With an LLM writing scripts and a TTS API doing voiceover, you can generate 50 Shorts a week with a Python script that runs every morning.

The architecture

  1. Script generation — Claude writes a 30–60 second script
  2. Voiceover generation — LeanVox converts it to audio
  3. Video assembly — ffmpeg combines voiceover with stock footage
  4. Upload — YouTube Data API v3 schedules the video

Stage 1: Script generation

import anthropic

claude = anthropic.Anthropic()

def write_short_script(topic: str) -> str:
    response = claude.messages.create(
        model="claude-opus-4-5",
        max_tokens=300,
        messages=[{"role": "user", "content": f"""Write a YouTube Shorts script about: {topic}
Length: 45-60 seconds (~120-150 words). Start with a hook. End with a CTA. Return only the script."""}]
    )
    return response.content[0].text
Enter fullscreen mode Exit fullscreen mode

Stage 2: Voiceover via CLI

For a single Short, use the CLI directly:

# Generate voiceover from script file
lvox generate \
  --model pro \
  --voice podcast_conversational_female \
  --speed 1.1 \
  --file script.txt \
  --output voiceover.mp3
Enter fullscreen mode Exit fullscreen mode

Or via Python SDK with async jobs for batch processing:

from leanvox import Leanvox
import requests, os

client = Leanvox(api_key="lv_live_...")
CHANNEL_VOICE = "podcast_conversational_female"

def generate_voiceover(script: str, output_path: str) -> str:
    job = client.generate_async(
        text=script,
        model="pro",
        voice=CHANNEL_VOICE,
        speed=1.1,
    )
    result = job.wait()
    audio = requests.get(result.audio_url).content
    with open(output_path, "wb") as f:
        f.write(audio)
    return output_path
Enter fullscreen mode Exit fullscreen mode

Stage 3: Video assembly

import subprocess, json

def get_audio_duration(audio_path: str) -> float:
    result = subprocess.run(
        ["ffprobe", "-v", "quiet", "-print_format", "json", "-show_streams", audio_path],
        capture_output=True, text=True
    )
    return float(json.loads(result.stdout)["streams"][0]["duration"])

def assemble_short(audio_path: str, background_video: str, output_path: str) -> str:
    duration = get_audio_duration(audio_path)
    subprocess.run([
        "ffmpeg", "-y", "-stream_loop", "-1",
        "-i", background_video, "-i", audio_path,
        "-t", str(duration), "-c:v", "libx264", "-c:a", "aac",
        "-vf", "scale=1080:1920",  # vertical 9:16 for Shorts
        output_path
    ], check=True, capture_output=True)
    return output_path
Enter fullscreen mode Exit fullscreen mode

Clone your voice for brand consistency

with open("my_voice.wav", "rb") as f:
    voice = client.voices.clone(name="My Channel Voice", audio=f)
    client.voices.unlock(voice.voice_id)

job = client.generate_async(text=script, model="pro", voice=voice.voice_id, speed=1.1)
Enter fullscreen mode Exit fullscreen mode

Or describe your channel persona with Max tier:

job = client.generate_async(
    text=script,
    model="max",
    instructions="Energetic tech educator, male, early 30s. Enthusiastic but not annoying. Clear and direct."
)
Enter fullscreen mode Exit fullscreen mode

Weekly automated batch

import schedule, time

def weekly_batch():
    topics = get_this_weeks_topics()
    # Submit all jobs in parallel
    pending = [(topic, client.generate_async(text=write_short_script(topic), model="pro", voice=CHANNEL_VOICE)) for topic in topics]
    # Collect and assemble
    for topic, job in pending:
        result = job.wait()
        video = assemble_short(result.audio_url, "bg.mp4", f"queue/{hash(topic)}.mp4")
        schedule_youtube_upload(video, topic)

schedule.every().monday.at("06:00").do(weekly_batch)
while True:
    schedule.run_pending()
    time.sleep(60)
Enter fullscreen mode Exit fullscreen mode

What it costs

A typical 45-second Short = ~750 characters.

Volume Cost/video Monthly
1 Short/day $0.0075 $0.23
5 Shorts/day $0.0075 $1.13
50 Shorts/day $0.0075 $11.25

Your $1.00 signup credit covers 130+ Shorts.

Try it

Browse voices · Get API key · Docs


Originally published at leanvox.com/blog

Top comments (0)