How ScoreMatchCalculator Matches Spotify Tracks to YouTube Music

ScoreMatchCalculator implements a refined weighted scoring algorithm that combines duration similarity (30%), artist overlap (40%), title similarity (30%), and an optional album bonus (+0.1) to identify the best YouTube Music counterpart for any Spotify track.

The ScoreMatchCalculator class in the gabrielbaute/spotify-saver repository serves as the core decision engine for cross-platform music matching. Implemented in spotifysaver/services/score_match_calculator.py, this service evaluates candidate YouTube Music results against Spotify track metadata using four specialized sub-scores. Understanding its weighted algorithm helps developers tune matching accuracy for music migration workflows.

Architecture of the Scoring Pipeline

The calculator aggregates four independent metrics into a single match score ranging from 0 to 1.0 (plus potential bonus). As implemented in the _calculate_match_score method, the final score derives from:

  • Duration similarity (maximum 0.3)
  • Artist overlap (maximum 0.3)
  • Title similarity (maximum 0.3)
  • Album match bonus (+0.1 optional)

The YoutubeMusicSearcher class in spotifysaver/services/youtube_api.py instantiates the calculator at lines 30-37 (self.scorer = ScoreMatchCalculator()). For every raw search result, the searcher calls self.scorer._calculate_match_score(result, track, strict), filters out scores ≤ 0, sorts remaining candidates descending, and selects the highest-scoring entry as the match.

The Four Scoring Components

Duration Matching with Linear Decay

The _score_duration method (lines 45-58 in spotifysaver/services/score_match_calculator.py) compares track lengths using a tolerance-based curve. It awards the full 0.3 weight when the absolute time difference is ≤ 2 seconds. For larger disparities, it applies a linear decay formula:

max(0, 1 - (diff / 5)) * 0.3

This ensures that tracks with minor duration variations (such as differing intro/outro lengths) still receive high scores while significantly different lengths are penalized proportionally.

Artist Overlap Detection

The _score_artist_overlap method (lines 59-74) calculates set intersection between lower-cased artist names from both platforms. Contributing up to 0.3 to the total score, this component adds a 0.1 bonus when the primary Spotify artist appears in the YouTube result's artist set. The method builds discrete sets for comparison, ensuring that "feat." or "with" variations do not disqualify otherwise valid matches.

Title Similarity with Normalization

Before string comparison, the _normalize method (lines 26-44) sanitizes both titles by converting to lower-case and removing "official", "video", punctuation, and common filler words. The _score_title_similarity method (lines 78-97) then measures similarity using difflib.SequenceMatcher.

If token overlap falls below 30%, the similarity score is halved to penalize weak matches. The final title score is multiplied by 0.3 to fit the weighted scale. This normalization ensures that "Song Name (Official Video)" matches against "Song Name" with high confidence.

Album Name Verification

The _score_album_bonus method (lines 99-117) adds a flat +0.1 to the total when the Spotify album name appears within the YouTube Music album string. This optional boost provides extra confidence when matching album tracks, helping distinguish between studio versions and compilation or live recordings.

Match Thresholds and Quality Controls

Strict Mode Implementation

The _calculate_match_score method accepts a strict boolean parameter that determines the acceptance threshold. When strict=True, the method requires a score of 0.7 or higher to return a valid match; otherwise, the threshold drops to 0.6. If the final score falls below the selected threshold, the method returns 0, signaling the caller to discard the result.

Early Cutoff Heuristic

To prevent low-quality matches from proceeding, the calculator implements an early cutoff: if title_score < 0.1, the total score caps at 0.5 regardless of how well the duration or artists match. This heuristic prevents instrumental versions or remixes from scoring highly when the titles diverge significantly.

Debug Logging

Each sub-score and the final total are logged for debugging purposes, enabling developers to trace why specific matches succeeded or failed during the selection process.

Practical Implementation Examples


# Manual scoring example

from spotifysaver.services.score_match_calculator import ScoreMatchCalculator
from spotifysaver.models.track import Track

yt_result = {
    "title": "Imagine (Official Video)",
    "duration_seconds": 183,
    "artists": [{"name": "John Lennon"}],
    "album": {"name": "Imagine"},
    "videoId": "abcd1234"
}

track = Track(
    number=1,
    total_tracks=10,
    name="Imagine",
    duration=183,
    uri="spotify:track:xyz",
    artists=["John Lennon"],
    album_artist=["John Lennon"],
    release_date="1971-09-09",
    disc_number=1,
    source_type="album",
    album_name="Imagine",
)

scorer = ScoreMatchCalculator()
score = scorer._calculate_match_score(yt_result, track, strict=False)
print("Match score:", score)   # → 1.0 (perfect duration, artist, title, album)

# Integration through the searcher service

from spotifysaver.services.youtube_api import YoutubeMusicSearcher

searcher = YoutubeMusicSearcher()
youtube_url = searcher.search_track(track)   # Returns best YouTube Music URL

print(youtube_url)

Summary

  • ScoreMatchCalculator applies a multi-factor weighted algorithm to compare Spotify tracks against YouTube Music candidates, implemented in spotifysaver/services/score_match_calculator.py.
  • The four scoring components (duration, artist overlap, title similarity, album bonus) sum to a maximum potential score of 1.0.
  • Duration matching uses a 2-second tolerance with linear decay, while title matching employs difflib.SequenceMatcher on normalized strings.
  • Strict mode raises the acceptance threshold from 0.6 to 0.7, and an early cutoff caps scores at 0.5 when title similarity falls below 0.1.
  • The YoutubeMusicSearcher service orchestrates the calculator, filtering and sorting results to return the single best match URL.

Frequently Asked Questions

What algorithm does ScoreMatchCalculator use for title comparison?

The calculator uses Python's difflib.SequenceMatcher to measure string similarity between normalized titles. The _normalize method strips "official", "video", punctuation, and common words before comparison. If token overlap is less than 30%, the similarity score is halved to penalize weak matches.

How does strict mode affect matching behavior?

When strict=True is passed to _calculate_match_score, the acceptance threshold increases from 0.6 to 0.7. This mode is useful when migrating curated playlists where false positives are costlier than missed tracks. If the calculated score falls below the threshold, the method returns 0, indicating no acceptable match was found.

Why does the duration scorer use a 2-second tolerance?

The 2-second tolerance accounts for platform-specific metadata variations, such as differing intro/outro silences or variations in metadata rounding. The linear decay formula (max(0, 1-(diff/5))*0.3) provides a smooth penalty curve rather than a hard cutoff, allowing slightly different track versions to remain competitive candidates.

Can the scoring weights be customized in the current implementation?

The weights (0.3 for duration, 0.3 for title, 0.3 for artist, plus 0.1 album bonus) are hardcoded as constants within the ScoreMatchCalculator class methods. To modify these weights, developers must edit the return values in _score_duration, _score_artist_overlap, and _score_title_similarity directly within spotifysaver/services/score_match_calculator.py.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →