Zum Inhalt

podcast-to-youtube

English · Deutsch

License: AGPL v3 Docs: CC BY-SA 4.0 Release CI Tests Python Platform: macOS Status: Active

Automated end-to-end pipeline: podcast audio → finished YouTube video, running locally on Apple Silicon.

Target platform: Apple Silicon Mac, macOS 15+. Mac-only by design.

Status: v1.0.0 — first public release. The full four-phase pipeline runs end-to-end; the WebGUI is the primary interface. Mac Silicon, AGPL-3.0.

The WebGUI start screen: an audio-path field, the visualizer, language, model and speaker options, and the Start pipeline button.

Drop an episode, pick the options, start the pipeline — everything runs locally.


About

A single audio file (.m4a / .mp3 / .wav) becomes a private YouTube video. Everything runs locally on Mac hardware — transcription with WhisperX, metadata generation with a local MLX-served LLM, video rendering with Remotion. The only network call is the YouTube upload itself.


Release status

For full per-release notes see CHANGELOG.md.

Version Date Headline
v1.0.0 2026-05-22 Initial public release — four-phase pipeline (transcribe · metadata · render · upload), WebGUI + TUI, 64 tests.

Features

Four phases, one pipeline:

flowchart TD
    A[Audio · m4a / mp3 / wav] --> B[Transcribe · WhisperX]
    B --> C[Metadata · local LLM]
    C --> D[Render · Remotion]
    D --> E[Upload · YouTube Data API]
    E --> F[Private video]
  1. Transcribe — WhisperX produces a word-level transcript (JSON / SRT / TXT) with speaker labels.
  2. Metadata — a local MLX LLM generates the YouTube title, description, tags and chapters.
  3. Render — Remotion renders a 1920×1080 MP4 with an audio visualiser.
  4. Upload — the YouTube Data API v3 publishes the video as private. Upload is a manual, explicit step.

Requirements

  • macOS on Apple Silicon — the pipeline is built around WhisperX and a locally served MLX model.
  • uv for the Python environment · Node.js for the Remotion renderer.
  • ffmpeg — audio probing and rendering (brew install ffmpeg).
  • A local, OpenAI-compatible LLM server on port 8080, serving the model that writes the metadata — see the central LLM setup guide. Which model it serves is configured via MLX_MODEL (see Configuration).
  • A Google Cloud OAuth client — client_secrets.json (Desktop App, with the YouTube Data API v3 enabled). upload_youtube.py prints the Google Cloud setup steps if the file is missing.

Install

git clone https://git.jkaindl.de/jkaindl/podcast-to-youtube.git
cd podcast-to-youtube

# Python environment (uv reads pyproject.toml + uv.lock, creates .venv)
uv sync                       # add --extra transcribe for the WhisperX phase
source .venv/bin/activate

# system tools
brew install ffmpeg

# Remotion dependencies (once)
cd visualizer && npm install && cd ..

# YouTube OAuth (once — needs a real terminal for the browser flow)
python auth_youtube.py

# launch the WebGUI
python webgui.py

The WebGUI opens at http://localhost:8765.


Usage

Two frontends onto the same pipeline: the WebGUI is the primary interface, the CLI runs the same four phases headless. Both read and write the same output/<stem>/run-state.json, so a run started in one can be inspected in the other.

WebGUI

python webgui.py starts a FastAPI + HTMX interface and opens the browser at http://localhost:8765.

Pick an audio file, choose the options, click Start pipeline. The run page streams the live log and phase progress over Server-Sent Events. After the render phase the MP4 preview plays inline. Upload is never automatic — choose the visibility (private / unlisted) and click Upload to YouTube.

WebGUI during a live run: the render phase in progress, the pipeline log streaming, and the transcript and YouTube-metadata previews already populated.
Live run — phase stepper, streaming log, transcript & metadata as they land.
WebGUI ready-to-upload state: the rendered video preview with the upload card, where you choose private or unlisted visibility before confirming the upload.
Ready to upload — render preview ready; you pick visibility and confirm. Never automatic.
WebGUI finished state: all four phases complete, the rendered video, and a card linking to the uploaded YouTube video.
Done — all four phases complete, video rendered and uploaded.
Key Action
Ctrl+R Open the start-pipeline dialog
Ctrl/Cmd+Z (on edit page) Undo the last editor action — native field-undo wins while a text input is focused
Ctrl/Cmd+S (on edit page) Save & return to the run page

