#851: normalize '/' ':' '_' to spaces so slash-titles match underscore sources

The candidate matcher rejected valid downloads of titles with a '/' or ':' (e.g.
Sawano's "You See Big Girl / T:T") because the unified normalize() removed those
chars ("t:t" -> "tt") while keeping the '_' that source filenames substitute for
them ("T_T" -> "t_t") — the asymmetry tanked the similarity score. Now '/ \ : _'
all map to spaces before the strip, so "/ T:T" and "_ T_T" both normalize to "t t".

Verified on the real library: similarity for the Sawano pair 0.927 -> 1.000; only
348/40786 strings (0.85%, all containing those separators) change; worst-case
joined-variant match (e.g. "12:05" vs "1205") stays 0.889, well above the 0.70
title threshold — no match regressions. Fixes the matching half of #851.
This commit is contained in:
BoulderBadgeDad 2026-06-11 10:46:36 -07:00
parent 17440329c1
commit 89f843d223
2 changed files with 29 additions and 0 deletions

View file

@ -67,6 +67,11 @@ def normalize(text: str) -> str:
'', s, flags=re.IGNORECASE,
)
s = re.sub(r'\s*-\s*from\s+.+$', '', s, flags=re.IGNORECASE)
# Path/separator punctuation -> space so a title keeps matching a source
# filename that substituted '_' for an illegal '/' or ':' (#851): the on-disk
# "You See Big Girl _ T_T" must normalize the same as "You See Big Girl / T:T".
# Done before the strip below so they become word boundaries, not joins.
s = re.sub(r'[\\/:_]+', ' ', s)
# Drop remaining punctuation but keep word chars (incl. CJK) + spaces.
s = re.sub(r'[^\w\s]', '', s)
s = re.sub(r'\s+', ' ', s).strip()

View file

@ -0,0 +1,24 @@
"""#851: a '/' or ':' in a title must normalize the same as a source filename
that substituted '_' for them, so the candidate matcher stops rejecting valid
downloads (e.g. Sawano's "You See Big Girl / T:T" vs on-disk "..._ T_T")."""
from __future__ import annotations
from core.matching.audio_verification import normalize, similarity
def test_slash_colon_title_matches_underscore_source():
assert normalize("You See Big Girl / T:T") == normalize("You See Big Girl _ T_T")
assert similarity("You See Big Girl / T:T", "You See Big Girl _ T_T") == 1.0
def test_separators_become_word_boundaries():
assert normalize("Re:Zero") == "re zero"
assert normalize("AC/DC") == "ac dc"
assert normalize("T_T") == "t t"
def test_joined_variant_stays_above_title_threshold():
# Spacing a separator must not drop a joined-variant match below 0.70.
assert similarity("AC/DC", "ACDC") >= 0.70
assert similarity("12:05", "1205") >= 0.70