--- name: music-library-management description: Manage local music libraries — tag, rename, and organize files with beets and mutagen. Absorbed music-renamer (mutagen-based in-place rename workflow). category: media --- # Music Library Management Tag, rename, and organize local music files. Covers beets (music library manager) for metadata-aware workflows and mutagen for fast bulk operations when files already have clean ID3 tags. ## Triggers - User wants to rename/tag/organize music files - User asks about beets, mutagen, or music file metadata - User has a collection of mp3/flac/m4a files that need cleanup ## Prerequisites Install beets (includes mutagen as a dependency): ```bash pip install --break-system-packages beets ``` Config lives at `~/.config/beets/config.yaml`. ## Workflow Decision ### Use beets when: - Files need MusicBrainz tagging (missing or wrong metadata) - You want to reorganize files into a standard Artist/Album/Track structure - You need beets' query and library features long-term - Small batch (<100 files) — even with MusicBrainz timeout, it's manageable ### Use mutagen directly when: - Files already have clean ID3 tags (artist, title, album present) - Bulk renaming is the only goal (format change, not metadata enrichment) - Large batch (100+ files) — beets import is too slow per-file - In-place renaming (keep directory structure, just fix filenames) ## Beets Workflow ### Configuration (`~/.config/beets/config.yaml`) ```yaml directory: /path/to/music/root library: /path/to/music/root/musiclibrary.db import: copy: no move: no # no for import-only; yes when ready to reorganize write: yes quiet: yes # skip confirmation prompts # Treat loose singles as a compilation singletons: album: Singles albumartist: Various Artists compilation: yes paths: singleton: %(artist)s - %(title)s comp: Compilations/%(album)s/%(artist)s - %(title)s default: %(albumartist)s/%(album)s/%(track)02d - %(title)s ``` ### Import files into the library ```bash # Slow — hits MusicBrainz per file. Use -A (no autotag) if tags are already good. beet import -A -q --singletons /path/to/music/dir/ ``` ### Query and rename already-imported files ```bash # Dry run: see what would change beet ls -f '$path || $artist - $title' 'query' # Actually rename (moves per path template in config) beet move 'query' ``` For in-place renaming with beets, the path template must match the directory structure. Beets always uses `directory + path_template` — it cannot rename within arbitrary subdirectories without moving. ## Mutagen Bulk Rename When beets import is too slow and files already have ID3 tags, use the script at `scripts/bulk-rename.py`. It walks a directory tree, reads artist/title from tags via mutagen, and renames in-place to `Artist - Title.ext`. ## Linked files - `scripts/bulk-rename.py` — Bulk in-place rename using mutagen ID3 tags - `scripts/rename_by_tags.py` — Alternative rename script (absorbed from music-renamer) The script handles: - Collisions (appends `(1)`, `(2)` to duplicates in the same directory) - Special characters (strips `/` and null bytes) - Files already correctly named (skips) ## Quick Rename Workflow (mutagen, absorbed from `music-renamer`) For the specific use case of renaming files in-place using embedded ID3 tags (files already have good tags, just need cleaner filenames), use the script at `scripts/rename_by_tags.py`. It walks a directory tree, reads artist/title from mutagen, and renames to `Artist - Title.ext`. ### Trigger for this workflow User asks to rename/organize music files, clean up filenames, strip number prefixes from downloaded music. ### Step 1 — Determine scope **CRITICAL**: Confirm which directories the user wants renamed. Never assume "all music." Use `find` with a `-regex` pattern to count files with number prefixes: ```bash find /path/to/music -type f -regex ".*/[0-9]+\. .*" ``` Files with number prefixes (e.g., `123. Title.mp3`) are the unrenamed ones. ### Step 2 — Run the rename script Use `scripts/rename_by_tags.py` via `execute_code`. The script: - Walks a base directory recursively - Reads artist/title from ID3 tags via mutagen - Renames files to `Artist - Title.ext` in-place (same directory) - Skips files already in the correct format - Handles collisions by appending `(1)`, `(2)` etc. - Handles slashes in artist/title by replacing with `-` ### Step 3 — Verify ```bash ls /path/to/music/some-folder/ | head -10 find /path/to/music -regex ".*/[0-9]+\. .*" | wc -l # should be 0 ``` ### When to use mutagen vs beets | Scenario | Tool | |---|---| | Files already have good ID3 tags, just need renaming | **mutagen** (scripts/rename_by_tags.py) | | Files have NO tags, need to be matched against MusicBrainz | **beets import** (with autotag) | | Small batch (< 50 files) needing autotag | **beets import** is fine | | Large batch (> 100 files) | **mutagen** — beets import will time out | ### User preferences - Ray prefers mutagen over beets for bulk renaming - Always confirm directory scope — don't expand beyond what was asked ## Pitfalls - **Beets import is slow**: Even with `-A` (no autotag), beets still does per-file MusicBrainz lookups. Expect ~1-3 seconds per file. For 1000+ files, mutagen is the right choice. - **Beets `move` vs in-place**: `beet move` respects `directory + path_template`. It cannot do truly in-place renames within arbitrary subdirectories. For in-place renames, use mutagen or a script as above. - **`beet update`**: Updates metadata FROM files TO library — not the reverse. Does not update stored paths after external renames. - **Config YAML quoting**: Beets path templates use `%(var)s` syntax. The YAML linter may warn about unquoted `%` characters but beets parses them correctly.