Transcript editor

Whisper occasionally mistypes names, jargon, and foreign words. Rather than re-running the whole pipeline, the editor lets the transcript be corrected between phases and reruns only what changed.

The transcript editor: a bulk speaker-rename form, then one card per segment with a timestamp, a speaker dropdown, an editable text field, and merge/split controls. An Undo dropdown sits top-right; Save & Return / Save & Continue / Cancel actions sit at the bottom. Saving invalidates the metadata and render phases so they re-run with the corrected transcript.
Transcript editor — per-segment text and speaker edits, merge/split, bulk speaker rename, and undo. Saving reruns only the affected phases.

Two ways in:

  • Pause after transcribe — tick the Pause after transcribe for editing checkbox on the start form. The pipeline stops after Whisper finishes; the run page surfaces an Edit Transcript button.
  • Edit anytime — every run with a transcript exposes an Edit transcript link on the run page. Saving an edit resets the meta + render phases to pending; click the phase indicator to re-run them with the corrected transcript.

A run paused after transcription: the phase stepper shows Transcribe done with Metadata, Render and Upload pending, and a 'Pipeline paused after Transcribe' card offers 'Edit Transcript' or 'Continue without editing', with the transcript preview below.
Pause after transcribe — the pipeline stops after Whisper; edit the transcript, then continue with Meta + Render.

What the editor can do:

  • Segment text — fix mistyped names, jargon, foreign words.
  • Speaker re-labelling — change the speaker per segment, or bulk-rename SPEAKER_00 → Anna across the whole transcript.
  • Merge / split segments — combine two consecutive segments or split one at the cursor position.
  • Word-level edits — /runs/<stem>/edit/words opens a per-word table for finer-grained corrections.
  • Diff view — /runs/<stem>/diff shows original vs. current, word-by-word.
  • Undo — every action snapshots the prior state. The Undo dropdown shows the recent history; Ctrl/Cmd+Z reverts the last action.

The first save creates a one-time <stem>.whisperx.original.json backup. Snapshots accumulate in output/<stem>/snapshots/, auto-trimmed to the 20 newest.


CLI

The pipeline also runs headless:

source .venv/bin/activate

# full run — transcribe, metadata, render, upload
python pipeline.py podcast.m4a

# skip the upload
python pipeline.py podcast.m4a --skip-upload

# pick a visualiser
python pipeline.py podcast.m4a --viz dialogue --skip-upload
python pipeline.py podcast.m4a --viz monologue --skip-upload

# speaker diarization (requires accepting the pyannote terms on huggingface.co)
python pipeline.py podcast.m4a --hf-token $HF_TOKEN

python pipeline.py --help

A Textual TUI is kept as a fallback frontend: python tui.py podcast.m4a.

Output lands in output/<stem>/:

  • <stem>.whisperx.json — word-level transcript with speaker labels
  • <stem>.whisperx.original.json — pristine backup, created the first time the transcript is edited (see Transcript editor)
  • <stem>.srt — subtitles
  • <stem>.txt — plain-text transcript
  • <stem>.youtube-meta.json — title, description, tags, chapters
  • <stem>-<viz>.mp4 — the finished video (1920×1080, 30 fps)
  • snapshots/<unix-ts>.json — per-mutation undo snapshots written by the editor; auto-trimmed to the 20 newest

Scripts

Script Purpose
pipeline.py Orchestrates all four phases
transcribe.py WhisperX: audio → JSON / SRT / TXT
generate_meta.py MLX LLM: transcript → YouTube metadata
render_video.py Remotion: audio + transcript → MP4
upload_youtube.py YouTube Data API v3: MP4 → private video
auth_youtube.py One-time OAuth authorisation
download_models.py Pre-fetch all models for offline use

Configuration

File Contents
client_secrets.json Google OAuth credentials (not committed)
.youtube_token.pickle Cached OAuth token (not committed)
playlists.json Playlist auto-assignment — copy from playlists.example.json
.env Optional environment variables

