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
This commit is contained in:
Eric
2025-04-16 16:12:04 +02:00
committed by GitHub
parent 5853523074
commit 53193cda98
3 changed files with 58 additions and 3 deletions

View File

@ -11,10 +11,13 @@ export class LRCLib implements LyricProvider {
async search({
title,
alternativeTitle,
artist,
album,
songDuration,
tags,
}: SearchSongInfo): Promise<LyricResult | null> {
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);
}

View File

@ -32,7 +32,7 @@ export interface LyricResult {
}
// prettier-ignore
export type SearchSongInfo = Pick<SongInfo, 'title' | 'artist' | 'album' | 'songDuration' | 'videoId'>;
export type SearchSongInfo = Pick<SongInfo, 'title' | 'alternativeTitle' | 'artist' | 'album' | 'songDuration' | 'videoId' | 'tags'>;
export interface LyricProvider {
name: string;

View File

@ -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;