// Package matcher matches song results from external agents (Last.fm, Deezer,// etc.) to tracks in the local music library, prioritizing accuracy over recall.//// It exposes a single [Matcher] type with two entry points that share the same// matching algorithm://// - [Matcher.MatchSongs] returns an ordered, deduplicated slice of library// tracks, capped at a requested count. Use it when presenting "similar// songs" results to a client.// - [Matcher.MatchSongsIndexed] returns a map from input-song index to matched// track, with no deduplication. Use it when the caller needs to correlate// each result back to its input position (e.g. to attach a per-song// similarity score).//// # Algorithm Overview//// Each input song is resolved to its best-matching library track using four// strategies, applied in priority order. A song matched by a higher-priority// strategy is never reconsidered by a lower-priority one://// 1. Direct ID match: songs with an ID are matched to a MediaFile by ID.// 2. MusicBrainz Recording ID (MBID) match: songs with an MBID are matched to// tracks with the same mbz_recording_id.// 3. ISRC match: songs with an ISRC are matched to tracks carrying that ISRC tag.// 4. Title+Artist fuzzy match: remaining songs are matched by fuzzy string// comparison with metadata-specificity scoring (see below).//// Priority order is ID > MBID > ISRC > Title+Artist, so more reliable// identifiers always take precedence over fuzzy text matching. Missing tracks// (those no longer present on disk) are never matched.//// # Fuzzy Matching Details//// Title+artist matching uses Jaro-Winkler similarity, with a threshold// configurable via conf.Server.Matcher.FuzzyThreshold (default 85%). A library// track must clear the title threshold to be considered. Candidates that clear// it are ranked by, in order://// 1. Title similarity (Jaro-Winkler score, 0.0–1.0)// 2. Duration proximity (closer duration scores higher; 1.0 when the agent// reports no duration)// 3. Specificity level (0–5, based on metadata precision; higher is better)// 4. Artist overlap (how many of the song's artists the track credits; more// shared artists is better)// 5. Preferred-track flag (enabled by conf.Server.Matcher.PreferStarred;// prioritizes tracks that are starred or rated >= 4, but only among// candidates of equal specificity and overlap)// 6. Album similarity (Jaro-Winkler, as the final tiebreaker)//// The specificity levels, from most to least specific, are://// Level 5: Title + Artist identity + Album MBID// Level 4: Title + Artist identity + Album name (fuzzy)// Level 3: Title + Artist name + Album name (fuzzy)// Level 2: Title + Artist identity// Level 1: Title + Artist name// Level 0: Title only//// "Artist identity" is a match on the artist's Navidrome ID (the strongest signal,// when a source supplies one) or its MBID. A plain name match is the weaker fallback// used for an artist with no identity match (e.g. a cover credited to a different// artist of the same name).//// The title phase always requires an agent artist to scope the library query, so// Level 0 does not mean "no artist": it applies when a candidate matches on title// but its own artist differs from the query's (e.g. a cover or a featured-artist// credit), leaving the title as the only shared field.//// A song may carry several artists, and the title phase scopes candidate tracks by// ANY of them: a track credited to at least one shared artist is considered. When a// source supplies a Navidrome artist ID, that artist is matched directly, skipping// name/MBID resolution. Among equally specific candidates, the one sharing more of// the song's artists wins, so a track crediting every collaborator outranks one// crediting only a single artist.//// Each input song is scored independently, so two songs with the same title and// artist but different durations can resolve to different library tracks (each// matches the track closest to its own duration).//// # Examples//// All examples below exercise the title+artist phase, where the interesting// behavior lives. (Identifier phases — ID, MBID, ISRC — are exact lookups that// always win over fuzzy matching; they need no illustration.)//// Title threshold — a near-miss title still matches; an exact-only threshold// rejects it://// Agent returns: {Name: "Bohemian Rhapsody", Artist: "Queen"}// Library has: {ID: "t1", Title: "Bohemian Rhapsody - Remastered", Artist: "Queen"}// With threshold 85%: match succeeds (similarity ~0.87)// With threshold 100%: no match (not an exact title)//// Specificity ranking — among candidates that clear the title threshold, a// better album match wins://// Agent returns: {Name: "Enjoy the Silence", Artist: "Depeche Mode", Album: "Violator"}// Library has:// {ID: "t1", Title: "Enjoy the Silence", Artist: "Depeche Mode", Album: "101"} // Level 1// {ID: "t2", Title: "Enjoy the Silence", Artist: "Depeche Mode", Album: "Violator"} // Level 3// Result: t2 (Level 3 beats Level 1 on the album match)//// Duration tiebreak — with title and artist equal, the closest duration wins,// so two near-identical input songs can resolve to different tracks://// Agent returns:// {Name: "Untitled", Artist: "Interpol", Duration: 245000} // 4:05// {Name: "Untitled", Artist: "Interpol", Duration: 600000} // 10:00 (a live take)// Library has:// {ID: "studio", Title: "Untitled", Artist: "Interpol", Duration: 248} // 4:08// {ID: "live", Title: "Untitled", Artist: "Interpol", Duration: 602} // 10:02// Result: studio for the first song, live for the second//// Preferred track — when conf.Server.Matcher.PreferStarred is enabled, a// starred (or rating >= 4) track is preferred, but only when specificity and// artist overlap are equal. A more specific match always wins regardless of the// preferred flag://// Agent returns: {Name: "Enjoy the Silence", Artist: "Depeche Mode", Album: "Violator"}// Library has:// {ID: "exact", Title: "Enjoy the Silence", Artist: "Depeche Mode", Album: "Violator"} // Level 3// {ID: "starred", Title: "Enjoy the Silence", Artist: "Depeche Mode", Album: "Singles", Starred: true} // Level 1, starred// Result: exact (specificity outranks the starred flag; preferred only breaks ties of equal identity)package matcher