From 17114546c74d268b40e295edd1cb4482eea28a8f Mon Sep 17 00:00:00 2001 From: David Bishop Date: Fri, 13 Feb 2026 01:33:02 -0800 Subject: [PATCH] Fix case-sensitive comparison in compare_strings fuzzy matching (#3151) In non-strict mode, compare_strings lowercases str1 but passes the original-case str2 to SequenceMatcher on line 564. This causes fuzzy matching to penalize case differences, making it fail to match strings like "Track Feat. John" vs "TRACK FT. JOHN" that should be considered equivalent. Also fix the elif branch (line 559) to replace on str2_lower instead of str2, so the result is consistently lowered. Co-authored-by: David Bishop Co-authored-by: Claude Opus 4.6 --- music_assistant/helpers/compare.py | 4 ++-- tests/core/test_compare.py | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/music_assistant/helpers/compare.py b/music_assistant/helpers/compare.py index 81b05029..c03d0648 100644 --- a/music_assistant/helpers/compare.py +++ b/music_assistant/helpers/compare.py @@ -556,12 +556,12 @@ def compare_strings(str1: str, str2: str, strict: bool = True) -> bool: if " & " in str1_lower and " and " in str2_lower: str2 = str2_lower.replace(" and ", " & ") elif " and " in str1_lower and " & " in str2: - str2 = str2.replace(" & ", " and ") + str2 = str2_lower.replace(" & ", " and ") if create_safe_string(str1) == create_safe_string(str2): return True # last resort: use difflib to compare strings required_accuracy = 0.9 if (len(str1) + len(str2)) > 18 else 0.8 - return SequenceMatcher(a=str1_lower, b=str2).ratio() > required_accuracy + return SequenceMatcher(a=str1_lower, b=str2_lower).ratio() > required_accuracy def compare_version(base_version: str, compare_version: str) -> bool: diff --git a/tests/core/test_compare.py b/tests/core/test_compare.py index f80711ce..578efbe8 100644 --- a/tests/core/test_compare.py +++ b/tests/core/test_compare.py @@ -369,3 +369,10 @@ def test_compare_track() -> None: # noqa: PLR0915 (ExternalID.MB_RECORDING, "abcd"), } assert compare.compare_track(track_a, track_b) is False + + +def test_compare_strings_case_insensitive_fuzzy() -> None: + """Test that non-strict fuzzy matching is fully case-insensitive.""" + # These differ slightly ("Feat." vs "FT.") so create_safe_string won't match, + # falling through to SequenceMatcher which must compare both strings lowered. + assert compare.compare_strings("Track Feat. John", "TRACK FT. JOHN", strict=False) is True -- 2.34.1