Skip to main content

Download TV show theme songs from YouTube for Emby/Jellyfin

Project description

themefetch

Automatically download TV show theme songs from YouTube and save them as theme.mp3 files for Emby/Jellyfin media servers.

Requirements

  • Python 3.10 or higher
  • ffmpeg (system package, required for audio conversion)

Installing ffmpeg

macOS:

brew install ffmpeg

Ubuntu/Debian:

sudo apt install ffmpeg

Windows: Download from ffmpeg.org

Installation

Quick Start (Recommended)

Run themefetch without installation using uvx:

uvx themefetch batch /path/to/tv/library

Install as Tool

Install themefetch as a persistent command:

uv tool install themefetch
themefetch batch /path/to/tv/library

Traditional pip install

pip install themefetch
themefetch batch /path/to/tv/library

Commands

themefetch provides four commands for different workflows:

Command Purpose When to Use
batch Process entire library Most common - download themes for all shows
list Fast scan without searches Preview which shows need themes
search Search and download one show Download theme for specific show by name
download Download from YouTube URL Use a specific YouTube video as theme

Usage

Batch Processing (Most Common)

Scan your entire TV library and download theme songs for all shows:

# Process entire library
themefetch batch /path/to/tv/library

# Dry run (preview what would be downloaded)
themefetch batch --dry-run /path/to/tv/library

# Parallel downloads with 6 workers (default: 4)
themefetch batch --workers 6 /path/to/tv/library

# Show verbose logging
themefetch batch --verbose /path/to/tv/library

List Shows Needing Themes

See which shows don't have theme songs yet (fast, no YouTube searches):

themefetch list /path/to/tv/library

Search for Single Show

Search YouTube and download theme for a specific show by name:

themefetch search "Breaking Bad" 2008 "/path/to/tv/library/Breaking Bad (2008)"

# Dry run to preview before downloading
themefetch search "The Wire" 2002 "/path/to/tv/library/The Wire (2002)" --dry-run

Download from Specific URL

Download theme from a specific YouTube URL:

themefetch download "https://www.youtube.com/watch?v=VIDEO_ID" "/path/to/tv/library/Show Name (Year)"

Features

  • Intelligent 3-tier search: Progressive query fallback (series theme → opening theme → intro)
  • Smart filtering: Blocks trailers, covers, reactions using keyword patterns
  • Accuracy over completeness: Skips shows rather than downloading incorrect themes
  • Batch processing: Scan and process entire TV libraries automatically
  • Parallel downloads: Configurable workers for faster processing (default: 4)
  • Dry-run mode: Preview actions with full metadata before downloading
  • Skip existing: Automatically skips shows that already have theme.mp3
  • Cross-platform: Works on Windows, macOS, and Linux

Library Format

Your TV shows must be organized in "Name (Year)" folder format:

/path/to/tv/library/
├── Breaking Bad (2008)/
├── The Wire (2002)/
├── Friends (1994)/
└── ...

Theme songs are saved as theme.mp3 in each show folder.

How It Works

themefetch uses a multi-stage filtering pipeline to ensure accurate theme downloads:

1. Library Scanning

  • Scans library for shows in "Name (Year)" folder format
  • Skips shows with existing theme.mp3 (unless --replace-all flag used)

2. Intelligent Search with 3-Tier Fallback

Progressively broader queries until a match is found:

  1. "{show} series theme song" - TV-specific, avoids movies
  2. "{show} opening theme" - Broader match
  3. "{show} intro" - Broadest match

3. Smart Filtering

Results are filtered through multiple criteria:

Duration filtering:

  • Must be 20-180 seconds (typical theme song length)
  • Filters out full episodes and short clips

View count threshold:

  • Minimum 5,000 views to filter spam/low-quality uploads

Keyword matching:

  • Must contain: theme, opening, intro, soundtrack, or ost
  • Uses fuzzy matching (70% threshold) to handle typos

Negative keyword filtering (word boundaries):

  • Blocks trailers: trailer, teaser, preview, promo
  • Blocks covers/remixes: cover, remix, acoustic, piano version, orchestral
  • Blocks reactions/analysis: reaction, reacting, breakdown, analysis, review

4. Ranking and Selection

  • Primary: View count (higher is better)
  • Bonus: Official channels (+1M views equivalent for official/vevo/records/music)
  • Tiebreaker: Newer upload date

5. Download and Conversion

  • Downloads best audio quality available
  • Converts to MP3 using ffmpeg (default: 128kbps)
  • Saves as theme.mp3 in show folder
  • Retries: 3 attempts with exponential backoff (2s, 4s, 8s)

