94 lines
3.6 KiB
Markdown
94 lines
3.6 KiB
Markdown
---
|
|
name: music-renamer
|
|
description: Rename local music files using ID3 tags (mutagen), and optionally manage with beets. For when the user wants to clean up numbered/poorly-named music files using their embedded artist/title metadata.
|
|
---
|
|
|
|
# Music Renamer
|
|
|
|
Rename music files in-place using embedded ID3 tags. The primary path uses `mutagen` directly — fast, no network calls, works on any file with good tags. Beets is available as a secondary path for library management but its import step (MusicBrainz matching) is too slow for bulk renames.
|
|
|
|
## Trigger
|
|
User asks to rename/organize music files, clean up filenames, strip number prefixes from downloaded music, or set up beets for local music.
|
|
|
|
## Support files
|
|
- `scripts/rename_by_tags.py` — mutagen-based in-place renamer (run via execute_code)
|
|
- `references/beets-config.yaml` — minimal beets config for no-move setup
|
|
|
|
## 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) |
|
|
| Need full library management / queries / stats | **beets** |
|
|
| Small batch (< 50 files) needing autotag | **beets import** is fine |
|
|
| Large batch (> 100 files) | **mutagen** — beets import will time out |
|
|
|
|
## Step 1 — Determine scope
|
|
|
|
**CRITICAL**: Confirm which directories the user wants renamed. Never assume "all music." If they say "just the Albanian folder," do NOT touch English or other folders. Use `find` with a `-regex` pattern to count files with number prefixes to identify what's unrenamed:
|
|
|
|
```
|
|
find /path/to/music -type f -regex ".*/[0-9]+\. .*"
|
|
```
|
|
|
|
Files with number prefixes (e.g., `123. Title.mp3`) are the unrenamed ones. Files already in `Artist - Title.ext` format are done.
|
|
|
|
## Step 2 — Run the rename script
|
|
|
|
Use `scripts/rename_by_tags.py` (copy the code from `execute_code` — it uses mutagen via the same Python environment). 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 `-`
|
|
|
|
Key code pattern:
|
|
|
|
```python
|
|
from mutagen import File
|
|
audio = File(fullpath, easy=True)
|
|
artist = audio.tags.get('artist', [None])[0]
|
|
title = audio.tags.get('title', [None])[0]
|
|
new_name = f"{artist} - {title}".replace('/', '-') + ext
|
|
```
|
|
|
|
## Step 3 — Verify
|
|
|
|
Check a few directories to confirm the rename:
|
|
|
|
```
|
|
ls /path/to/music/some-folder/ | head -10
|
|
find /path/to/music -regex ".*/[0-9]+\. .*" | wc -l # should be 0
|
|
```
|
|
|
|
## Beets config (fallback)
|
|
|
|
Beets config lives at `~/.config/beets/config.yaml`. Minial config for in-place (no-move) setup:
|
|
|
|
```yaml
|
|
directory: /path/to/music
|
|
library: /path/to/music/musiclibrary.db
|
|
|
|
import:
|
|
copy: no
|
|
move: no
|
|
write: yes
|
|
quiet: yes
|
|
|
|
paths:
|
|
singleton: %(artist)s - %(title)s
|
|
comp: Compilations/%(album)s/%(artist)s - %(title)s
|
|
```
|
|
|
|
### Known pitfalls with beets
|
|
- `beet import` without `-A` hits MusicBrainz for every file — very slow for 100+ files, will time out
|
|
- `beet import -A -q --singletons` still slow for 1000+ files due to per-file overhead
|
|
- `beet move` uses `directory + path_template` — cannot rename truly in-place within subdirectories
|
|
- Only use beets for library management / queries, not bulk renames
|
|
|
|
## User preferences
|
|
- Ray prefers mutagen over beets for bulk renaming
|
|
- Always confirm directory scope — don't expand beyond what was asked
|