- hint_extractor: filter existing tags through _is_good() so 'Unknown',
'Unknown Artist' etc. in existing ID3 tags don't override filename-parsed
artist names
- executor: _is_classical() now returns False when track_artist is a placeholder
('unknown', 'unknown artist') — prevents pop albums from getting the
Performer-Composer-Work filename schema
- executor/music_enricher: pass albumartist to _proposed_filename() so fallback
works when track artist is missing; fix display to use albumartist fallback too
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
disc_number=1 is now treated identical to disc_number=None: no 'D-' prefix in
filenames, no discnumber tag written. The D-TT prefix and discnumber tag are
only applied for genuine multi-CD albums (disc_number > 1).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
_update_m3u(): writes #EXTM3U + #EXTINF:seconds,Artist - Title + filename
per track, in disc/track order (same order as the renamed files).
Duration is read from mutagen; -1 if unavailable.
execute_album(): after renaming, finds existing *.m3u / *.m3u8 in the
album directory and overwrites it. Only triggered when files_renamed > 0
and a playlist file exists — never creates a new one from scratch.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
executor: disc=1 now generates '1-01' prefix (same as disc=2 → '2-01'),
so multi-disc albums have consistent D-TT scheme throughout.
Single-disc tracks without disc tag stay as plain 'TT'.
hint_extractor: tracklist pattern 2 now requires '.' ')' or ':' as separator
(not bare whitespace) — prevents false-positive matches like
'2 x CD, Compilation, Remastered' being parsed as track 2.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Pop schema: TT_-_Artist_-_Title.ext
Classical schema: TT_-_Performer_-_Komponist_-_Werk[-_Orchester_Dirigent].ext
triggered when albumartist ≠ track artist (pianist vs composer)
All spaces in names → underscores; separator _-_ between parts.
Missing parts (orchestra, conductor) are omitted.
models.py: added conductor/orchestra optional fields to TrackProposal.
executor.py: sanitize_dir_names() tries NameToUnix first, falls back to detox.
Called after all renames in a directory are complete.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Filename schema now: TT - AlbumArtist - TrackArtist - Title when albumartist
differs from track artist (e.g. pianist vs. composer). Identical artist → old
two-part format unchanged.
metadata_resolver: removed Claude API fallback entirely from _claude_resolve.
Chain is now Ollama (local, free) → OpenRouter (DeepSeek V3, cheap) only.
music_enricher: updated status line and use_claude flag accordingly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Strip non-timestamp characters (BOM, invisible chars) from date/year values
both when reading existing tags in metadata_resolver and when writing in
executor. Also harden the EasyID3 except block to not wipe existing tags
when adding a missing ID3 header, and add per-field try/except in MP3 tag
writing so one bad field doesn't abort the entire track.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
AI-powered per-album pipeline: scan → local hints → MusicBrainz/Discogs/Claude
resolve → cover art → interactive or auto review → tag write + rename + report.
All external dependencies optional; 17/17 unit tests passing.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>