- π΅ Downloads audio from YouTube videos - High-quality MP3 conversion
- π 104 music genres - Pre-configured genre search (lofi, jazz, rock, classical, etc.)
- π Smart search - Built-in YouTube search with metadata extraction
- β±οΈ Duration filtering - Avoid long livestreams with min/max duration filters
- πΎ Per-genre caching - Separate DETS cache files prevent duplicate downloads
- π¨ Two UX modes - CLI mode (fast/scriptable) + Interactive mode (user-friendly)
- π§Ή Auto-cleanup - Removes intermediate files, sanitizes filenames
- π Cache management - View stats, clear per-genre, track what you've downloaded
uv (Python package manager):
curl -LsSf https://astral.sh/uv/install.sh | shyt-dlp:
~/.local/bin/uv tool install yt-dlp --force
yt-dlp --version # Verifyffmpeg:
# Ubuntu/Debian
sudo apt-get update && sudo apt-get install -y ffmpeg
# macOS
brew install ffmpeg
# Verify
ffmpeg -version# Ubuntu/Debian
sudo apt-get install -y build-essential g++ cmake libasound2-dev
# Verify
g++ --version# Create Python virtual environment
cd youtube_audio_dl
python3 -m venv ml_env
# Activate and install packages
ml_env/bin/pip install --upgrade pip
ml_env/bin/pip install cython numpy
ml_env/bin/pip install madmom scipy mido
# Verify installation
ml_env/bin/python -c "import madmom; print('Madmom installed successfully')"What gets installed:
- Madmom: Neural network-based onset detection (pre-trained models)
- NumPy: Numerical computing
- SciPy: Scientific computing
- Cython: C-extensions for Python (required by Madmom)
- MIDO: MIDI file handling
Note: No GPU required! CPU-only processing works great (~1-2 seconds for 15 seconds of audio).
cd youtube_audio_dl
mix deps.get
mix compile./ytdl-interactiveFeatures visual menus, step-by-step prompts, and built-in help.
# Browse genres
./ytdl genres
# Browse lofi music (with duration filter!)
./ytdl browse lofi 5 --max-duration 600
# Download single track
./ytdl download jazz --index 3
# Download multiple tracks (filter recommended!)
./ytdl download lofi --count 5 --max-duration 900
# Search custom query
./ytdl search "epic orchestral music" 10 --max-duration 1800
# Download from URL
./ytdl get "https://www.youtube.com/watch?v=VIDEO_ID"
# Check cache
./ytdl cache
# Clear cache for genre
./ytdl clear lofi--index N Download Nth search result (default: 1)
--count N Download N tracks
--limit N Number of search results (default: 5)
--min-duration N Minimum duration in seconds
--max-duration N Maximum duration in seconds
--force Skip cache check, re-download
--output DIR Output directory (default: downloads)
Many search results are long livestreams (6+ hours). Use duration filters to avoid timeouts:
# Only short tracks (under 10 minutes)
./ytdl download lofi --count 5 --max-duration 600
# Only 3-15 minute videos
./ytdl download jazz --count 5 --min-duration 180 --max-duration 900
# Browse with filter
./ytdl browse classical 10 --max-duration 1800Duration reference:
180= 3 minutes300= 5 minutes600= 10 minutes β Recommended max for most genres900= 15 minutes1800= 30 minutes
# Start interactive shell
iex -S mix
# Browse genres
YoutubeAudioDl.Music.print_genres()
# Search with duration filter
{:ok, tracks} = YoutubeAudioDl.Search.search("lofi beats",
min_duration: 180,
max_duration: 600,
limit: 10
)
# Download from genre
YoutubeAudioDl.Music.download(:lofi)
YoutubeAudioDl.Music.download(:jazz, index: 3)
YoutubeAudioDl.Music.download_multiple(:classical, 5)
# Download from URL
YoutubeAudioDl.download_audio("https://www.youtube.com/watch?v=VIDEO_ID")
# With options
YoutubeAudioDl.download_audio(url,
category: "lofi",
output_dir: "./music",
force: true
)
# Cache management
YoutubeAudioDl.Cache.stats("lofi")
YoutubeAudioDl.Cache.all_stats()
YoutubeAudioDl.Cache.list_categories()- Electronic:
:lofi,:edm,:techno,:house,:synthwave,:ambient,:chillwave - Jazz:
:jazz,:smooth_jazz,:bebop,:cool_jazz,:jazz_fusion - Rock:
:rock,:classic_rock,:indie_rock,:punk_rock,:metal - Classical:
:classical,:baroque,:piano,:violin,:orchestra - Hip Hop:
:hip_hop,:rap,:trap,:boom_bap,:lofi - Relaxation:
:meditation,:sleep,:study,:spa,:nature_sounds
See all 104 genres: ./ytdl genres or YoutubeAudioDl.Music.print_genres()
- Search - Uses yt-dlp's built-in YouTube search (
ytsearch:query) - Filter - Optional duration filtering (min/max seconds)
- Cache Check - Checks genre-specific DETS cache to prevent duplicates
- Download - Downloads best audio stream via yt-dlp
- Convert - Converts to MP3 using ffmpeg (48kHz, best quality)
- Sanitize - Cleans filename (removes spaces, special chars)
- Cache - Marks URL as downloaded in genre cache
- Cleanup - Removes intermediate files (.part, .webm, etc.)
Each genre gets its own cache file for organization and performance:
downloads/
βββ .cache/
β βββ lofi # DETS cache for lofi downloads
β βββ jazz # DETS cache for jazz downloads
β βββ classical # etc...
β βββ general # Default category
βββ *.mp3 # Your music files
# View cache statistics
./ytdl cache
# Clear specific genre cache
./ytdl clear lofi
# Clear all caches
./ytdl clear all# Check if URL is cached
YoutubeAudioDl.Cache.downloaded?("https://youtube.com/watch?v=...", "lofi")
# Mark as downloaded
YoutubeAudioDl.Cache.mark_downloaded(url, "jazz")
# Get stats
YoutubeAudioDl.Cache.stats("lofi")
YoutubeAudioDl.Cache.all_stats()
# List all categories
YoutubeAudioDl.Cache.list_categories()
# Clear cache
YoutubeAudioDl.Cache.clear_all("lofi")youtube_audio_dl/
βββ ytdl # Main CLI script
βββ ytdl-interactive # Interactive mode script
βββ assets/ # Brand assets
β βββ youtube_audio_dl.svg # Project logo
βββ downloads/ # Downloaded MP3s (gitignored)
β βββ .cache/ # DETS cache files
β βββ *.mp3 # Your music
βββ ml_env/ # Python virtual environment (gitignored)
β βββ bin/ # Python executables
β βββ lib/ # Installed packages (madmom, numpy, scipy)
β βββ ...
βββ simple_filter/ # C++ DSP pipeline
β βββ dsp_pipeline.h # Core DSP classes (filters, FFT, normalizers)
β βββ dsp # CLI helper script (./dsp hp 2k)
β βββ filter # 4-pole Butterworth filter tool
β βββ normalize # Peak/RMS normalization tool
β βββ pipeline_demo # 13-stage filter demonstration
β βββ onset_detector # Spectral onset detection (FFT-based)
β βββ README_DSP.md # DSP pipeline documentation
β βββ NORMALIZATION_GUIDE.md # Normalization theory & usage
β βββ TEST_RESULTS.md # Performance benchmarks
βββ simple_filter_demos/ # Generated filter outputs (gitignored)
β βββ 01_original.wav # Reference (no processing)
β βββ 04_bandpass_kick.wav # Isolated kick drum (60-200Hz)
β βββ 05_bandpass_snare.wav # Isolated snare (800-3000Hz)
β βββ 07_lowpass_800hz.wav # Lofi effect
β βββ 13_combined_clean.wav # Broadcast quality
β βββ ... (13 total stages)
βββ scripts/ # Demo & utility scripts
β βββ find_chops.exs # Two-stage threshold chop detector (Elixir)
β βββ find_hihat_chops.exs # Hi-hat specific chop detector
β βββ find_kick_chops.exs # Kick drum specific chop detector
β βββ chop_ml.py # ML-based chop detector (Madmom RNN)
β βββ demo_parameters.exs # Parameter examples for chop detection
βββ lib/
β βββ youtube_audio_dl.ex # Main module
β βββ youtube_audio_dl/
β βββ search.ex # YouTube search + metadata
β βββ music.ex # Genre presets (104 genres)
β βββ cache.ex # DETS cache management
β βββ audio/ # Audio processing
β βββ transient_detector.ex # Beat detection
β βββ waveform_slicer.ex # Audio slicing
βββ docs/ # Documentation
β βββ QUICK_START.md # Getting started guide
β βββ TROUBLESHOOTING.md # Common issues & solutions
β βββ CACHE_GUIDE.md # Cache management guide
β βββ MUSIC_EXAMPLES.md # API usage examples
β βββ TWO_STAGE_CHOP_GUIDE.md # Chop detection documentation
β βββ TRANSIENT_DETECTION_TECHNOLOGIES.md # Audio analysis tech overview
βββ test/ # Elixir tests
βββ mix.exs
βββ mix.lock
βββ .formatter.exs
βββ .gitignore
βββ README.md # This file (you are here)
# Check what's available and their durations
./ytdl browse lofi 10 --max-duration 600
# Pick a good short one (e.g., #3)
./ytdl download lofi --index 3# Download curated collection with filters
./ytdl download lofi --count 5 --max-duration 600
./ytdl download jazz --count 5 --max-duration 900
./ytdl download classical --count 3 --max-duration 1800
./ytdl download ambient --count 5 --max-duration 600
# Check what you've downloaded
./ytdl cache# Download 5 lofi tracks (3-10 min each)
{:ok, tracks} = YoutubeAudioDl.Search.search("lofi beats",
min_duration: 180,
max_duration: 600,
limit: 5
)
Enum.each(tracks, fn track ->
YoutubeAudioDl.download_audio(track.url, category: "lofi_short")
end)# Download multiple genres, short tracks only
genres = [:lofi, :jazz, :classical, :ambient]
Enum.each(genres, fn genre ->
IO.puts("\nDownloading from: #{genre}")
# Search with duration filter
{:ok, videos} = YoutubeAudioDl.Music.search(genre,
limit: 3,
max_duration: 600
)
# Download filtered results
Enum.each(videos, fn v ->
YoutubeAudioDl.download_audio(v.url, category: to_string(genre))
end)
end)Professional-grade audio processing tools with composable filter chains:
Filter Tool - Apply IIR filters with automatic normalization
cd simple_filter
# Lowpass filter at 800Hz
./dsp lp 800
# Highpass filter at 2kHz
./dsp hp 2k
# Bandpass filter (isolate frequency range)
./dsp bp 800 3000
# Custom input/output
./dsp hp 4000 my_drums.wav filtered.wavNormalizer - Peak and RMS loudness normalization
# Peak normalize to -0.1 dB (maximizes volume)
./normalize input.wav
# RMS normalize to -14 dB (streaming standard)
./normalize input.wav --rms -14
# Custom target levels
./normalize input.wav output.wav --peak -3
./normalize vocals.wav --rms -18Pipeline Demo - Process entire file through 13 filter stages
# Creates 13 variations with different filters
./pipeline_demo drums.wav
# Outputs to simple_filter_demos/:
# - Original, HPF/LPF variations
# - Band-isolated (kick/snare/hihat)
# - Creative effects (lofi, telephone, etc.)IIR Filters (24dB/octave Butterworth)
- Lowpass - Remove high frequencies (dark/warm sound)
- Highpass - Remove bass (thin/bright sound)
- Bandpass - Isolate frequency range (extract instruments)
Normalization
- Peak normalization - Maximize volume without clipping
- RMS normalization - Match perceived loudness
- Automatic clipping protection
- Applied to all filter outputs
Composable Architecture
DSP::ProcessingChain chain;
chain.add(std::make_unique<DSP::Butterworth4PoleHighpass>(40, sr))
.add(std::make_unique<DSP::Butterworth4PoleLowpass>(12000, sr))
.add(std::make_unique<DSP::PeakNormalizer>(-0.1f));Music Production
- Clean up recordings (highpass @ 40Hz removes rumble)
- Create lofi effects (lowpass @ 800Hz)
- Isolate instruments (bandpass kick: 60-200Hz, snare: 800-3kHz)
Sound Design
- Telephone effect (lowpass @ 1200Hz)
- Underwater sound (lowpass @ 500Hz)
- Radio effect (bandpass @ 800-3000Hz)
Mastering
- Peak normalize to -0.1 dB for maximum loudness
- RMS normalize to -14 dB for streaming platforms
- Broadcast quality filtering (HPF 40Hz + LPF 12kHz)
Filters accessible from Windows Explorer:
\\wsl.localhost\Ubuntu\home\home\p\g\n\elixir_tube\youtube_audio_dl\simple_filter_demos
See detailed docs in simple_filter/:
- README_DSP.md - Complete DSP pipeline guide
- NORMALIZATION_GUIDE.md - Peak/RMS normalization explained
- TEST_RESULTS.md - Filter performance benchmarks
- LISTEN_GUIDE.txt - How to hear filter effects
Performance
- Processing: ~5ms for 15 seconds @ 48kHz
- Normalization overhead: Negligible
- Real-time capable for streaming
Quality
- 4-pole Butterworth (maximally flat response)
- 24 dB/octave rolloff (very steep)
- 32-bit float processing
- 16-bit PCM output
Dependencies
- C++17 standard library only
- No external DSP libraries required
- Compiles with g++ on Linux/WSL
- Format: Best available audio stream (bestaudio/best)
- Output: MP3
- Sample Rate: 48kHz
- Bitrate: 128-256kbps (depends on source)
- Quality: yt-dlp quality 0 (best)
- Processing: Optional DSP filters and normalization (see above)
- Converts to lowercase
- Removes apostrophes
- Replaces special chars with underscores
- Collapses multiple underscores
- Examples:
"Intelligence Isn't What You Think"βintelligence_isnt_what_you_think.mp3"Best of Lofi 2024 [Chill Mix]"βbest_of_lofi_2024_chill_mix.mp3
- exyt_dlp (~> 0.1.6) - Elixir wrapper for yt-dlp
- jason (~> 1.4) - JSON parsing
- yt-dlp - YouTube downloader (system)
- ffmpeg - Audio conversion (system)
"Broken pipe" error:
- You're trying to download a very long video (6+ hours livestream)
- Solution: Use
--max-duration 600to filter for shorter tracks
Downloads taking forever:
- Likely downloading a long video
- Solution: Browse first, check durations, use filters
No results found:
- Duration filter too strict
- Solution: Increase max-duration or remove min-duration
See docs/TROUBLESHOOTING.md for detailed solutions.
- docs/QUICK_START.md - Getting started guide with safe examples
- docs/TROUBLESHOOTING.md - Common issues and solutions
- docs/CACHE_GUIDE.md - Cache system explained
- docs/MUSIC_EXAMPLES.md - API usage examples
- docs/TWO_STAGE_CHOP_GUIDE.md - Transient/chop detection guide
- docs/TRANSIENT_DETECTION_TECHNOLOGIES.md - Audio analysis technologies
./ytdl genres # List all genres
./ytdl browse <genre> [limit] [opts] # Browse genre
./ytdl download <genre> [opts] # Download by genre
./ytdl get <url> [opts] # Download from URL
./ytdl search <query> [limit] [opts] # Search YouTube
./ytdl cache # View cache stats
./ytdl clear [category] # Clear cache
./ytdl help # Show help
./ytdl-interactive # Interactive mode# Search & Discovery
YoutubeAudioDl.Search.search(query, opts)
YoutubeAudioDl.Search.search_urls(query, opts)
YoutubeAudioDl.Music.search(genre, opts)
YoutubeAudioDl.Music.browse(genre, opts)
# Downloading
YoutubeAudioDl.download_audio(url, opts)
YoutubeAudioDl.Music.download(genre, opts)
YoutubeAudioDl.Music.download_multiple(genre, count, opts)
# Cache Management
YoutubeAudioDl.Cache.downloaded?(url, category)
YoutubeAudioDl.Cache.mark_downloaded(url, category)
YoutubeAudioDl.Cache.stats(category)
YoutubeAudioDl.Cache.all_stats()
YoutubeAudioDl.Cache.clear_all(category)
# Utilities
YoutubeAudioDl.Music.print_genres()
YoutubeAudioDl.Music.list_genres()
YoutubeAudioDl.Search.format_duration(seconds)
YoutubeAudioDl.Search.format_views(count)β DO:
- Use
--max-duration 600for most downloads - Browse before downloading (
./ytdl browse lofi 10) - Start with small batches (
--count 3) - Check cache regularly (
./ytdl cache) - Use interactive mode if unsure
β AVOID:
- Downloading without duration filters
- Requesting 20+ tracks at once (first time)
- Downloading videos with "N/A" duration (livestreams)
- Ignoring the "Broken pipe" warnings
This is a personal project, but feel free to fork and modify!
Open source. Use freely.
./ytdl download lofi --count 5 --max-duration 600
./ytdl get "https://youtube.com/watch?v=..."cd simple_filter
./dsp lp 800 # Lowpass (dark/lofi)
./dsp hp 2k # Highpass (remove bass)
./dsp bp 800 3000 # Bandpass (isolate mids)
./normalize input.wav # Peak normalize
./pipeline_demo file.wav # 13 filter demos./ytdl cache # View stats
./ytdl clear lofi # Clear genre cache- Lofi effect:
./dsp lp 800 - Remove rumble:
./dsp hp 40 - Telephone:
./dsp lp 1200 - Kick only:
./dsp bp 60 200 - Snare only:
./dsp bp 800 3000 - Hi-hat only:
./dsp bp 5k 15k
- Built with Elixir
- Uses yt-dlp for downloading
- Uses ffmpeg for audio conversion
- Powered by exyt_dlp wrapper
- DSP algorithms: Butterworth filters, FFT onset detection, peak/RMS normalization
π΅ Happy downloading! Remember to use --max-duration filters to avoid livestreams!
ποΈ Pro tip: All DSP filters automatically normalize output for consistent volume!