Two real failures from the first hour of round 45, both fixed by treating the file itself as evidence. "B. Clem - Zuuso [1025657891]" fingerprinted fine and matched nothing. Checked before building: AcoustID returns zero results, a MusicBrainz text search for "Zuuso" returns zero, iTunes returns zero. The track is in no metadata service on earth, so no smarter lookup could have answered it — but its filename said exactly what it was. New filename_tags.py reads that, validated against all 474 files in the untagged folder rather than invented examples: it strips yt-dlp ids, drops "(Official Video)" noise, splits Artist - Title, reads a leading 1-04, and falls back to the iTunes tree. It is careful about what it removes — an 11-char trailing token counts as a YouTube id only if it carries a digit, an underscore or flipping case, or "Cold Draft-Underground" loses a word, and a hyphen only splits when it has spaces around it so Jay-Z survives. A guess says it is one: candidates carry a source, and the dialog quotes a confidence only for real fingerprint matches. The file now ranks lookup results too, which fixes the Alhambra case: token overlap (words double, bare numbers single), a duration bucket against the recording lengths AcoustID returns, and a filename year that predates what the database knows. Live also stops counting as a demerit — it was lumped in with Compilation and buried a live album for a file whose name said "Live At The Alhambra". That track now proposes the right album with year 1961 instead of a compilation with 2002. The year genuinely needed the filename: MusicBrainz's own first-release-date for "Ahmad Jamal's Alhambra" is 2002, because its three original 1961 pressings are stored undated. The obvious fix of a second MusicBrainz call returns the same wrong answer. Not done on purpose: trav rejected "never propose a shorter title" — truncating is wanted, since the garbage is usually the part being dropped. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NsiFHdyVg1UhBSxDJfTNRm
228 lines
9.3 KiB
Python
228 lines
9.3 KiB
Python
"""Round 46 — Identify Track stops giving up when no database knows the song.
|
|
|
|
Two real failures from trav's library drove this. "B. Clem - Zuuso
|
|
[1025657891]" fingerprinted fine and returned *zero* AcoustID results — the
|
|
track is underground, so it is in no database at all, and no amount of
|
|
cleverness can look up what does not exist. But its filename said exactly what
|
|
it was, which is how trav knew. And "Snowfall (Live At The Alhambra_1961)"
|
|
matched, yet proposed a 2002 reissue year and ranked a compilation above the
|
|
album the filename named, because nothing compared the results against what
|
|
the file already said.
|
|
|
|
So the file itself is now evidence: it seeds a proposal when the lookup finds
|
|
nothing, and it ranks the lookup's results when it finds several.
|
|
"""
|
|
import pytest
|
|
|
|
from lintunes.fingerprint import (
|
|
IdentifyCandidate, candidate_from_filename, hint_for, parse_lookup,
|
|
)
|
|
from lintunes.filename_tags import (
|
|
leading_track_number, parse_filename, split_artist_title,
|
|
strip_download_id, strip_noise, year_in,
|
|
)
|
|
|
|
|
|
class _Track:
|
|
def __init__(self, name="", artist="", album="", location=""):
|
|
self.track_id = 1
|
|
self.name = name
|
|
self.artist = artist
|
|
self.album = album
|
|
self.location = location
|
|
|
|
|
|
UNKNOWN = "/m/Unknown Artist/Unknown Album/"
|
|
|
|
|
|
class TestStrippingDownloadIds:
|
|
@pytest.mark.parametrize("stem, expected", [
|
|
("B. Clem - Zuuso [1025657891]", "B. Clem - Zuuso"),
|
|
("Stardust-321895497", "Stardust"),
|
|
("Cold Draft-WC7gK2kgyTQ", "Cold Draft"),
|
|
("Self Esteem [EtNZnhxWLHo]", "Self Esteem"),
|
|
("Caught Up in the Rapture [Oz-b86LZ21c]", "Caught Up in the Rapture"),
|
|
])
|
|
def test_yt_dlp_ids_are_removed(self, stem, expected):
|
|
assert strip_download_id(stem) == expected
|
|
|
|
def test_a_real_word_is_not_mistaken_for_a_youtube_id(self):
|
|
""""Underground" is 11 characters too — length alone can't decide."""
|
|
assert strip_download_id("Cold Draft-Underground") == \
|
|
"Cold Draft-Underground"
|
|
|
|
def test_a_year_is_not_mistaken_for_a_numeric_id(self):
|
|
assert strip_download_id("Cross Country Tour 1958-1961") == \
|
|
"Cross Country Tour 1958-1961"
|
|
|
|
|
|
class TestStrippingNoise:
|
|
@pytest.mark.parametrize("text", [
|
|
"Dance II (Official Video)",
|
|
"Dance II (Official Music Video)",
|
|
"Dance II (Official Lyric Video)",
|
|
"Dance II [HD]",
|
|
"Dance II (Audio)",
|
|
])
|
|
def test_decorations_go(self, text):
|
|
assert strip_noise(text) == "Dance II"
|
|
|
|
def test_a_real_parenthetical_stays(self):
|
|
assert strip_noise("Take Off (ASOT 1159)") == "Take Off (ASOT 1159)"
|
|
|
|
|
|
class TestSplittingAndNumbering:
|
|
def test_artist_title_split(self):
|
|
assert split_artist_title("B. Clem - Zuuso") == ("B. Clem", "Zuuso")
|
|
|
|
def test_hyphen_inside_a_word_is_not_a_separator(self):
|
|
assert split_artist_title("350-440-DialTone") == \
|
|
(None, "350-440-DialTone")
|
|
|
|
def test_a_repeated_uploader_collapses(self):
|
|
assert split_artist_title("Sean Dream - Sean Dream - tomorrow111") == \
|
|
("Sean Dream", "tomorrow111")
|
|
|
|
def test_leading_track_and_disc_numbers(self):
|
|
assert leading_track_number("1-04 Sitting Back") == \
|
|
("Sitting Back", 4, 1)
|
|
assert leading_track_number("02 nashville cats") == \
|
|
("nashville cats", 2, None)
|
|
|
|
def test_a_number_that_is_the_whole_title_is_kept(self):
|
|
assert leading_track_number("1979") == ("1979", None, None)
|
|
|
|
|
|
class TestYearMining:
|
|
def test_a_single_year_is_read(self):
|
|
assert year_in("Snowfall (Live At The Alhambra_1961)") == 1961
|
|
|
|
def test_a_range_is_refused(self):
|
|
""""1958-62" and "1958-1961" name a span, not a release year."""
|
|
assert year_in("Cross Country Tour 1958-1961") is None
|
|
|
|
def test_no_year_is_none(self):
|
|
assert year_in("Zuuso") is None
|
|
|
|
|
|
class TestFilenameProposal:
|
|
def test_the_zuuso_case(self):
|
|
"""The track that started this: fingerprinted fine, matched nothing,
|
|
and its name said exactly what trav already knew it was."""
|
|
track = _Track(name="B. Clem - Zuuso [1025657891]",
|
|
location=UNKNOWN + "B. Clem - Zuuso [1025657891].mp3")
|
|
candidate = candidate_from_filename(track)
|
|
assert candidate.source == "filename"
|
|
assert candidate.fields() == {"name": "Zuuso", "artist": "B. Clem"}
|
|
|
|
def test_apostrophes_come_back(self):
|
|
track = _Track(location=UNKNOWN + "cehryl - That_s So Sick! [738273660].mp3")
|
|
assert candidate_from_filename(track).name == "That's So Sick!"
|
|
|
|
def test_the_folder_tree_fills_what_the_name_omits(self):
|
|
track = _Track(location="/m/Onra/Long Distance/1-04 Sitting Back.mp3")
|
|
fields = candidate_from_filename(track).fields()
|
|
assert fields["artist"] == "Onra"
|
|
assert fields["album"] == "Long Distance"
|
|
assert fields["name"] == "Sitting Back"
|
|
assert fields["track_number"] == 4
|
|
|
|
def test_placeholder_folders_are_not_treated_as_tags(self):
|
|
track = _Track(location=UNKNOWN + "blurf.mp3")
|
|
fields = candidate_from_filename(track).fields()
|
|
assert "artist" not in fields and "album" not in fields
|
|
|
|
def test_a_track_with_no_file_proposes_nothing(self):
|
|
assert candidate_from_filename(_Track()) is None
|
|
|
|
|
|
def _payload(*recordings, score=0.9):
|
|
return {"status": "ok",
|
|
"results": [{"score": score, "recordings": list(recordings)}]}
|
|
|
|
|
|
def _recording(title, duration=None, groups=()):
|
|
rec = {"title": title, "artists": [{"name": "The Band"}],
|
|
"releasegroups": list(groups)}
|
|
if duration is not None:
|
|
rec["duration"] = duration
|
|
return rec
|
|
|
|
|
|
def _group(title, year, type_="Album", secondary=None):
|
|
return {"title": title, "type": type_,
|
|
"secondarytypes": list(secondary or []),
|
|
"releases": [{"date": {"year": year} if year else {}}]}
|
|
|
|
|
|
class TestRankingAgainstTheFile:
|
|
def test_the_album_the_filename_names_wins(self):
|
|
payload = _payload(_recording("Snowfall", groups=[
|
|
_group("Some Other Record", 1970),
|
|
_group("Ahmad Jamal's Alhambra", 2002, secondary=["Live"]),
|
|
]))
|
|
hint = "Snowfall (Live At The Alhambra_1961)"
|
|
assert parse_lookup(payload, hint=hint)[0].album == \
|
|
"Ahmad Jamal's Alhambra"
|
|
|
|
def test_a_live_album_is_no_longer_demoted(self):
|
|
"""Live is what a live album *is*; only compilations get pushed down."""
|
|
payload = _payload(_recording("Snowfall", groups=[
|
|
_group("Greatest Hits", 1990, secondary=["Compilation"]),
|
|
_group("The Live Album", 1995, secondary=["Live"]),
|
|
]))
|
|
assert parse_lookup(payload)[0].album == "The Live Album"
|
|
|
|
def test_duration_picks_the_right_take(self):
|
|
"""Two recordings of one song; only the length tells them apart."""
|
|
payload = _payload(
|
|
_recording("Snowfall", duration=155.0,
|
|
groups=[_group("The Long Take", 2000)]),
|
|
_recording("Snowfall", duration=150.3,
|
|
groups=[_group("The Short Take", 2000)]),
|
|
)
|
|
assert parse_lookup(payload, duration=150)[0].album == "The Short Take"
|
|
|
|
def test_a_year_in_the_filename_beats_a_reissue_date(self):
|
|
"""MusicBrainz often has only the CD reissue dated, with the original
|
|
pressing undated — so the file's own 1961 is the better evidence."""
|
|
payload = _payload(_recording("Snowfall", groups=[
|
|
_group("Ahmad Jamal's Alhambra", 2002)]))
|
|
hint = "Snowfall (Live At The Alhambra_1961)"
|
|
assert parse_lookup(payload, hint=hint)[0].year == 1961
|
|
|
|
def test_a_later_filename_year_never_overrides_an_earlier_one(self):
|
|
payload = _payload(_recording("Song", groups=[_group("Album", 1967)]))
|
|
assert parse_lookup(payload, hint="Song (remaster 1999)")[0].year == 1967
|
|
|
|
def test_hint_never_invents_a_candidate(self):
|
|
assert parse_lookup({"status": "ok", "results": []},
|
|
hint="anything at all") == []
|
|
|
|
|
|
class TestHint:
|
|
def test_hint_gathers_tags_and_filename(self):
|
|
track = _Track(name="Snowfall", artist="Ahmad Jamal",
|
|
location=UNKNOWN + "Snowfall (Live At The Alhambra_1961).mp3")
|
|
hint = hint_for(track)
|
|
assert "Snowfall" in hint and "Ahmad Jamal" in hint and "1961" in hint
|
|
|
|
|
|
class TestDialogLabelsAGuessHonestly:
|
|
def test_filename_candidates_say_where_they_came_from(self, qapp):
|
|
from lintunes.gui.identify_dialog import IdentifyDialog
|
|
track = _Track(name="B. Clem - Zuuso [1025657891]")
|
|
guess = IdentifyCandidate(score=0.0, source="filename",
|
|
name="Zuuso", artist="B. Clem")
|
|
dialog = IdentifyDialog(track, [guess])
|
|
assert "file's name" in dialog._header.text().lower()
|
|
# No fabricated confidence percentage for something nothing matched.
|
|
assert "%" not in dialog._header.text()
|
|
assert "From the filename" in dialog._combo.itemText(0)
|
|
|
|
def test_acoustid_candidates_still_quote_confidence(self, qapp):
|
|
from lintunes.gui.identify_dialog import IdentifyDialog
|
|
candidate = IdentifyCandidate(score=0.98, name="Snowfall")
|
|
dialog = IdentifyDialog(_Track(name="x"), [candidate])
|
|
assert "98%" in dialog._header.text()
|