summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/src/meshbay_node/indexer/title_parse.py
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-08-24 18:30:00 +0200
committerChristophe Besson <cbesson@gmail.com>2026-08-24 18:30:00 +0200
commit584730f9486e153c6c477293f03a95e78f5ab5f3 (patch)
tree5b8c65926d3cd2a2a187a532e6f92ec35b8b615b /packages/meshbay-node/src/meshbay_node/indexer/title_parse.py
parentc2f5e0ed7ff22e2e176686d0074b027712e9efa3 (diff)
downloadmeshbay-584730f9486e153c6c477293f03a95e78f5ab5f3.tar.gz
fix(node): stop inventing a fake artist from the shared root's name
Music grouping was measured against a real ~5700-file library and came back worse than a plain file listing. Root cause: the artist/album ancestor walk always climbed exactly two levels (parent = album, grandparent = artist) with no idea where the group's own shared root was. Any file in a flat top-level folder — common here: bare `Artist/track.mp3`, no album subfolder at all — had its "grandparent" resolve to the root directory's own name, so the artist got replaced by the share's name. Measured: 289 of 5664 tracks across 41 real, unrelated artists (Ben Harper, Dire Straits, Jimi Hendrix, Janis Joplin, ...) collapsed into one fake artist this way — the single biggest bucket in the whole library, ahead of every real one. - `_artist_album_from_ancestors` now takes the entry's own root boundary (daemon.py resolves it via `RootSet.split`) and refuses to read it as a name. A file sitting in a top-level folder — genuinely ambiguous, artist or a standalone album/compilation — is handled by `_split_top_level_folder`: split on "Artist - Album" when the (cleaned) folder name has that shape, otherwise the whole name becomes the artist alone, the more common real case here. - `_clean_tag` treats known tagger placeholders ("No Artist", a French tool's "Nouvel artiste (334)") as absent rather than a real value — they were just as truthy as a real name and were locking out the fallback that would have done better. "Various Artists" is kept, a real compilation credit rather than a placeholder. - A `title` tag that's the bare filename copied verbatim (track number included — found live on a whole CD-single) is stripped through the same prefix rule the filename parser already used (`title_parse.strip_track_prefix`), since a tag normally wins over the parsed title. - Cover art: only 11% of a 400-file sample had embedded art (expected for this era of rip), but 267 loose cover images sit beside the tracks across the library (Windows Media Player's `Folder.jpg`/ `AlbumArt_{guid}_*.jpg`, manual `cover.jpg`) and were never looked at. `_find_sibling_cover` checks the track's own folder before giving up — measured coverage 11% -> 26% on the same library, zero network calls. `_enriched_attempted` is in-memory and resets on restart, so a node restart is enough to re-run enrichment over an already-scanned library with the fixed logic — no rescan flag, no cache to clear by hand. 22 tests in test_enrich_audio.py (11 new), including the exact regression case end to end through the real pool. Full suite: 1129 passed, no regressions. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KBi7ALLGfwcjBXt57yNMcy
Diffstat (limited to 'packages/meshbay-node/src/meshbay_node/indexer/title_parse.py')
-rw-r--r--packages/meshbay-node/src/meshbay_node/indexer/title_parse.py14
1 files changed, 14 insertions, 0 deletions
diff --git a/packages/meshbay-node/src/meshbay_node/indexer/title_parse.py b/packages/meshbay-node/src/meshbay_node/indexer/title_parse.py
index 6676e9b..5203247 100644
--- a/packages/meshbay-node/src/meshbay_node/indexer/title_parse.py
+++ b/packages/meshbay-node/src/meshbay_node/indexer/title_parse.py
@@ -187,6 +187,20 @@ def parse_track_filename(filename: str) -> ParsedTrack:
return ParsedTrack(title=title, track_no=track_no, naive_title=naive_title(filename))
+def strip_track_prefix(text: str) -> str:
+ """
+ Some taggers copy the bare filename into the `title` tag verbatim,
+ track-number prefix included (found live: a whole CD-single's worth of
+ `title` tags reading "01 - Venus As A Boy" rather than "Venus As A
+ Boy") — since a tag normally wins over the filename-parsed title
+ (enrich_audio.py), that pollution would otherwise beat a cleaner parse.
+ A no-op when there's no such prefix, so a genuinely clean tag is
+ returned unchanged.
+ """
+ m = _TRACK_PREFIX_RE.match(text)
+ return re.sub(r"^[\s-]+", "", text[m.end():]).strip() if m else text
+
+
def parse_episode_filename(filename: str) -> ParsedName:
"""
Parse an episode filename. `display_title` may come back None (e.g.