Skip to main content

YT Mixer

Server-side YouTube audio/video mixer with professional podcast-style processing, ad-hoc video injection, and iOS-native playback.

Features

  • 🎵 Mix two YouTube playlists (music + speech/podcast) into continuous 1-hour chunks
  • 🎚️ Podcast-quality audio: Automatic sidechain ducking (music lowers for voice), LUFS normalization to broadcast standards (-16 for speech, -23 for music), and a final mastering limiter
  • 🎬 Ad-hoc video injection — queue any YouTube video and it plays with a freshly mixed background track, then seamlessly returns to your stream
  • 📱 iOS-native video playback with HTTP 206 range support and a dedicated video page (no competing <audio> session)
  • 🔁 Persistent server-side job state — close the browser mid-render and the Play button is waiting when you come back
  • 📦 Session-based mixing with bookmarkable URLs
  • ⚡ Optional omnipkg integration for pinned yt-dlp worker processes (~2ms probe vs ~400ms cold import)

Installation

From Source

git clone https://github.com/1minds3t/yt-mixer.git
cd yt-mixer
pip install -e .

With omnipkg daemon support (optional, recommended on Linux):

pip install -e ".[full]"

Requirements

  • Python 3.8+
  • FFmpeg
# Ubuntu/Debian
sudo apt install ffmpeg

# macOS
brew install ffmpeg

Quick Start

yt-mixer serve

Access at http://localhost:5052

Install as a systemd service

yt-mixer service --install
systemctl --user start yt-mixer
systemctl --user enable yt-mixer
yt-mixer service --logs

CLI

# Configuration
yt-mixer config --list
yt-mixer config --set port=5053
yt-mixer config --get port

# Sessions
yt-mixer sessions
yt-mixer sessions --clean

# Service
yt-mixer service --status
yt-mixer service --start | --stop | --restart | --enable

# Update yt-dlp
yt-mixer update

Environment Variables

export YT_MIXER_HOST=0.0.0.0          # default: 0.0.0.0
export YT_MIXER_PORT=5052              # default: 5052
export YT_MIXER_DATA_DIR=./data        # default: ./data
export YT_MIXER_MAX_CHUNKS=3           # default: 3 (chunks kept in memory)
export YT_MIXER_PRUNE_DAYS=7           # default: 7 (days before session cleanup)
export YT_MIXER_CHUNK_DURATION=3600    # default: 3600 (seconds per chunk)
export YT_MIXER_MUSIC_VOLUME=0.4       # default: 0.4
export YT_MIXER_SPEECH_VOLUME=1.0      # default: 1.0

Usage

Background Mix

  1. Open http://localhost:5052
  2. Enter two YouTube playlist IDs or full URLs — one music, one speech/podcast
  3. Click Create Mix and bookmark the generated /?sid=... URL

The mixer produces continuous 1-hour chunks. Each chunk goes through three quality tiers as processing completes: Immediate (raw) → Quick (normalized) → Final (LUFS mastered).

Ad-hoc Video Injection

While the background mix is playing, paste any YouTube URL into the Add Video Audio panel and hit Prepare. The server downloads audio and video in parallel in the background — your mix keeps playing uninterrupted. When ready, hit Play video to watch with a freshly mixed background track underneath. On exit, the background stream resumes where it left off.

State is held server-side: close the tab mid-render, reopen it, and the Play video button appears automatically without re-hitting Prepare.

Bookmarking

http://localhost:5052/?sid=abc123def456

Bookmarking this URL restores your session including playlist configuration and playback position.

Audio Processing

Speech:

  • High-pass filter (removes rumble)
  • Center mono panning
  • LUFS normalization to -16 (podcast standard)
  • Compression

Music:

  • LUFS normalization to -23 (background level)

Mix:

  • Sidechain ducking — music automatically lowers when speech plays
  • Final limiter prevents clipping

omnipkg Integration

When omnipkg is installed (pip install -e ".[full]"), yt_mixer uses its daemon to keep two tagged persistent worker processes alive:

  • ytdlp-info — metadata probes (title, duration, livestream check)
  • ytdlp-dl — audio and video downloads

Workers are pinned (survive idle timeout) and kept alive by a keepalive thread that pings every 4 minutes. This cuts yt-dlp startup from ~400ms to ~2ms and avoids repeated Python interpreter cold-starts for every download. Falls back transparently to direct import yt_dlp if the daemon is unavailable.

Data Storage

data/
├── raw_audio/       # Downloaded audio per session
├── mixed_chunks/    # Generated 1-hour mixed chunks per session
├── adhoc_jobs/      # Ad-hoc video job state and final renders
└── config.json      # Persistent configuration

Sessions auto-clean after 7 days of inactivity. Ad-hoc jobs GC after 2 hours (ready) or 30 minutes (failed/cancelled).

Development

pip install -e ".[dev]"
pytest
black src/
ruff src/

Troubleshooting

yt-dlp errors — YouTube changes its API frequently; update regularly:

yt-mixer update

Port in use:

yt-mixer config --set port=5053

FFmpeg not found:

sudo apt install ffmpeg   # Ubuntu/Debian
brew install ffmpeg       # macOS

Video won't play on iOS — This is usually caused by an audio session conflict. The Play video button solves this automatically by navigating to a dedicated player page that has no competing <audio> element. If you've modified the frontend and are trying to play a <video> element on the main mixer page, it will fail on iOS — the dedicated page is required.

License

AGPL-3.0

Release files for yt-mixer 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for yt-mixer 0.2.0
File Size Uploaded
yt_mixer-0.2.0.tar.gz 64.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for yt-mixer 0.2.0
File Interpreter ABI Platform
yt_mixer-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 123.9 kB

Release files / yt_mixer-0.2.0.tar.gz

Download URL yt_mixer-0.2.0.tar.gz
Size 64.8 kB
Tags Source
SHA-256 checksum
How to use checksums
f596e9b42844d03acc47cacde8a2e741fced2c8026bd7595d54273b2a7cd0efe
BLAKE2b-256 checksum
How to use checksums
55250a2d3b2b8d805add0af0b6ef9cefb9ee42165fd2ce6a49051260101be3d8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 28, 2026.

Transparency log

Release files / yt_mixer-0.2.0-py3-none-any.whl

Download URL yt_mixer-0.2.0-py3-none-any.whl
Size 59.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
072493df546becec11e86e659da647928602f53c32c87b0e5945e41422f962d9
BLAKE2b-256 checksum
How to use checksums
e63304c0ac9d4aee38759702f7dc24b3e997a886de84b5c5879106081b58d304
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 28, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page