Your music library, your rules.
A lightweight, filesystem-driven Python tool that enforces your personal genre definitions across your entire music library. No more "Alternative Rock" vs "Rock - Alternative" inconsistencies—define each artist's genre once, and let the tool handle the rest.
- 🎯 Artist-based genre enforcement — Define genre once per artist, apply to their entire discography
- 💾 Persistent definitions — Never re-prompt for artists you've already defined
- 🔄 Incremental workflow — Only prompts for new artists on subsequent runs
- ⚡ Smart apply mode — Only processes artists modified since last run (fast for large libraries)
- 🎨 Reuse existing genres — Pick from your already-defined genres to maintain consistency
- 🗂️ Filesystem-driven — No database; works directly with your folder structure
- 🏷️ Universal format support — Writes to MP3, FLAC, M4A, OGG, OPUS, and more via Mutagen
- 🔍 Dry-run mode — Preview changes before writing
- 🐧 Debian-friendly — Uses system Python packages (no pip/venv headaches)
- 📋 Interactive CLI — Clean menu-driven interface, no GUI dependencies
- 🚀 Parallel processing — Uses multiple threads for faster tagging
- Python 3.7+ (usually pre-installed on Debian/Ubuntu)
- python3-mutagen (for reading/writing audio tags)
On Debian/Ubuntu:
sudo apt update
sudo apt install -y python3-mutagenOn other distros (using pip):
pip install mutagensudo curl -o /usr/local/bin/music-genre-enforcer https://raw.githubusercontent.com/WB2024/Artist-Genre-Metadata-Enforcer/main/music-genre-enforcer.py
sudo chmod +x /usr/local/bin/music-genre-enforcerOr clone the repo:
git clone https://github.com/WB2024/Artist-Genre-Metadata-Enforcer.git
cd Artist-Genre-Metadata-Enforcer
sudo cp music-genre-enforcer.py /usr/local/bin/music-genre-enforcer
sudo chmod +x /usr/local/bin/music-genre-enforcermusic-genre-enforcerOn first run, you'll be prompted to configure your library base path.
When a new version is released, simply re-download and replace the script:
sudo curl -o /usr/local/bin/music-genre-enforcer https://raw.githubusercontent.com/WB2024/Artist-Genre-Metadata-Enforcer/main/music-genre-enforcer.py && sudo chmod +x /usr/local/bin/music-genre-enforcercd Artist-Genre-Metadata-Enforcer
git pull
sudo cp music-genre-enforcer.py /usr/local/bin/music-genre-enforcer
sudo chmod +x /usr/local/bin/music-genre-enforcerNote: Your configuration and artist definitions in
~/.config/music-genre-enforcer/are preserved—updating only replaces the script itself.
The tool expects this folder hierarchy:
/your/music/library/
├── A/
│ ├── Aphex Twin/
│ │ ├── [1992] Selected Ambient Works 85-92/
│ │ │ ├── 01 - Xtal.flac
│ │ │ └── ...
│ └── ...
├── B/
│ ├── Bowie, David/
│ │ └── ...
├── #/
│ └── 2Pac/
│ └── ...
└── Various Artists/
└── ...
Where:
- Top level = grouping folders (typically letters:
A,B,#,Various Artists, etc.) - Second level = artist folders
- Third level+ = albums, discs, tracks
- Configure your library base path (menu option
1) - Scan for new artists and define genres (menu option
2)- Type a genre as free text, or
- Press
lto list existing genres - Press
pto pick from existing genres (keeps spelling/casing consistent) - Press
sto skip an artist
- Apply genres using smart or full mode:
- Smart apply (option
3) — Only processes artists with new/modified files since last run - Full apply (option
4) — Checks all artists (use periodically for thorough verification) - Writes the
GENREtag to every audio file under each artist folder - Skips files that already have the correct genre
- Smart apply (option
When prompted for a genre:
| Command | Action |
|---|---|
| (type text) | Define a new genre (free text) |
l |
List all your existing unique genres |
p |
Pick a genre by number from the list |
s |
Skip this artist for now |
? |
Show help |
Example:
Genre for [Aphex Twin] (or l/p/?/s): p
Existing genres:
[1] Electronic - Ambient
[2] Hip-Hop
[3] Post-Punk
[4] Rock - Art
Pick number (blank cancels): 1
-> Set [Aphex Twin] = Electronic - Ambient
1) Configure — Edit base library path, file extensions, dry-run mode
2) Scan artists and prompt for NEW artist genres
3) Smart apply — Only process artists modified since last run (fast)
4) Full apply — Check ALL artists (slower but thorough)
5) List undefined artists
6) Edit an existing artist genre (numbered picker)
7) Toggle dry-run (preview mode on/off)
q) Quit
All settings and definitions are stored in:
~/.config/music-genre-enforcer/
├── config.json # Library path, extensions, dry-run setting
├── artist_genres.json # Your artist → genre mappings
└── last_apply.json # Timestamp of last apply (for smart mode)
Portable? Yes—copy these files to another machine and your definitions travel with you.
You ripped CDs, downloaded from different sources, used multiple taggers—now you have:
- "Hip-Hop" vs "Hip Hop" vs "Rap"
- "Rock - Art" vs "Art Rock" vs "Alternative"
Solution: Define your canonical genre once per artist, enforce it everywhere.
As far as you're concerned, all 2Pac songs are Hip-Hop and all Velvet Underground songs are Rock - Art—regardless of what MusicBrainz says.
Solution: This tool enforces your subjective opinion, not a database's.
Every time you add a new artist, you'd have to manually tag every file.
Solution: Define the artist's genre once; the tool writes it to all files (even multi-disc sets with hundreds of tracks).
Anything Mutagen supports in easy mode:
- MP3 (ID3v2)
- FLAC (Vorbis comments)
- M4A/MP4/ALAC (iTunes-style tags)
- OGG Vorbis, Opus
- WavPack, APE, ASF/WMA
- AIFF (experimental)
Edit ~/.config/music-genre-enforcer/config.json:
{
"library_base": "/your/music/path",
"supported_exts": [".mp3", ".flac", ".m4a", ".ogg", ".opus"],
"dry_run": false
}Or use menu option 1 to configure interactively.
Enable via menu option 6 or edit config.json to set "dry_run": true.
The tool will:
- Scan all files
- Report what would change
- Not write anything to disk
Perfect for testing before committing.
Debian/Ubuntu:
sudo apt install python3-mutagenOther (pip):
pip install mutagenRun menu option 1 (Configure) and double-check your path. Use absolute paths (e.g., /srv/data/Music).
This is normal. Skipped = file already has the correct genre tag (no need to rewrite).
The tool enforces single genre only (no ;, |, or / separators).
If you need multi-genre tags, open an issue—I can add that as an option.
Contributions welcome! Please:
- Fork the repo
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License—see LICENSE for details.
You are free to use, modify, and distribute this software. Attribution appreciated but not required.
If this tool saved you hours of manual tagging, consider buying me a coffee:
- Built with Mutagen—the best Python audio tagging library
- Inspired by the frustration of managing 4,000+ track libraries with inconsistent metadata
- Author: WB2024
- GitHub: @WB2024
- Issues: Report a bug or request a feature
Happy tagging! 🎶