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-allflag used)
2. Intelligent Search with 3-Tier Fallback
Progressively broader queries until a match is found:
- "{show} series theme song" - TV-specific, avoids movies
- "{show} opening theme" - Broader match
- "{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.mp3in 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=Truein 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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file themefetch-1.0.0.tar.gz.
File metadata
- Download URL: themefetch-1.0.0.tar.gz
- Upload date:
- Size: 18.9 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":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
183c1b5112fb4b931a1ae12f9d36fdf8834b53389dbffe96c2748c3ddf2af694
|
|
| MD5 |
1ed70c7d5d591be647509f5f2d7dd58c
|
|
| BLAKE2b-256 |
3322440bbbf897e1f98caebdbf149228e37df20edad185e039dd9798b07057d9
|
File details
Details for the file themefetch-1.0.0-py3-none-any.whl.
File metadata
- Download URL: themefetch-1.0.0-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":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1fba2ec19491f229c1beea7b69f0cfeeaa8086d1bb21284d407ec94a2de69170
|
|
| MD5 |
28fe73f5973470c59593909ba9c4aea3
|
|
| BLAKE2b-256 |
6731f7335650675db2fca18656405a221586b02ad3d93dff55f4f08721f11f50
|