A desktop DJ track analysis, preview, and download tool built with PyQt6.
Analyze BPM, key, and energy across your library β then preview, loop, cue, find similar tracks,
organize playlists, and auto-download new music from YouTube, Apple Music, Spotify, Shazam, and SoulSeek.
- BPM detection via onset-based autocorrelation on a mel spectrogram β analyzes first 60s, pure NumPy/SciPy (no librosa/numba overhead)
- Musical key detection using chroma-based KrumhanslβSchmuckler profiles across all 24 major/minor keys, returned in standard, Camelot wheel, and Open Key notation
- Energy level scoring (1β10) from full-track RMS via 65 536-sample chunked reads β constant memory usage regardless of file size
- Genre detection using Essentia's Discogs-EffNet model (400 Discogs genre/style labels) via ONNX Runtime β no TensorFlow required, works on Windows. Top genres appear in the library table and cache per-track
- Audio metadata extraction (format, bitrate, sample rate, duration, file size)
- Batch analysis with background thread pool and JSON cache (skips already-analyzed tracks)
- Find the 25 most similar tracks in your library to any loaded track
- 32-dimensional feature vectors: 20 MFCC coefficients (timbral texture, spectral envelope) + 12 chroma means (pitch-class energy across the 12 semitones)
- Cosine similarity β measures the angle between feature vectors, not their magnitude, so loudness differences between tracks don't affect the score. Displayed as 0β100% match
- Results in a dedicated Similar tab with BPM, key, and score; double-click to load
- Frequency-colored filled waveform: red = bass (0β200 Hz), amber = mids (200β4000 Hz), cyan = highs (4000+ Hz)
- Overview panel (40px) β full-track minimap, click anywhere to seek
- Main panel (120px) β auto-zooms to a Β±15s window around the playhead for beat-level precision
- Beat and bar grid overlay synced to detected BPM
- Hot cue tick marks and loop region overlay rendered on both panels
- Click or drag-to-seek (seeks on mouse release, no audio artifacts)
- 6 color-coded hot cues with persistence across sessions (keys
1β6,Shift+1β6to clear) - Seamless AβB looping β loop region decoded into RAM and played via
pygame.Sound(loops=-1), eliminating the pop/gap found in timer-based approaches - Bar-snap loop presets: Β½, 1, 2, 4, 8 bars from nearest beat
- Loop in/out controls (
I/Okeys) with live overlay on both waveform panels - Volume slider, play/pause, and keyboard-driven seeking
- Load individual tracks or entire folders (recursive scan); multi-file select supported
- Sortable columns: track name, BPM, key (Camelot order), energy level
- Low-bitrate flag on tracks below 320 kbps
- Search/filter across library
- Create, delete, and export playlists (copy tracks to folder)
- Multi-select drag-and-drop from library to playlist
- Multi-delete from playlist; sort playlist by BPM or key
- Paste any YouTube video or playlist URL into the Queue tab
- Downloads as MP3 320 kbps (requires ffmpeg β see Prerequisites). Falls back to native M4A if ffmpeg is missing.
- Parallel downloads β several tracks download at once (default 3, adjustable 1-8 in the Queue tab). Most of the per-track time is metadata lookup and MP3 encoding rather than the transfer itself, so this measured ~1.8x faster at 2 workers and ~2.3x at 4 on a 5-track batch. Past 4 the gain flattens while YouTube rate-limiting risk rises, so 3-4 is the sweet spot.
- Per-row progress indicator with format badge; Import button appears on completion
- βΉ Stop cancels all active and pending queue items at once
- Per-row β button and β Remove Selected to clear finished or unwanted entries
- Bulk Import Selected adds finished tracks to the library and auto-triggers analysis
Subscribe to YouTube, Apple Music, or Spotify playlists, then sync when you want new tracks. Syncing on every launch is available but off by default (see Sync on startup below).
| Source | How it works |
|---|---|
| YouTube Playlist | yt-dlp extract_flat fetches the playlist index; new video IDs are downloaded directly |
| Apple Music URL | Paste any public music.apple.com playlist URL β TrackFlow extracts the anonymous JWT developer token from Apple's web-player JS bundle and calls the Apple Music catalog API to list tracks |
| iTunes XML | Parses the iTunes Music Library.xml written by Apple Music for Windows (uses Python stdlib plistlib) β works for private/personal playlists not accessible via URL |
| Spotify | Paste any public open.spotify.com/playlist/... URL β TrackFlow scrapes embedded track metadata from the playlist page (no API key needed) |
| Shazam | Shazam syncs to Apple Music β enable the Apple Music integration and add the "Shazam Library" playlist via iTunes XML |
For Apple Music, Spotify, and Shazam tracks, TrackFlow searches YouTube using three query variants (Artist - Title, Title Artist, Artist Title official audio) and downloads the best match. Tracks not found on YouTube are shown in a Not Found table for manual retry.
Age-restricted videos: set πͺ Cookies in the Queue tab β pick your browser or a cookies.txt file β and TrackFlow uses your logged-in YouTube session to download them. The choice is verified as soon as you make it, so an unusable source is reported immediately. Chrome-family browsers lock their cookie database while running (close the browser completely) and recent versions encrypt it in ways yt-dlp cannot always read; a cookies.txt file exported with a browser extension always works. If cookies can't be read, downloads still proceed without them β only age-restricted videos fail. Errored rows can be re-queued with β» Retry Errors.
"Sign in to confirm you're not a bot": YouTube applies this to your whole connection once request volume looks automated, so it affects every download, not one track. TrackFlow detects it and pauses the queue rather than failing everything and prolonging the block. Set πͺ Cookies to a cookies.txt file, give it time to clear, then click β» Retry Errors. Lowering Parallel downloads reduces the chance of triggering it.
HTTP 403 errors: usually caused by a stale yt-dlp. Update with
pip install -U yt-dlp(source) or grab the latest TrackFlow release β every build ships a fresh yt-dlp.
Sync state is persisted in data/sync_state.json. A track is recorded as synced only once its file actually lands on disk, so an interrupted playlist resumes where it stopped β the next sync fetches just the tracks that never downloaded, rather than starting the playlist over. If a downloaded file is later deleted, that track is fetched again too. Use π Clear Cache next to any subscription to reset its sync history (useful when testing or re-downloading a playlist).
- Point TrackFlow at your SoulSeek "completed downloads" folder
watchdogmonitors the folder in real time (catches both new file creation and SoulSeek's.tmpβ final filename rename-on-completion)- Each detected audio file appears in the watcher table with an Import button
| Key | Action |
|---|---|
Space |
Play / Pause |
β / β |
Seek Β±5 seconds |
Shift+β / β |
Seek Β±30 seconds |
I |
Set loop in-point (A) |
O |
Set loop out-point (B) |
L |
Toggle loop on / off |
1β6 |
Jump to hot cue (sets if empty) |
Shift+1β6 |
Clear hot cue |
Enter |
Play selected track (library or playlist) |
Delete |
Remove selected track(s) from playlist |
F1 / ? |
Open help |
- Grab the latest
TrackFlow.zipfrom the Releases page - Extract it somewhere (e.g.
C:\TrackFlow\) - Install ffmpeg (required β see below)
- Run
TrackFlow.exefrom the extracted folder
Distribute/keep the entire extracted folder, not just the
.exeβ the bundled DLLs and assets sit alongside it.
TrackFlow needs ffmpeg to produce MP3 320 kbps files. It's auto-detected from your PATH and common install locations.
- Windows: Download from gyan.dev β extract β add
bin/folder to PATH, or usewinget install ffmpeg/choco install ffmpeg - macOS:
brew install ffmpeg - Linux:
sudo apt install ffmpeg(Debian/Ubuntu) or equivalent
β οΈ Without ffmpeg, downloads fall back to native M4A β works but has lower compatibility with some DJ software.
- Python 3.11+
- Conda (recommended) or pip
- ffmpeg (see above)
# Clone the repository
git clone https://github.com/ashaydave/TrackFlow.git
cd TrackFlow
# Create conda environment
conda create -n dj-analyzer python=3.11 -y
conda activate dj-analyzer
# Install dependencies
pip install PyQt6 numpy scipy soundfile soxr mutagen pygame yt-dlp watchdog onnxruntimeGenre detection requires
onnxruntime(included above). The Discogs-EffNet model (~37 MB) downloads automatically on first use. Ifonnxruntimeis unavailable the rest of the app works normally β the Genre column simply stays empty.
python main.pybuild.bat
# Output: dist\TrackFlow\TrackFlow.exeRequires PyInstaller (pip install pyinstaller) and the conda environment above. See build.bat for details.
- Click the β¬ Downloads tab at the top of the window
- Set your Save to folder (your DJ library folder, or a staging area)
- Paste a YouTube URL (video or playlist) into the URL bar and click + Add
- Click βΆ Download All β progress updates per row; the format badge shows
MP3orM4A - When a row shows β Done, click β¬ Import to add it to the library and trigger analysis
- Use βΉ Stop to cancel all downloads at once, or β on individual rows to remove them
- Go to β¬ Downloads β Subscriptions
- Pick a source in the dropdown (YouTube / Spotify / Apple Music), paste the playlist URL, and click + Add β the source is auto-detected from the URL, so pasting any supported link just works
- iTunes XML / Shazam: choose iTunes XML / Shazam in the dropdown and click + Add to pick a playlist by name from your local
iTunes Music Library.xml(e.g.Shazam Library) - Every playlist row in the table has its own controls:
- π Folder β give this playlist its own download folder ("Default" uses the Queue tab's Save-to folder)
- π Sync β sync just this playlist
- Reset β clear just this playlist's sync history, so its tracks re-queue on the next sync (other playlists untouched, no files deleted)
- Remove β unsubscribe from this playlist (downloaded files are kept)
- Click π Sync All to check every playlist. Automatic syncing on launch is off by default β tick Sync on startup next to Sync All to enable it. It is opt-in because a sync can queue hundreds of downloads, and a large burst of requests risks YouTube rate-limiting
- π Clear Cache (next to Sync All) resets the sync history for every playlist at once β it asks for confirmation and reports how many tracks would re-queue, since that can mean a very large batch of downloads. To reset one playlist, use Reset on its row instead.
- New tracks appear in the Queue tab; click Import Selected to bring them into the library
- Go to β¬ Downloads β SoulSeek Watcher
- Click Browse and navigate to your SoulSeek "Finished Downloads" folder
- Click βΆ Start Watching β the status indicator turns green
- As SoulSeek completes downloads, files appear in the table automatically
- Click β¬ Import on any row (or Import All New) to add files to the library
TrackFlow/
βββ main.py # Application entry point
βββ paths.py # Path resolution (dev + frozen exe)
βββ build.bat # One-click Windows build script
βββ TrackFlow.spec # PyInstaller spec (includes all Phase 2 modules)
βββ analyzer/
β βββ audio_analyzer.py # BPM / key / energy / MFCC / chroma engine
β βββ batch_analyzer.py # Background batch analysis + JSON cache
β βββ genre_detector.py # Discogs-EffNet ONNX genre detection (400 styles)
β βββ similarity.py # 32-dim cosine similarity search
βββ downloader/
β βββ yt_handler.py # DownloadWorker(QThread) β yt-dlp wrapper,
β β # ffmpeg auto-detection, MP3/M4A format selection
β βββ watcher.py # FolderWatcher(QObject) β watchdog wrapper
β βββ playlist_sync.py # YouTubePlaylistSource, AppleMusicSource,
β # AppleMusicURLSource, SpotifyPlaylistSource,
β # PlaylistSyncWorker, search_youtube()
βββ ui/
β βββ main_window.py # Main window, Library tab, all DJ controls
β βββ downloads_tab.py # Downloads tab (Queue / Subscriptions / SoulSeek)
β βββ waveform_dj.py # Frequency-colored waveform widget
β βββ audio_player.py # Pygame player (STOPPED/PLAYING/PAUSED/LOOP_PLAYING)
β βββ styles.py # Centralized cyberpunk QSS stylesheet
βββ assets/
β βββ logo.svg # Application logo (vector)
β βββ logo_32.png # Toolbar icon
β βββ logo_256.png # Window / tray icon
βββ data/
β βββ cache/ # Analysis cache (JSON, keyed by path+mtime+size)
β βββ hot_cues.json # Saved hot cue positions per track
β βββ playlists.json # Saved playlists
β βββ sync_state.json # Last-known playlist state (prevents re-downloads)
β βββ downloads_config.json # Output folder, watch dir, subscriptions
βββ tests/
βββ test_analyzer_speed.py
βββ test_batch_analyzer.py
βββ test_similarity.py
βββ test_genre.py # 17 tests β genre cache, model manifest, label cleaning,
β # format_genres edge cases, onnxruntime availability
βββ test_downloads.py # FolderWatcher, AppleMusicSource, AppleMusicURLSource,
# sync state, yt_handler imports
This project was built entirely with Claude by Anthropic β from the analysis engine and UI layout to the waveform rendering, seamless looping, similarity search, and the full playlist-sync and download pipeline.
This project is open source and available under the MIT License.