From 1d872debd7a7f96b34ed715aaac469a4a8220513 Mon Sep 17 00:00:00 2001 From: anshg1214 Date: Sat, 26 Oct 2024 16:55:41 +0000 Subject: [PATCH 1/5] feat: improve apple music fuzzy matching --- .../common/brainzplayer/AppleMusicPlayer.tsx | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/frontend/js/src/common/brainzplayer/AppleMusicPlayer.tsx b/frontend/js/src/common/brainzplayer/AppleMusicPlayer.tsx index ef9ddf8998..04a0c1c64d 100644 --- a/frontend/js/src/common/brainzplayer/AppleMusicPlayer.tsx +++ b/frontend/js/src/common/brainzplayer/AppleMusicPlayer.tsx @@ -197,8 +197,6 @@ export default class AppleMusicPlayer const { onTrackNotFound } = this.props; const trackName = getTrackName(listen); const artistName = getArtistName(listen); - // Album name can give worse results, re;oving it from search terms - // const releaseName = _get(listen, "track_metadata.release_name"); const searchTerm = `${trackName} ${artistName}`; if (!searchTerm) { onTrackNotFound(); @@ -209,38 +207,44 @@ export default class AppleMusicPlayer `/v1/catalog/{{storefrontId}}/search`, { term: searchTerm, types: "songs" } ); + const releaseName = _get(listen, "track_metadata.release_name"); // Remove accents from both the search term and the API results const trackNameWithoutAccents = deburr(trackName); + const releaseNameWithoutAccents = deburr(releaseName); const candidateMatches = response?.data?.results?.songs?.data.map( (candidate) => ({ ...candidate, attributes: { ...candidate.attributes, name: deburr(candidate.attributes.name), + albumName: deburr(candidate.attributes.albumName), }, }) ); - // Check if the first API result is a match - if ( - new RegExp(escapeRegExp(trackNameWithoutAccents), "igu").test( - candidateMatches?.[0]?.attributes.name - ) - ) { - // First result matches track title, assume it's the correct result - await this.playAppleMusicId(candidateMatches[0].id); - return; - } - // Fallback to best fuzzy match based on track title - const fruzzyMatches = fuzzysort.go( + const fuzzyMatches = fuzzysort.go( trackNameWithoutAccents, candidateMatches, { key: "attributes.name", - limit: 1, } ); - if (fruzzyMatches[0]) { - await this.playAppleMusicId(fruzzyMatches[0].obj.id); + + const matchWithAlbum = fuzzyMatches.find((match) => { + const albumMatch = fuzzysort.single( + releaseNameWithoutAccents, + match.obj.attributes.albumName + ); + return albumMatch && albumMatch.score > 0.8; + }); + + if (matchWithAlbum) { + await this.playAppleMusicId(matchWithAlbum.obj.id); + return; + } + + // If no match found with album, play the best track match + if (fuzzyMatches[0]) { + await this.playAppleMusicId(fuzzyMatches[0].obj.id); return; } // No good match, onTrackNotFound will be called in the code block below From f88076ac72d5e81feb5fcf8234f56c79a3185a81 Mon Sep 17 00:00:00 2001 From: anshg1214 Date: Thu, 7 Nov 2024 05:42:27 +0000 Subject: [PATCH 2/5] feat: Improve Performance of fuzzy search --- .../common/brainzplayer/AppleMusicPlayer.tsx | 44 ++++++++++--------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/frontend/js/src/common/brainzplayer/AppleMusicPlayer.tsx b/frontend/js/src/common/brainzplayer/AppleMusicPlayer.tsx index 04a0c1c64d..e69098f916 100644 --- a/frontend/js/src/common/brainzplayer/AppleMusicPlayer.tsx +++ b/frontend/js/src/common/brainzplayer/AppleMusicPlayer.tsx @@ -207,10 +207,9 @@ export default class AppleMusicPlayer `/v1/catalog/{{storefrontId}}/search`, { term: searchTerm, types: "songs" } ); - const releaseName = _get(listen, "track_metadata.release_name"); - // Remove accents from both the search term and the API results - const trackNameWithoutAccents = deburr(trackName); + const releaseName = _get(listen, "track_metadata.release_name", ""); const releaseNameWithoutAccents = deburr(releaseName); + const trackNameWithoutAccents = deburr(trackName); const candidateMatches = response?.data?.results?.songs?.data.map( (candidate) => ({ ...candidate, @@ -221,25 +220,29 @@ export default class AppleMusicPlayer }, }) ); - const fuzzyMatches = fuzzysort.go( - trackNameWithoutAccents, - candidateMatches, - { - key: "attributes.name", - } - ); - const matchWithAlbum = fuzzyMatches.find((match) => { - const albumMatch = fuzzysort.single( - releaseNameWithoutAccents, - match.obj.attributes.albumName + let fuzzyMatches; + if (releaseNameWithoutAccents) { + // If we have a release name, search for both track and album + fuzzyMatches = fuzzysort.go( + `${trackNameWithoutAccents} ${releaseNameWithoutAccents}`, + candidateMatches, + { + keys: ["attributes.name", "attributes.albumName"], + scoreFn: (a) => { + const NO_MATCH = -Infinity; + const trackScore = a[0]?.score ?? NO_MATCH; + const albumScore = a[1]?.score ?? NO_MATCH; + + return trackScore + (albumScore > 0 ? albumScore * 0.8 : 0); + }, + } ); - return albumMatch && albumMatch.score > 0.8; - }); - - if (matchWithAlbum) { - await this.playAppleMusicId(matchWithAlbum.obj.id); - return; + } else { + // Otherwise just search for track name + fuzzyMatches = fuzzysort.go(trackNameWithoutAccents, candidateMatches, { + key: "attributes.name", + }); } // If no match found with album, play the best track match @@ -249,6 +252,7 @@ export default class AppleMusicPlayer } // No good match, onTrackNotFound will be called in the code block below } catch (error) { + // eslint-disable-next-line no-console console.debug("Apple Music API request failed:", error); } onTrackNotFound(); From 2338b9e1687263e3fa9bed55633cd27e4ce046bf Mon Sep 17 00:00:00 2001 From: anshg1214 Date: Fri, 22 Nov 2024 08:59:16 +0000 Subject: [PATCH 3/5] feat: Improve Mapping --- .../js/src/common/brainzplayer/AppleMusicPlayer.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/frontend/js/src/common/brainzplayer/AppleMusicPlayer.tsx b/frontend/js/src/common/brainzplayer/AppleMusicPlayer.tsx index e69098f916..c49cbd21ff 100644 --- a/frontend/js/src/common/brainzplayer/AppleMusicPlayer.tsx +++ b/frontend/js/src/common/brainzplayer/AppleMusicPlayer.tsx @@ -239,6 +239,16 @@ export default class AppleMusicPlayer } ); } else { + // Check if the first API result is a match + if ( + new RegExp(escapeRegExp(trackNameWithoutAccents), "igu").test( + candidateMatches?.[0]?.attributes.name + ) + ) { + // First result matches track title, assume it's the correct result + await this.playAppleMusicId(candidateMatches[0].id); + return; + } // Otherwise just search for track name fuzzyMatches = fuzzysort.go(trackNameWithoutAccents, candidateMatches, { key: "attributes.name", From 60ddb11fd33b29eeb9655325fd32dd373ce975f1 Mon Sep 17 00:00:00 2001 From: anshg1214 Date: Fri, 22 Nov 2024 09:00:24 +0000 Subject: [PATCH 4/5] chore: Update fuzzysearch --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 79a0374f63..3fec661890 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46,7 +46,7 @@ "fetch-retry": "^5.0.3", "file-saver": "^2.0.5", "fuse.js": "^7.0.0", - "fuzzysort": "^3.0.1", + "fuzzysort": "^3.1.0", "he": "^1.2.0", "highlight.js": "^11.6.0", "html2canvas": "^1.4.1", @@ -9426,9 +9426,9 @@ } }, "node_modules/fuzzysort": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/fuzzysort/-/fuzzysort-3.0.1.tgz", - "integrity": "sha512-jpiSrv+j2GwtI13z3vzdBAD5m7iVH23spSZwJrD657dfr6jUV2Jyc5YuSPKirmTp9OqnJbHudPIBGIId1bCWmA==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fuzzysort/-/fuzzysort-3.1.0.tgz", + "integrity": "sha512-sR9BNCjBg6LNgwvxlBd0sBABvQitkLzoVY9MYYROQVX/FvfJ4Mai9LsGhDgd8qYdds0bY77VzYd5iuB+v5rwQQ==" }, "node_modules/gensync": { "version": "1.0.0-beta.2", diff --git a/package.json b/package.json index 1eb2de6116..b9595668ed 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "fetch-retry": "^5.0.3", "file-saver": "^2.0.5", "fuse.js": "^7.0.0", - "fuzzysort": "^3.0.1", + "fuzzysort": "^3.1.0", "he": "^1.2.0", "highlight.js": "^11.6.0", "html2canvas": "^1.4.1", From 519af974f525a72b1ac7d59d053d6ea95b90dbc4 Mon Sep 17 00:00:00 2001 From: anshg1214 Date: Fri, 22 Nov 2024 09:21:55 +0000 Subject: [PATCH 5/5] feat: Handle cases where track with releaseName is not mapped --- .../common/brainzplayer/AppleMusicPlayer.tsx | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/frontend/js/src/common/brainzplayer/AppleMusicPlayer.tsx b/frontend/js/src/common/brainzplayer/AppleMusicPlayer.tsx index c49cbd21ff..3793389f3b 100644 --- a/frontend/js/src/common/brainzplayer/AppleMusicPlayer.tsx +++ b/frontend/js/src/common/brainzplayer/AppleMusicPlayer.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import { get as _get, deburr, escapeRegExp, isString } from "lodash"; +import { get as _get, escapeRegExp, isString } from "lodash"; import { faApple } from "@fortawesome/free-brands-svg-icons"; import { Link } from "react-router-dom"; import fuzzysort from "fuzzysort"; @@ -208,24 +208,22 @@ export default class AppleMusicPlayer { term: searchTerm, types: "songs" } ); const releaseName = _get(listen, "track_metadata.release_name", ""); - const releaseNameWithoutAccents = deburr(releaseName); - const trackNameWithoutAccents = deburr(trackName); const candidateMatches = response?.data?.results?.songs?.data.map( (candidate) => ({ ...candidate, attributes: { ...candidate.attributes, - name: deburr(candidate.attributes.name), - albumName: deburr(candidate.attributes.albumName), + name: candidate.attributes.name, + albumName: candidate.attributes.albumName, }, }) ); let fuzzyMatches; - if (releaseNameWithoutAccents) { + if (releaseName) { // If we have a release name, search for both track and album fuzzyMatches = fuzzysort.go( - `${trackNameWithoutAccents} ${releaseNameWithoutAccents}`, + `${trackName} ${releaseName}`, candidateMatches, { keys: ["attributes.name", "attributes.albumName"], @@ -238,10 +236,11 @@ export default class AppleMusicPlayer }, } ); - } else { + } + if (!fuzzyMatches || !fuzzyMatches.length) { // Check if the first API result is a match if ( - new RegExp(escapeRegExp(trackNameWithoutAccents), "igu").test( + new RegExp(escapeRegExp(trackName), "igu").test( candidateMatches?.[0]?.attributes.name ) ) { @@ -250,7 +249,7 @@ export default class AppleMusicPlayer return; } // Otherwise just search for track name - fuzzyMatches = fuzzysort.go(trackNameWithoutAccents, candidateMatches, { + fuzzyMatches = fuzzysort.go(trackName, candidateMatches, { key: "attributes.name", }); }