From 53193cda981046d50e7d4ad3225750e650e10b87 Mon Sep 17 00:00:00 2001 From: Eric <51269470+Laesx@users.noreply.github.com> Date: Wed, 16 Apr 2025 16:12:04 +0200 Subject: [PATCH] feat(Synced-Lyrics): Also search for lyrics with the original title language (#3206) * Add tags array for song info * Implement original title and original artist search for LRCBLib * comment cleanup * Check if microformat.tags is an array --- src/plugins/synced-lyrics/providers/LRCLib.ts | 56 ++++++++++++++++++- src/plugins/synced-lyrics/types.ts | 2 +- src/providers/song-info.ts | 3 + 3 files changed, 58 insertions(+), 3 deletions(-) diff --git a/src/plugins/synced-lyrics/providers/LRCLib.ts b/src/plugins/synced-lyrics/providers/LRCLib.ts index db44ba8a..9e3acbe9 100644 --- a/src/plugins/synced-lyrics/providers/LRCLib.ts +++ b/src/plugins/synced-lyrics/providers/LRCLib.ts @@ -11,10 +11,13 @@ export class LRCLib implements LyricProvider { async search({ title, + alternativeTitle, artist, album, songDuration, + tags, }: SearchSongInfo): Promise { + let query = new URLSearchParams({ artist_name: artist, track_name: title, @@ -42,7 +45,9 @@ export class LRCLib implements LyricProvider { return null; } - query = new URLSearchParams({ q: title }); + // Try to search with the alternative title (original language) + const trackName = alternativeTitle || title; + query = new URLSearchParams({ q: `${trackName}` }); url = `${this.baseUrl}/api/search?${query.toString()}`; response = await fetch(url); @@ -54,6 +59,22 @@ export class LRCLib implements LyricProvider { if (!Array.isArray(data)) { throw new Error(`Expected an array, instead got ${typeof data}`); } + + // If still no results, try with the original title as fallback + if (data.length === 0 && alternativeTitle) { + query = new URLSearchParams({ q: title }); + url = `${this.baseUrl}/api/search?${query.toString()}`; + + response = await fetch(url); + if (!response.ok) { + throw new Error(`bad HTTPStatus(${response.statusText})`); + } + + data = (await response.json()) as LRCLIBSearchResponse; + if (!Array.isArray(data)) { + throw new Error(`Expected an array, instead got ${typeof data}`); + } + } } const filteredResults = []; @@ -63,6 +84,7 @@ export class LRCLib implements LyricProvider { const artists = artist.split(/[&,]/g).map((i) => i.trim()); const itemArtists = artistName.split(/[&,]/g).map((i) => i.trim()); + // Try to match using artist name first const permutations = []; for (const artistA of artists) { for (const artistB of itemArtists) { @@ -76,10 +98,40 @@ export class LRCLib implements LyricProvider { } } - const ratio = Math.max( + let ratio = Math.max( ...permutations.map(([x, y]) => jaroWinkler(x, y)), ); + // If direct artist match is below threshold and we have tags, try matching with tags + if (ratio <= 0.9 && tags && tags.length > 0) { + // Filter out the artist from tags to avoid duplicate comparisons + const filteredTags = tags.filter(tag => tag.toLowerCase() !== artist.toLowerCase()); + + const tagPermutations = []; + // Compare each tag with each item artist + for (const tag of filteredTags) { + for (const itemArtist of itemArtists) { + tagPermutations.push([tag.toLowerCase(), itemArtist.toLowerCase()]); + } + } + + // Compare each item artist with each tag + for (const itemArtist of itemArtists) { + for (const tag of filteredTags) { + tagPermutations.push([itemArtist.toLowerCase(), tag.toLowerCase()]); + } + } + + if (tagPermutations.length > 0) { + const tagRatio = Math.max( + ...tagPermutations.map(([x, y]) => jaroWinkler(x, y)), + ); + + // Use the best match ratio between direct artist match and tag match + ratio = Math.max(ratio, tagRatio); + } + } + if (ratio <= 0.9) continue; filteredResults.push(item); } diff --git a/src/plugins/synced-lyrics/types.ts b/src/plugins/synced-lyrics/types.ts index b33e16ab..37686222 100644 --- a/src/plugins/synced-lyrics/types.ts +++ b/src/plugins/synced-lyrics/types.ts @@ -32,7 +32,7 @@ export interface LyricResult { } // prettier-ignore -export type SearchSongInfo = Pick; +export type SearchSongInfo = Pick; export interface LyricProvider { name: string; diff --git a/src/providers/song-info.ts b/src/providers/song-info.ts index ee8b084f..50ea6b5c 100644 --- a/src/providers/song-info.ts +++ b/src/providers/song-info.ts @@ -42,6 +42,7 @@ export interface SongInfo { videoId: string; playlistId?: string; mediaType: MediaType; + tags?: string[]; } // Grab the native image using the src @@ -83,6 +84,7 @@ const handleData = async ( videoId: '', playlistId: '', mediaType: MediaType.Audio, + tags: [], } satisfies SongInfo; const microformat = data.microformat?.microformatDataRenderer; @@ -96,6 +98,7 @@ const handleData = async ( songInfo.alternativeTitle = microformat.linkAlternates.find( (link) => link.title, )?.title; + songInfo.tags = Array.isArray(microformat.tags) ? microformat.tags : []; } const { videoDetails } = data;