Advanced Options

Global flags (all commands):

  • --verbose - Show detailed logging and filter statistics
  • --quality N - Audio bitrate in kbps (default: 128)

Batch-specific flags:

  • --workers N - Number of parallel downloads (default: 4)
  • --dry-run - Preview actions with full metadata before downloading
  • --replace-all - Re-download existing themes

Search-specific flags:

  • --dry-run - Preview selected video before downloading

Development

themefetch uses modern Python tooling for code quality:

Code Quality Tools

Linting and formatting with ruff:

# Check code style and lint issues
uv run ruff check .

# Auto-fix issues where possible
uv run ruff check --fix .

# Format code
uv run ruff format .

Type checking with mypy:

# Run type checking from project root
uv run mypy .

# Or check the src directory
uv run mypy src/

Run all checks:

# Lint, format, and type check in one command
uv run ruff check . && uv run ruff format . && uv run mypy .

Installing Development Dependencies

Development tools are installed automatically with uv, but you can install them explicitly:

uv pip install -e ".[dev]"

Troubleshooting

"No matching theme found"

The filters may be too strict for some shows. This happens when:

  • All results are filtered out (trailers, covers, wrong duration)
  • Show has low YouTube presence (< 5,000 views)
  • Theme song isn't available on YouTube

Solution: This is working as intended - accuracy over completeness. You can manually download using the download command with a specific URL.

Wrong theme downloaded

themefetch prioritizes accuracy, but mismatches can occur for:

  • Shows with similar names (word boundary matching helps but isn't perfect)
  • Shows where "official" uploads are lower quality than fan uploads

Solution: Delete the incorrect theme.mp3 and use themefetch download <URL> <path> with a specific YouTube URL.

Rate limiting / HTTP 429 errors

themefetch includes built-in rate limiting protection:

  • Random 2-5 second delays between searches
  • 60 second backoff on HTTP 429 errors

Solution: If you still hit rate limits, reduce --workers to 1 or 2 for slower sequential processing.

"ffmpeg not found" errors

ffmpeg must be installed separately (see Requirements section above).

Solution: Install ffmpeg using your system package manager.

Technical Details

YouTube integration:

  • Uses yt-dlp for YouTube searches (no API key required)
  • No quota limits - yt-dlp uses built-in search

Performance:

  • Parallel processing with ThreadPoolExecutor (default 4 workers)
  • Dry-run mode uses parallel search for fast previews

Reliability:

  • 3 retry attempts with exponential backoff (2s, 4s, 8s)
  • Random delays (2-5s) between operations to avoid rate limits
  • 60s backoff on HTTP 429 (Too Many Requests)

Filtering accuracy:

  • Fuzzy keyword matching with RapidFuzz (70% threshold for typos)
  • Word boundary matching for show names (avoids "Trust" matching "In Family We Trust")
  • Negative patterns use regex word boundaries to avoid false positives

Cross-platform:

  • windowsfilenames=True in yt-dlp for safe filenames on all platforms
  • Path handling uses Python's pathlib for OS-independent paths

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

themefetch-1.0.1.tar.gz (18.8 kB view details)

Uploaded Source

Built Distribution

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

themefetch-1.0.1-py3-none-any.whl (17.3 kB view details)

Uploaded Python 3

File details

Details for the file themefetch-1.0.1.tar.gz.

File metadata

  • Download URL: themefetch-1.0.1.tar.gz
  • Upload date:
  • Size: 18.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for themefetch-1.0.1.tar.gz
Algorithm Hash digest
SHA256 5f241fa515b4c5c15df75ef0d90b7552965cdd77308e58dde5302084e37cbdf5
MD5 72f07d86f8e9f0b19ddd7606a784939b
BLAKE2b-256 ab4e8f6c3be8ea354d4a24fe94ad0b79c662e77ce2d865918b28cad48e7461dd

See more details on using hashes here.

File details

Details for the file themefetch-1.0.1-py3-none-any.whl.

File metadata

  • Download URL: themefetch-1.0.1-py3-none-any.whl
  • Upload date:
  • Size: 17.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for themefetch-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 a1a59752d29ebe3a539450d3d985560536d6d28e30b39445c9229574fd5d1723
MD5 815a2cbe29b5975a5665e6a81f4d149e
BLAKE2b-256 8511e2ef06b07bf1ae7c7b4347d8c510fcda4f5faf9b047107708e045bfe2bc4

See more details on using hashes here.

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