feat(lyrics): simpmusic lyrics provider (#1820)

* feat(lyrics): simpmusic lyrics provider
This commit is contained in:
riccardo
2026-03-11 09:04:55 +01:00
committed by GitHub
parent f51d3d5711
commit 16ac536f93
4 changed files with 138 additions and 0 deletions
+8
View File
@@ -5,6 +5,10 @@ import { getLyricsBySongId as getGenius, getSearchResults as searchGenius } from
import { getLyricsBySongId as getLrcLib, getSearchResults as searchLrcLib } from './lrclib'; import { getLyricsBySongId as getLrcLib, getSearchResults as searchLrcLib } from './lrclib';
import { getLyricsBySongId as getNetease, getSearchResults as searchNetease } from './netease'; import { getLyricsBySongId as getNetease, getSearchResults as searchNetease } from './netease';
import { orderSearchResults } from './shared'; import { orderSearchResults } from './shared';
import {
getLyricsBySongId as getSimpMusic,
getSearchResults as searchSimpMusic,
} from './simpmusic';
import { Song } from '/@/shared/types/domain-types'; import { Song } from '/@/shared/types/domain-types';
@@ -12,6 +16,7 @@ export enum LyricSource {
GENIUS = 'Genius', GENIUS = 'Genius',
LRCLIB = 'lrclib.net', LRCLIB = 'lrclib.net',
NETEASE = 'NetEase', NETEASE = 'NetEase',
SIMPMUSIC = 'SimpMusic',
} }
export type FullLyricsMetadata = Omit<InternetProviderLyricResponse, 'id' | 'lyrics' | 'source'> & { export type FullLyricsMetadata = Omit<InternetProviderLyricResponse, 'id' | 'lyrics' | 'source'> & {
@@ -66,12 +71,14 @@ const SEARCH_FETCHERS: Record<LyricSource, SearchFetcher> = {
[LyricSource.GENIUS]: searchGenius, [LyricSource.GENIUS]: searchGenius,
[LyricSource.LRCLIB]: searchLrcLib, [LyricSource.LRCLIB]: searchLrcLib,
[LyricSource.NETEASE]: searchNetease, [LyricSource.NETEASE]: searchNetease,
[LyricSource.SIMPMUSIC]: searchSimpMusic,
}; };
const GET_FETCHERS: Record<LyricSource, GetFetcher> = { const GET_FETCHERS: Record<LyricSource, GetFetcher> = {
[LyricSource.GENIUS]: getGenius, [LyricSource.GENIUS]: getGenius,
[LyricSource.LRCLIB]: getLrcLib, [LyricSource.LRCLIB]: getLrcLib,
[LyricSource.NETEASE]: getNetease, [LyricSource.NETEASE]: getNetease,
[LyricSource.SIMPMUSIC]: getSimpMusic,
}; };
const MAX_CACHED_ITEMS = 10; const MAX_CACHED_ITEMS = 10;
@@ -191,6 +198,7 @@ const searchRemoteLyrics = async (params: LyricSearchQuery) => {
[LyricSource.GENIUS]: [], [LyricSource.GENIUS]: [],
[LyricSource.LRCLIB]: [], [LyricSource.LRCLIB]: [],
[LyricSource.NETEASE]: [], [LyricSource.NETEASE]: [],
[LyricSource.SIMPMUSIC]: [],
}; };
for (const item of allSearchResults) { for (const item of allSearchResults) {
results[item.source].push(item); results[item.source].push(item);
+126
View File
@@ -0,0 +1,126 @@
import axios, { AxiosResponse } from 'axios';
import {
InternetProviderLyricResponse,
InternetProviderLyricSearchResponse,
LyricSearchQuery,
LyricSource,
} from '.';
import { orderSearchResults } from './shared';
const API_URL = 'https://api-lyrics.simpmusic.org/v1';
const TIMEOUT_MS = 5000;
export interface SimpMusicLyric {
albumName?: string;
artistName: string;
durationSeconds?: number;
id: string;
plainLyric?: string;
richSyncLyrics?: string;
songTitle: string;
syncedLyrics?: string;
videoId: string;
vote?: number;
}
export interface SimpMusicSearchResponse {
data: SimpMusicLyric[];
success: boolean;
}
export async function getLyricsBySongId(songId: string): Promise<null | string> {
let result: AxiosResponse;
try {
result = await axios.get(`${API_URL}/${songId}`, {
timeout: TIMEOUT_MS,
});
} catch (e) {
console.error('SimpMusic lyrics request errored:', (e as Error)?.message);
return null;
}
const firstLyric = (result.data.data?.[0] ?? null) as null | SimpMusicLyric;
if (!firstLyric) return null;
return firstLyric.syncedLyrics || firstLyric.plainLyric || null;
}
export async function getSearchResults(
params: LyricSearchQuery,
): Promise<InternetProviderLyricSearchResponse[] | null> {
let result: AxiosResponse<SimpMusicSearchResponse>;
if (!params.name) return null;
try {
result = await axios.get<SimpMusicSearchResponse>(`${API_URL}/search`, {
params: {
q: params.name,
},
timeout: TIMEOUT_MS,
});
} catch (e) {
console.error('SimpMusic search errored:', (e as Error)?.message);
return null;
}
if (!result.data?.data) return null;
const songResults: InternetProviderLyricSearchResponse[] = result.data.data.map((song) => ({
artist: song.artistName,
id: song.videoId,
isSync: song.syncedLyrics ? true : false,
name: song.songTitle,
source: LyricSource.SIMPMUSIC,
}));
return orderSearchResults({ params, results: songResults });
}
export async function query(
params: LyricSearchQuery,
): Promise<InternetProviderLyricResponse | null> {
if (!params.name) return null;
let search: AxiosResponse<SimpMusicSearchResponse>;
try {
search = await axios.get<SimpMusicSearchResponse>(`${API_URL}/search`, {
params: {
q: params.name,
},
timeout: TIMEOUT_MS,
});
} catch (e) {
console.error('SimpMusic search errored:', (e as Error).message);
return null;
}
const first = search.data?.data?.[0];
if (!first) return null;
let lyric: AxiosResponse<SimpMusicLyric>;
try {
lyric = await axios.get<SimpMusicLyric>(`${API_URL}/${first.videoId}`, {
timeout: TIMEOUT_MS,
});
} catch (e) {
console.error('SimpMusic lyrics fetch errored:', (e as Error).message);
return null;
}
const lyrics = lyric.data.syncedLyrics || lyric.data.plainLyric || null;
if (!lyrics) return null;
return {
artist: lyric.data.artistName,
id: lyric.data.videoId,
lyrics,
name: lyric.data.songTitle,
source: LyricSource.SIMPMUSIC,
};
}
@@ -167,6 +167,9 @@ const getSettingsProperties = (): SettingsProperties => {
'settings.lyrics.sources.netease': ignoreWeb( 'settings.lyrics.sources.netease': ignoreWeb(
settings.lyrics.sources.includes(LyricSource.NETEASE), settings.lyrics.sources.includes(LyricSource.NETEASE),
), ),
'settings.lyrics.sources.simpmusic': ignoreWeb(
settings.lyrics.sources.includes(LyricSource.SIMPMUSIC),
),
'settings.minimizeToTray': ignoreWeb(settings.window.minimizeToTray), 'settings.minimizeToTray': ignoreWeb(settings.window.minimizeToTray),
// 'settings.musicBrainz': settings.general.musicBrainz, // 'settings.musicBrainz': settings.general.musicBrainz,
'settings.nativeAspectRatio': settings.general.nativeAspectRatio, 'settings.nativeAspectRatio': settings.general.nativeAspectRatio,
+1
View File
@@ -1346,6 +1346,7 @@ export enum LyricSource {
GENIUS = 'Genius', GENIUS = 'Genius',
LRCLIB = 'lrclib.net', LRCLIB = 'lrclib.net',
NETEASE = 'NetEase', NETEASE = 'NetEase',
SIMPMUSIC = 'SimpMusic',
} }
export type AlbumRadioArgs = BaseEndpointArgs & { export type AlbumRadioArgs = BaseEndpointArgs & {