Tag: ffmpeg

  • How to Automate YouTube Shorts with Python and FFmpeg

    A single recording session can produce a month of Shorts. OBS captures the source video; Python extracts the clips; FFmpeg assembles them with title cards and audio normalization; the results land in a scheduled queue. Here’s how the pipeline works and where the real technical problems live.

    ## The Manual Alternative

    Without automation, every Short is a separate project: import the clip, trim it, add text, normalize audio, export, upload, write the description, schedule. Multiply that by daily publishing and the content process consumes more time than the recording session that produced it.

    The pipeline inverts that ratio. Recording remains manual — you capture the session, play the game, narrate the moment. Everything after the recording is automated. A two-hour recording session produces a batch of Shorts, scheduled across the next two weeks, without touching a video editor.

    ## Pipeline Architecture

    **Stage 1: Capture.** OBS records the session with NVENC hardware encoding. The recording is a single long source file — one session, one file.

    **Stage 2: Beat extraction.** A Python script reads a YAML file that defines the beats — timestamp pairs marking where each clip starts and ends. The beats are written during or after the session, while the content is fresh. Each beat includes metadata: the clip title, any title card text, whether the outro should be appended.

    **Stage 3: Clip assembly.** For each beat, FFmpeg extracts the clip from the source file and assembles the output: title card burned in, audio normalized, outro appended if configured. The output is a single MP4 per Short, ready to upload.

    **Stage 4: Upload queue.** The assembled Shorts land in a staging directory with a manifest file — clip filename, title, description template, scheduled publish date. Scheduled upload can be handled via the YouTube Data API (requires OAuth setup) or manually via YouTube Studio using the manifest as a reference.

    ## The Beat File

    The beat file is the human-in-the-loop interface. Everything else is automated; this is where editorial judgment lives.

    “`yaml
    beats:
    – title: “the game told me what I became”
    start: “00:34:12”
    end: “00:34:34”
    title_card: “the game told me what I became”
    outro: true

    – title: “I had no reason to be nervous”
    start: “01:02:45”
    end: “01:03:03”
    title_card: “I had no reason to be nervous”
    outro: true
    “`

    The pipeline reads this file, processes each beat sequentially, and produces one output file per entry. Adding a new Short to the queue means adding a beat entry — no video editor, no manual assembly.

    ## FFmpeg Assembly

    The core FFmpeg command for each clip handles four operations simultaneously: trim to the beat window, burn in the title card, normalize audio, and concatenate the outro if configured.

    The title card burn-in uses `drawtext`. The audio normalization uses a two-pass `loudnorm` filter to hit a consistent LUFS target across all Shorts regardless of how the source was recorded. The outro is a pre-assembled MP4 concatenated via the `concat` demuxer.

    The key parameters worth tuning:

    – **Font path** — absolute path required on Windows; relative paths fail silently in some FFmpeg builds
    – **Text positioning** — pixel coordinates, not percentages; mobile-safe zones differ from desktop
    – **Audio target** — YouTube recommends -14 LUFS; clips recorded at different volumes need normalization before loudness differences affect viewer retention
    – **Output codec** — H.264 with `yuv420p` pixel format for maximum compatibility; some encoding combinations produce files that upload but play incorrectly on certain devices

    ## The Complication: FFmpeg Drawtext and Encoding Edge Cases

    The drawtext filter is where most automation pipelines stall.

    The first failure mode: drawtext silently produces a video without the title card rather than throwing an error. The font path is wrong, or the font doesn’t support a character in the title, or the filter syntax has a subtle issue — and the output looks correct until you check whether the text is actually there. The pipeline needs an explicit check after assembly, not just a check that the FFmpeg process exited cleanly.

    The second failure mode is the outro timing. Appending an outro by extending the clip to a fixed duration doesn’t work when the clip’s natural end is close to the source file boundary — FFmpeg extends into data that doesn’t exist. The correct approach: use `ffprobe` to read the actual clip duration before assembling, then clamp the outro start point to the verified clip end. A clip that crashes FFmpeg with a duration error is more informative than one that silently produces a truncated output.

    The third failure mode is encoder-specific rendering differences. NVENC hardware encoding and libx264 software encoding produce visually similar output but handle edge cases differently. Text overlays that render correctly with software encoding occasionally produce artifacts with NVENC, particularly at high character density. Testing the full pipeline with both encoders before committing to one prevents discovering this in production.

    ## Scheduled Upload

    The YouTube Data API supports programmatic upload and scheduling. OAuth2 credentials are required — the setup is a one-time process through Google Cloud Console, and the credentials are stored locally.

    The API call sets the video status to `private` with a `publishAt` timestamp in the future. YouTube schedules the publish automatically. The pipeline writes the API response (video ID, scheduled timestamp, title) to a log file that serves as the upload manifest.

    What the API doesn’t provide: thumbnail upload is a separate call, and the thumbnail must be uploaded after the video is created. If thumbnails are part of the pipeline, the upload sequence is: create video → wait for processing → upload thumbnail.

    ## What This Doesn’t Cover

    The pipeline as described handles clip extraction and assembly from a single long recording. It doesn’t handle:

    – **Multi-source assembly** — combining clips from different recording sessions into a single Short
    – **Motion graphics** — complex animated overlays require a compositor (DaVinci Resolve, After Effects) rather than FFmpeg alone
    – **Thumbnail generation** — thumbnail design is still manual; the pipeline can insert a pre-designed template but not generate one
    – **Analytics feedback loop** — the pipeline schedules content based on a manual calendar; incorporating retention data to adjust scheduling cadence requires a separate analytics layer

    ## Frequently Asked Questions

    **Does this work on Mac or Linux, or only Windows?**
    The pipeline is OS-independent. FFmpeg runs everywhere, and the Python script has no platform-specific dependencies. The font path format differs between operating systems — Windows uses backslashes, Mac and Linux use forward slashes — and the font file itself needs to exist at the specified path on each machine.

    **Can this handle vertical (9:16) and horizontal (16:9) sources?**
    Yes, with separate output configurations. The drawtext coordinates and crop parameters differ between orientations. The beat file can specify orientation per clip, and the assembly step selects the appropriate FFmpeg filter chain.

    **What’s the minimum recording quality needed?**
    The pipeline preserves source quality; it doesn’t enhance it. Audio normalization helps inconsistent microphone levels but doesn’t fix poor room acoustics. For Shorts specifically, 1080p source at 60fps gives enough headroom for the encoding pipeline without quality loss in the output.

    ## The Code

    The ContentPipeline source is on GitHub at github.com/rfd62794/ContentPipeline. The pipeline is structured as a Python package with separate modules for beat extraction, FFmpeg assembly, and upload management. It’s documented with examples and is currently in active production use for a gaming channel publishing daily Shorts.