/// <summary> /// Tries to finds matches for the title of a scrobbled track /// </summary> /// <returns>A perfect match if any is found, or null</returns> protected static LibraryTrack MatchTitle(ScrobbledTrack scrobble, IDictionary <string, List <LibraryTrack> > artistTracks, ref List <RelevantLibraryTrack> fuzzyMatches, float artistDistance) { List <LibraryTrack> duplicateTracks; if (artistTracks.TryGetValue(scrobble.TitleKey, out duplicateTracks)) { // Direct title match if (duplicateTracks.Count == 1) { // Perfect match; return immediately return(duplicateTracks[0]); } // Duplicate titles for direct match if (duplicateTracks.Count > 0) { if (fuzzyMatches == null) { fuzzyMatches = new List <RelevantLibraryTrack>(); } foreach (var duplicate in duplicateTracks) { fuzzyMatches.Add(new RelevantLibraryTrack(duplicate)); } } } var minEditDistance = Settings.Default.MinimumEditDistance; // If nothing was found yet, if (duplicateTracks == null || duplicateTracks.Count == 0) { // No direct title match, check for edit distance var titleKey = scrobble.TitleKey; foreach (var trackEntry in artistTracks) { var titleDistance = (float)trackEntry.Key.LevenshteinDistance(titleKey) / titleKey.Length; if (titleDistance < minEditDistance) { if (fuzzyMatches == null) { fuzzyMatches = new List <RelevantLibraryTrack>(); } // The final relevance is the product of the artist and title relevances var relevance = (1 - artistDistance) * (1 - (titleDistance / minEditDistance)); foreach (var duplicate in trackEntry.Value) { fuzzyMatches.Add(new RelevantLibraryTrack(duplicate, relevance)); } } } } // No direct match to return, fuzzy matches added to state return(null); }
/// <summary> /// Tries to finds matches for a scrobbled track /// </summary> /// <param name="scrobble">The scrobble to search for</param> /// <param name="state">The current task's state</param> /// <returns>A perfect match if found, or null</returns> protected static LibraryTrack Find(ScrobbledTrack scrobble, MapState state) { List <RelevantLibraryTrack> fuzzyMatches = null; Dictionary <string, List <LibraryTrack> > artistTracks; if (state.LibraryArtists.TryGetValue(scrobble.ArtistKey, out artistTracks)) { // Direct artist match var track = MatchTitle(scrobble, artistTracks, ref fuzzyMatches); if (track != null) { // Perfect match; return immediately return(track); } } var minEditDistance = Settings.Default.MinimumEditDistance; // If nothing was found yet, if (artistTracks == null || artistTracks.Count == 0 || fuzzyMatches == null || fuzzyMatches.Count == 0) { // No direct artist match, check for edit distance var artistKey = scrobble.ArtistKey; foreach (var entry in state.LibraryArtists) { var titleDistance = (float)entry.Key.LevenshteinDistance(artistKey) / artistKey.Length; if (titleDistance < minEditDistance) { MatchTitle(scrobble, entry.Value, ref fuzzyMatches, titleDistance / minEditDistance); } } } // If any fuzzy matches were found if (fuzzyMatches != null && fuzzyMatches.Count != 0) { state.FuzzyMatches.Add(new FuzzyMatch(scrobble, fuzzyMatches.OrderByDescending(x => x.Relevance).ToArray())); } else { state.NotFound++; } // No direct match to return, fuzzy matches added to state return(null); }
/// <summary> /// Try to update the library track from the scrobble information. /// A COMException will be raised on error. /// </summary> /// <returns>True if the file was updated, false if no update was necessary</returns> public bool TryUpdate(ScrobbledTrack scrobble) { var updated = false; if (PlayCount < scrobble.PlayCount) { PlayCount = scrobble.PlayCount; updated = true; } if (PlayDate < scrobble.WeekLastPlayed) { PlayDate = scrobble.WeekLastPlayed; updated = true; } return(updated); }
/// <summary> /// Tries to finds matches for a scrobbled track /// </summary> /// <param name="scrobble">The scrobble to search for</param> /// <param name="state">The current task's state</param> /// <returns>A perfect match if found, or null</returns> protected static LibraryTrack Find(ScrobbledTrack scrobble, MapState state) { List<RelevantLibraryTrack> fuzzyMatches = null; Dictionary<string, List<LibraryTrack>> artistTracks; if (state.LibraryArtists.TryGetValue(scrobble.ArtistKey, out artistTracks)) { // Direct artist match var track = MatchTitle(scrobble, artistTracks, ref fuzzyMatches); if (track != null) // Perfect match; return immediately return track; } var minEditDistance = Settings.Default.MinimumEditDistance; // If nothing was found yet, if (artistTracks == null || artistTracks.Count == 0 || fuzzyMatches == null || fuzzyMatches.Count == 0) { // No direct artist match, check for edit distance var artistKey = scrobble.ArtistKey; foreach (var entry in state.LibraryArtists) { var titleDistance = (float)entry.Key.LevenshteinDistance(artistKey) / artistKey.Length; if (titleDistance < minEditDistance) MatchTitle(scrobble, entry.Value, ref fuzzyMatches, titleDistance / minEditDistance); } } // If any fuzzy matches were found if (fuzzyMatches != null && fuzzyMatches.Count != 0) state.FuzzyMatches.Enqueue(new FuzzyMatch(scrobble, fuzzyMatches.OrderByDescending(x => x.Relevance).ToArray())); else Interlocked.Increment(ref state.NotFound); // No direct match to return, fuzzy matches added to state return null; }
/// <summary> /// Tries to finds matches for the title of a scrobbled track /// </summary> /// <returns>A perfect match if any is found, or null</returns> protected static LibraryTrack MatchTitle(ScrobbledTrack scrobble, IDictionary <string, List <LibraryTrack> > artistTracks, ref List <RelevantLibraryTrack> fuzzyMatches) { return(MatchTitle(scrobble, artistTracks, ref fuzzyMatches, 0)); }
/// <summary> /// Tries to finds matches for the title of a scrobbled track /// </summary> /// <returns>A perfect match if any is found, or null</returns> protected static LibraryTrack MatchTitle(ScrobbledTrack scrobble, IDictionary<string, List<LibraryTrack>> artistTracks, ref List<RelevantLibraryTrack> fuzzyMatches, float artistDistance) { List<LibraryTrack> duplicateTracks; if (artistTracks.TryGetValue(scrobble.TitleKey, out duplicateTracks)) { // Direct title match if (duplicateTracks.Count == 1) // Perfect match; return immediately return duplicateTracks[0]; // Duplicate titles for direct match if (duplicateTracks.Count > 0) { if (fuzzyMatches == null) fuzzyMatches = new List<RelevantLibraryTrack>(); foreach (var duplicate in duplicateTracks) fuzzyMatches.Add(new RelevantLibraryTrack(duplicate)); } } var minEditDistance = Settings.Default.MinimumEditDistance; // If nothing was found yet, if (duplicateTracks == null || duplicateTracks.Count == 0) { // No direct title match, check for edit distance var titleKey = scrobble.TitleKey; foreach (var trackEntry in artistTracks) { var titleDistance = (float)trackEntry.Key.LevenshteinDistance(titleKey) / titleKey.Length; if (titleDistance < minEditDistance) { if (fuzzyMatches == null) fuzzyMatches = new List<RelevantLibraryTrack>(); // The final relevance is the product of the artist and title relevances var relevance = (1 - artistDistance) * (1 - (titleDistance / minEditDistance)); foreach (var duplicate in trackEntry.Value) fuzzyMatches.Add(new RelevantLibraryTrack(duplicate, relevance)); } } } // No direct match to return, fuzzy matches added to state return null; }
/// <summary> /// Tries to finds matches for the title of a scrobbled track /// </summary> /// <returns>A perfect match if any is found, or null</returns> protected static LibraryTrack MatchTitle(ScrobbledTrack scrobble, IDictionary<string, List<LibraryTrack>> artistTracks, ref List<RelevantLibraryTrack> fuzzyMatches) { return MatchTitle(scrobble, artistTracks, ref fuzzyMatches, 0); }
public FuzzyMatch(ScrobbledTrack scrobble, IEnumerable<RelevantLibraryTrack> matches) { Scrobble = scrobble; Matches = matches; }
public FuzzyMatch(ScrobbledTrack scrobble, IEnumerable <RelevantLibraryTrack> matches) { Scrobble = scrobble; Matches = matches; }