Music For Programming Integration
Music For Programming is a series of ambient/electronic mixes curated for deep work. DJ mode integrates this as its primary content source.
Playing Episodes
# Play by episode number
mpv-dj mfp 49 # Episode 49 (Julien Mier)
mpv-dj mfp 51 # Episode 51 (Mücha)
The command:
- Checks for local cached audio in
~/.voicemode/music-for-programming/ - If not cached, looks up the episode URL from the RSS feed via
mfp-rss-helper - Streams from the URL (e.g.,
https://datashat.net/music_for_programming_49-julien_mier.mp3) - Loads chapter metadata for track-level navigation
File Structure
All MFP files live in ~/.voicemode/music-for-programming/:
~/.voicemode/music-for-programming/
├── rss.xml # Cached RSS feed (auto-updated)
├── music_for_programming_49-julien_mier.mp3 # Audio (downloaded or cached)
├── music_for_programming_49-julien_mier.cue # CUE sheet (for local playback)
├── music_for_programming_49-julien_mier.ffmeta # FFMETADATA chapters (for streaming)
├── music_for_programming_51-mücha.mp3
├── music_for_programming_51-mücha.cue
├── music_for_programming_51-mücha.ffmeta
└── ...
Naming Convention: Files are named to match the RSS MP3 filename exactly (minus extension), ensuring automatic pairing of audio with chapter metadata.
File Types:
rss.xml- Cached RSS feed for episode URL lookups (see RSS Caching).mp3- Audio file (downloaded by user or cached during streaming).cue- CUE sheet with track timestamps (for local mpv playback).ffmeta- FFMETADATA chapters (for HTTP streaming where CUE doesn't work)
LLM Access: You can read CUE files to search for tracks across episodes. Example: "Which episode has Boards of Canada?" - search the CUE files for artist names.
Episode URLs
Music For Programming hosts audio at URLs that include the curator name:
https://datashat.net/music_for_programming_49-julien_mier.mp3
The episode number and curator name form the filename. Episodes are freely downloadable from the website.
Episode URLs are obtained dynamically from the MFP RSS feed using the mfp-rss-helper tool, which handles the lookup and caching automatically.
RSS Caching
The mfp-rss-helper script fetches episode URLs from the official MFP RSS feed with smart caching for offline support.
How It Works
- First run: Fetches RSS feed from
musicforprogramming.net/rss.xml - Caches to:
~/.voicemode/music-for-programming/rss.xml - Subsequent runs: Tries fresh fetch, falls back to cache if offline
- Episode lookup: Parses RSS to find correct URL for any episode number
Offline Support
If you're offline but have a cached RSS feed, mpv-dj mfp will still work:
- Episode URL lookups use the cached
rss.xml - Already-downloaded audio files play normally
- Only fails if both network and cache are unavailable
Helper Commands
# Get URL for an episode
mfp-rss-helper url 49 # Returns full URL for episode 49
# Get RSS-based filename (without extension)
mfp-rss-helper filename 49 # Returns: music_for_programming_49-julien_mier
# List all available episodes
mfp-rss-helper list # Shows episode numbers and curators
# Force refresh the cache
mfp-rss-helper refresh # Updates rss.xml from network
Cache Location
~/.voicemode/music-for-programming/rss.xml
The cache is automatically created and updated. Delete it to force a fresh fetch on next use.
Streaming vs Local
| Mode | When | Chapter File |
|---|---|---|
| Streaming | Audio not cached locally | .ffmeta (FFMETADATA format) |
| Local | Audio exists in music-for-programming/ | .cue (CUE sheet format) |
The mpv-dj tool automatically:
- Checks for local audio file first
- Falls back to HTTP streaming if not found
- Uses appropriate chapter format for each mode
Caching Streams
To save an episode while streaming (future feature):
mpv-dj mfp 49 --cache # Stream and save to local directory
Chapter Metadata
Chapters enable track-level navigation within episodes. Each chapter identifies:
- Track title
- Artist/performer
- Timestamp
With chapters loaded, you can:
- Ask "what's playing?" and get the actual track name
- Skip forward/backward by track (not arbitrary seek)
- See track information in
mpv-dj status
Creating Chapter Files
Chapter files are created by:
- Getting the tracklist from musicforprogramming.net
- Downloading reference tracks
- Using MFCC fingerprinting to find timestamps
- Converting results to CUE and FFMETADATA formats
See chapters.md for the chapter file format details.
Chapter Distribution
VoiceMode provides chapter files through a three-tier lookup system that ensures you have access to chapter metadata even for episodes not bundled with your installed version.
Lookup order (local → package → GitHub):
- Local cache (
~/.voicemode/music-for-programming/) - fastest, checked first - Bundled package - chapter files included in VoiceMode, copied to cache on-demand
- GitHub repository - fallback for newly added chapters not yet in your installed version
How it works:
- When you run
voicemode dj mfp play 49, the system checks for chapter files in the local cache - If not found locally, chapter files are copied from the bundled package to the cache
- If not in the package, chapters are fetched from GitHub (requires network, 5-second timeout)
- Fetched files are cached locally for future use
- User modifications to chapter files are preserved during updates (backed up with
.userextension)
Benefits of GitHub fallback:
- Access chapter files added after your VoiceMode version was released
- Community contributions available immediately without upgrading
- Graceful degradation: playback continues without chapters if unavailable
Available bundled chapters:
- Episode 49 (Julien Mier)
Syncing Chapter Files
Use the sync command to manage chapter files:
# Sync all chapter files from package
voicemode dj mfp sync
# Force overwrite (backs up user modifications to .user)
voicemode dj mfp sync --force
Sync behavior:
- New files: Copied from package to local directory
- Unchanged files: Skipped (already up to date)
- Updated files: If you modified a chapter file, your version is backed up to
.userbefore updating - Skipped files: Local modifications preserved unless
--forceis used
Example output:
Added: music_for_programming_49-julien_mier.cue
Added: music_for_programming_49-julien_mier.ffmeta
Unchanged: music_for_programming_51-mücha.ffmeta
Chapter sync complete
Cache directory: /Users/you/.voicemode/music-for-programming
Checksum-based sync: The sync compares SHA256 checksums between package and local files to detect changes accurately. Local checksums are stored in .chapters.sha256 (hidden file).
Contributing Chapter Files
Community contributions to improve chapter accuracy are welcome! To contribute:
- Fork the VoiceMode repository on GitHub
- Create or improve chapter files in
voice_mode/data/mfp/ - Update the checksums by regenerating
chapters.sha256:cd voice_mode/data/mfp shasum -a 256 *.cue *.ffmeta > chapters.sha256 - Submit a Pull Request with your changes
Chapter file format: See chapters.md for FFMETADATA format details.
Quality guidelines:
- Timestamps should be accurate to within a few seconds
- Track titles should match the official MFP tracklist
- Test playback with
voicemode dj mfp play <episode>and verify navigation works
Listing Episodes
The mfp list command shows available episodes with their chapter and local file status.
Basic Usage
# Show episodes with complete chapter files (CUE + FFmeta)
mpv-dj mfp list
# Show all episodes from RSS feed
mpv-dj mfp list --all
# Show episode URLs for downloading
mpv-dj mfp list --urls
# Show CUE/FFmeta file status separately
mpv-dj mfp list --verbose
Output Formats
The list command automatically detects terminal vs pipe:
- Terminal: Human-readable columnized format
- Pipe/redirect: TSV (tab-separated values) for scripting
# Human format (in terminal)
mpv-dj mfp list
# # Curator Ch MP3
# 49 julien mier yes yes
# 51 mücha yes -
# TSV format (piped)
mpv-dj mfp list | head -3
# 49 julien mier chapters local
# 51 mücha chapters -
TSV Output Format for LLMs
When piped, output is tab-separated for easy parsing by AI assistants:
| Column | Description | Values |
|---|---|---|
| 1 | Episode number | Integer (e.g., 49) |
| 2 | Curator name | String (e.g., julien mier) |
| 3 | Chapter status | chapters or - |
| 4 | Local MP3 | local or - |
| 5 | URL (with --urls) | Full download URL |
With --verbose flag, column 3 splits into:
| Column | Description | Values |
|---|---|---|
| 3 | CUE file | cue or - |
| 4 | FFmeta file | ffmeta or - |
| 5 | Local MP3 | local or - |
LLM Examples:
# Find episodes with chapters ready to play
mpv-dj mfp list | awk -F'\t' '$3=="chapters" {print $1, $2}'
# Get download URL for episode 49
mpv-dj mfp list --all --urls | awk -F'\t' '$1==49 {print $5}'
# Count episodes with local MP3s
mpv-dj mfp list --all | awk -F'\t' '$4=="local"' | wc -l
Column Indicators
| Indicator | Human Format | TSV Format | Meaning |
|---|---|---|---|
| Ch/chapters | yes / - |
chapters / - |
Both CUE and FFmeta files exist |
| CUE | yes / - |
cue / - |
CUE file exists (--verbose) |
| FFm | yes / - |
ffmeta / - |
FFmeta file exists (--verbose) |
| MP3 | yes / - |
local / - |
Local audio file downloaded |
Note: "Chapters" requires both CUE (for local playback) and FFmeta (for streaming) files. Use --verbose to see which files are missing.
Episode Discovery
Current
- Request episodes by number:
mpv-dj mfp 49 - List episodes with chapters:
mpv-dj mfp list - List all episodes:
mpv-dj mfp list --all - Search tracks:
grep "Boards of Canada" ~/.voicemode/music-for-programming/*.cue
Planned
- History tracking: What episodes have been played
- Favorites: Mark episodes you enjoyed
- Suggestions: "Play something new" selects unplayed episodes
Collaboration Opportunity
The chapter generation process could be shared with the Music For Programming curator (Datasette) to:
- Provide chapters for all ~90 episodes
- Enable richer track metadata
- Create a great partnership for promotion