Environment variables:

  • MLX_BASE_URL — base URL of the local LLM server (default http://localhost:8080/v1)
  • MLX_MODEL — the local LLM model id
  • HF_TOKEN — Hugging Face token for speaker diarization

Offline use

python download_models.py pre-fetches the Whisper and alignment models so the pipeline runs without internet (except the upload). --hf-token adds the diarization model; --status shows the cache state.


Test suite

uv run pytest tests/ -q        # or: make test

151 unit and integration tests covering the shared pipeline core, the probe and run-history helpers, the job runner, every WebGUI route, and the four transcript-editor modules (transcript_editor, transcript_segment_ops, transcript_word_ops, transcript_history, transcript_diff). Runs in ~3 s on Apple Silicon.


Documentation

The full user manual lives in docs/manual/, organised by Diátaxis:

  • Tutorial — your first video, start to finish.
  • How-to guides — focused recipes (headless runs, transcript editing, offline models, upload visibility).
  • Reference — every CLI flag, WebGUI route, the run-state.json schema, configuration + environment.
  • Explanation — the four-phase architecture and the design rationale.

Project layout

pipeline.py            Orchestrator — four phases, writes run-state.json
pipeline_core.py       Shared helpers (TUI + WebGUI)
transcribe.py          WhisperX step
generate_meta.py       Metadata step (local MLX LLM)
render_video.py        Render step (Remotion)
upload_youtube.py      Upload step (YouTube Data API v3)
transcript_editor.py        Editor V1 — load / save / regen / invalidate
transcript_segment_ops.py   Editor — merge, split, change_speaker, bulk_rename
transcript_word_ops.py      Editor — load_words_flat, save_word_edits
transcript_history.py       Editor — snapshot, undo_last, cleanup_snapshots
transcript_diff.py          Editor — compute_segment_diff vs .original.json
webgui/                FastAPI app — routes, job runner, SSE, templates, static
webgui.py              WebGUI entry point
tui*.py                Textual TUI (fallback frontend)
visualizer/            Remotion project (Node) — the video renderer
tests/                 pytest suite
docs/                  Design specs, implementation plans, screenshots
tools/                 Dev tooling (e.g. screenshot regeneration)
pyproject.toml         Project metadata, deps, and ruff/mypy/pytest config (uv)
uv.lock                Pinned dependency lockfile
Makefile               Standard targets (install / check / serve / …)

Contributing

Issues and pull requests are welcome on the project's Forgejo instance — the issue templates in .gitea/issue_template/ prompt for everything that's needed. For larger changes, open an issue first. See CONTRIBUTING.md for the development workflow and SECURITY.md for security-sensitive reports.


Project status

Actively maintained by a single contributor. Apple Silicon focus — the pipeline is Mac-only by design. Cross-platform pull requests are accepted but not actively driven.


License

Code: AGPL-3.0-or-later (LICENSE). Documentation: CC BY-SA 4.0 (LICENSE-DOCS).

The AGPL network clause keeps modifications to a networked deployment open-source. A commercial license is available for uses that cannot meet the AGPL terms — see LICENSING.md. External contributions are accepted under the CLA.md.


Copyright (C) 2026 Johannes Kaindl.

Modul-Abhängigkeiten

graph LR
    pipeline --> generate_meta
    pipeline --> render_video
    pipeline --> transcribe
    pipeline --> upload_youtube
    transcript_editor --> transcript_align
    transcript_segment_ops --> transcript_editor
    transcript_word_ops --> transcript_editor
    tui --> tui_app
    tui_app --> tui_cmd
    tui_app --> tui_progress
    tui_cmd --> pipeline_core
    tui_progress --> pipeline_core
    webgui_app --> pipeline_core
    webgui_app --> transcript_diff
    webgui_app --> transcript_editor
    webgui_app --> transcript_history
    webgui_app --> transcript_segment_ops
    webgui_app --> transcript_word_ops
    webgui_app --> webgui_probe
    webgui_app --> webgui_runner
    webgui_app --> webgui_runs
    webgui_app --> webgui_settings
    webgui_runner --> pipeline_core

Quelldateien