- 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>
- hint_extractor: add _normalize_vertical_tracklist() to handle bare-number/
title/duration format (Tufaranka-style tracklists)
- hint_extractor: fix level-1 tracklist match — allow disc_num=None (single-CD)
by assuming disc=1; previously no tracklist title was ever applied to single-
CD tracks because the guard required disc_num to be set
- music_enricher: register module in sys.modules before exec_module() so
@dataclass definitions in jellyfin_playlist_generator work correctly
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
scanner.py: collect_album_dirs() now recursively finds album dirs
- Dirs with audio files at root → album
- Dirs with disc subdirs (CD1/CD2) and no root audio → multi-CD album
- Container dirs without audio → recurse into subdirs
music_enricher.py:
- After execute_album(), auto-discovers jellyfin_playlist_generator.py
in ../Jellyfin_Playlist_Generator/ (or via --playlist-generator PATH)
- Calls generate_playlist() directly via importlib — no subprocess,
no destructive cleanup_all_playlists, targeted to the enriched album
- New --playlist-generator CLI option for custom generator path
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
hint_extractor: _ocr_back_cover() sends back/inlay images to Ollama Vision
when no tracklist .txt/.htm/.nfo is present. Model priority:
qwen3-vl:latest → minicpm-v:latest → deepseek-ocr:latest (configurable
via OLLAMA_OCR_MODEL env var). Timeout 180s. OCR text is fed into the
same _parse_tracklist() pipeline as regular text files.
music_enricher: extract_hints(use_ocr=not args.no_api) — OCR is skipped
with --no-api to allow fully offline/fast runs.
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>
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>