Skip to main content

Server-side YouTube Audio Mixer with ad-hoc video injection, LUFS normalization, and podcast-style ducking

Project description

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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

yt_mixer-0.2.0.tar.gz (64.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

yt_mixer-0.2.0-py3-none-any.whl (59.1 kB view details)

Uploaded Python 3

File details

Details for the file yt_mixer-0.2.0.tar.gz.

File metadata

  • Download URL: yt_mixer-0.2.0.tar.gz
  • Upload date:
  • Size: 64.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for yt_mixer-0.2.0.tar.gz
Algorithm Hash digest
SHA256 f596e9b42844d03acc47cacde8a2e741fced2c8026bd7595d54273b2a7cd0efe
MD5 1249c65c972c5c0de2419c626a48a3a8
BLAKE2b-256 55250a2d3b2b8d805add0af0b6ef9cefb9ee42165fd2ce6a49051260101be3d8

See more details on using hashes here.

Provenance

The following attestation bundles were made for yt_mixer-0.2.0.tar.gz:

Publisher: publish.yml on 1minds3t/yt-mixer

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file yt_mixer-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: yt_mixer-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 59.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for yt_mixer-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 072493df546becec11e86e659da647928602f53c32c87b0e5945e41422f962d9
MD5 9edb4ac086c903430aec30fd511af6ac
BLAKE2b-256 e63304c0ac9d4aee38759702f7dc24b3e997a886de84b5c5879106081b58d304

See more details on using hashes here.

Provenance

The following attestation bundles were made for yt_mixer-0.2.0-py3-none-any.whl:

Publisher: publish.yml on 1minds3t/yt-mixer

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page