Skip to content

Commit

Permalink
Merge pull request #2905 from metabrainz/apple-music-search
Browse files Browse the repository at this point in the history
Improve Apple Music search with fuzzy matching
  • Loading branch information
MonkeyDo committed Jun 24, 2024
2 parents a1f2533 + 4b3c3fa commit e217d89
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 7 deletions.
44 changes: 38 additions & 6 deletions frontend/js/src/common/brainzplayer/AppleMusicPlayer.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import * as React from "react";
import { get as _get, isString } from "lodash";
import { get as _get, deburr, escapeRegExp, isString } from "lodash";
import { faApple } from "@fortawesome/free-brands-svg-icons";
import { Link } from "react-router-dom";
import fuzzysort from "fuzzysort";
import {
getArtistName,
getTrackName,
Expand Down Expand Up @@ -178,8 +179,9 @@ export default class AppleMusicPlayer
const { onTrackNotFound } = this.props;
const trackName = getTrackName(listen);
const artistName = getArtistName(listen);
const releaseName = _get(listen, "track_metadata.release_name");
const searchTerm = `${trackName} ${artistName} ${releaseName}`;
// 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();
return;
Expand All @@ -189,11 +191,41 @@ export default class AppleMusicPlayer
`/v1/catalog/{{storefrontId}}/search`,
{ term: searchTerm, types: "songs" }
);
const apple_music_id = response?.data?.results?.songs?.data?.[0]?.id;
if (apple_music_id) {
await this.playAppleMusicId(apple_music_id);
// Remove accents from both the search term and the API results
const trackNameWithoutAccents = deburr(trackName);
const candidateMatches = response?.data?.results?.songs?.data.map(
(candidate) => ({
...candidate,
attributes: {
...candidate.attributes,
name: deburr(candidate.attributes.name),
},
})
);
// 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(
trackNameWithoutAccents,
candidateMatches,
{
key: "attributes.name",
limit: 1,
}
);
if (fruzzyMatches[0]) {
await this.playAppleMusicId(fruzzyMatches[0].obj.id);
return;
}
// No good match, onTrackNotFound will be called in the code block below
} catch (error) {
console.debug("Apple Music API request failed:", error);
}
Expand Down
58 changes: 57 additions & 1 deletion frontend/js/src/utils/musickit.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,58 @@ declare namespace MusicKit {
*/
function getInstance(): MusicKitInstance;

interface Song {
attributes: {
albumName: string;
artistName: string;
artwork: {
bgColor: string;
height: number;
textColor1: string;
textColor2: string;
textColor3: string;
textColor4: string;
url: string;
width: number;
};
composerName: string;
discNumber: number;
durationInMillis: number;
genreNames: string[];
hasCredits: boolean;
hasLyrics: boolean;
isAppleDigitalMaster: boolean;
isrc: string;
name: string;
playParams: {
id: string;
kind: string;
};
previews: [
{
url: string;
}
];
releaseDate: string;
trackNumber: number;
url: string;
};
href: string;
id: string;
type: string;
}

interface APISearchResult {
meta: {};
results: {
songs: {
data: Song[];
href: string;
next: string;
};
};
}

/**
* This class represents the Apple Music API.
*/
Expand All @@ -26,7 +78,11 @@ declare namespace MusicKit {
* @param options An object with additional options to control how requests are made
* directly to the Apple Music API.
*/
music(path: string, parameters?: any, options?: any): Promise<any>;
music(
path: string,
parameters?: any,
options?: any
): Promise<{ data: APISearchResult }>;
}

/**
Expand Down
6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
"external-svg-loader": "^1.7.1",
"fetch-retry": "^5.0.3",
"file-saver": "^2.0.5",
"fuzzysort": "^3.0.1",
"he": "^1.2.0",
"highlight.js": "^11.6.0",
"html2canvas": "^1.4.1",
Expand Down

0 comments on commit e217d89

Please sign in to comment.