2023-06-05 09:00:12 -07:00
|
|
|
import { UseQueryResult, useQuery } from '@tanstack/react-query';
|
2023-06-05 02:50:01 -07:00
|
|
|
import {
|
|
|
|
|
LyricsQuery,
|
|
|
|
|
QueueSong,
|
|
|
|
|
SynchronizedLyricsArray,
|
|
|
|
|
InternetProviderLyricResponse,
|
2023-06-05 09:00:12 -07:00
|
|
|
FullLyricsMetadata,
|
2023-06-05 02:50:01 -07:00
|
|
|
} from '/@/renderer/api/types';
|
2023-06-02 23:54:34 -07:00
|
|
|
import { QueryHookArgs } from '/@/renderer/lib/react-query';
|
2023-06-05 02:50:01 -07:00
|
|
|
import { getServerById, useLyricsSettings } from '/@/renderer/store';
|
2023-06-02 23:54:34 -07:00
|
|
|
import { queryKeys } from '/@/renderer/api/query-keys';
|
|
|
|
|
import { ServerType } from '/@/renderer/types';
|
2023-06-05 02:50:01 -07:00
|
|
|
import { api } from '/@/renderer/api';
|
|
|
|
|
import isElectron from 'is-electron';
|
2023-06-02 23:54:34 -07:00
|
|
|
|
2023-06-05 02:50:01 -07:00
|
|
|
const lyricsIpc = isElectron() ? window.electron.lyrics : null;
|
|
|
|
|
|
2023-06-07 13:32:21 -07:00
|
|
|
// Match LRC lyrics format by https://github.com/ustbhuangyi/lyric-parser
|
|
|
|
|
// [mm:ss.SSS] text
|
2023-06-05 02:50:01 -07:00
|
|
|
const timeExp = /\[(\d{2,}):(\d{2})(?:\.(\d{2,3}))?]([^\n]+)\n/g;
|
|
|
|
|
|
2023-06-07 13:32:21 -07:00
|
|
|
// Match karaoke lyrics format returned by NetEase
|
|
|
|
|
// [SSS,???] text
|
|
|
|
|
const alternateTimeExp = /\[(\d*),(\d*)]([^\n]+)\n/g;
|
|
|
|
|
|
2023-06-05 02:50:01 -07:00
|
|
|
const formatLyrics = (lyrics: string) => {
|
|
|
|
|
const synchronizedLines = lyrics.matchAll(timeExp);
|
|
|
|
|
const formattedLyrics: SynchronizedLyricsArray = [];
|
|
|
|
|
|
|
|
|
|
for (const line of synchronizedLines) {
|
|
|
|
|
const [, minute, sec, ms, text] = line;
|
|
|
|
|
const minutes = parseInt(minute, 10);
|
|
|
|
|
const seconds = parseInt(sec, 10);
|
|
|
|
|
const milis = ms?.length === 3 ? parseInt(ms, 10) : parseInt(ms, 10) * 10;
|
|
|
|
|
|
|
|
|
|
const timeInMilis = (minutes * 60 + seconds) * 1000 + milis;
|
2023-06-07 13:32:21 -07:00
|
|
|
|
2023-06-05 02:50:01 -07:00
|
|
|
formattedLyrics.push([timeInMilis, text]);
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-07 13:32:21 -07:00
|
|
|
if (formattedLyrics.length > 0) return formattedLyrics;
|
2023-06-05 02:50:01 -07:00
|
|
|
|
2023-06-07 13:32:21 -07:00
|
|
|
const alternateSynchronizedLines = lyrics.matchAll(alternateTimeExp);
|
|
|
|
|
for (const line of alternateSynchronizedLines) {
|
|
|
|
|
const [, timeInMilis, , text] = line;
|
|
|
|
|
const cleanText = text.replaceAll(/\(\d+,\d+\)/g, '');
|
|
|
|
|
formattedLyrics.push([Number(timeInMilis), cleanText]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (formattedLyrics.length > 0) return formattedLyrics;
|
|
|
|
|
|
|
|
|
|
// If no synchronized lyrics were found, return the original lyrics
|
|
|
|
|
return lyrics;
|
2023-06-05 02:50:01 -07:00
|
|
|
};
|
|
|
|
|
|
2023-06-05 09:00:12 -07:00
|
|
|
export const useServerLyrics = (
|
|
|
|
|
args: QueryHookArgs<LyricsQuery>,
|
|
|
|
|
): UseQueryResult<string | null> => {
|
2023-06-02 23:54:34 -07:00
|
|
|
const { query, serverId } = args;
|
|
|
|
|
const server = getServerById(serverId);
|
|
|
|
|
|
|
|
|
|
return useQuery({
|
|
|
|
|
// Note: This currently fetches for every song, even if it shouldn't have
|
|
|
|
|
// lyrics, because for some reason HasLyrics is not exposed. Thus, ignore the error
|
|
|
|
|
onError: () => {},
|
|
|
|
|
queryFn: ({ signal }) => {
|
|
|
|
|
if (!server) throw new Error('Server not found');
|
|
|
|
|
// This should only be called for Jellyfin. Return null to ignore errors
|
|
|
|
|
if (server.type !== ServerType.JELLYFIN) return null;
|
2023-06-05 02:50:01 -07:00
|
|
|
return api.controller.getLyrics({ apiClientProps: { server, signal }, query });
|
|
|
|
|
},
|
|
|
|
|
queryKey: queryKeys.songs.lyrics(server?.id || '', query),
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2023-06-05 09:00:12 -07:00
|
|
|
export const useSongLyrics = (
|
|
|
|
|
args: QueryHookArgs<LyricsQuery>,
|
|
|
|
|
song: QueueSong | undefined,
|
|
|
|
|
): UseQueryResult<FullLyricsMetadata> => {
|
2023-06-05 02:50:01 -07:00
|
|
|
const { query } = args;
|
|
|
|
|
const { fetch } = useLyricsSettings();
|
|
|
|
|
const server = getServerById(song?.serverId);
|
|
|
|
|
|
|
|
|
|
return useQuery({
|
|
|
|
|
cacheTime: 1000 * 60 * 10,
|
|
|
|
|
enabled: !!song && !!server,
|
|
|
|
|
onError: () => {},
|
|
|
|
|
queryFn: async ({ signal }) => {
|
|
|
|
|
if (!server) throw new Error('Server not found');
|
|
|
|
|
if (!song) return null;
|
|
|
|
|
|
|
|
|
|
if (song.lyrics) {
|
|
|
|
|
return {
|
|
|
|
|
artist: song.artists?.[0]?.name,
|
|
|
|
|
lyrics: formatLyrics(song.lyrics),
|
|
|
|
|
name: song.name,
|
2023-06-05 09:00:12 -07:00
|
|
|
remote: false,
|
2023-06-05 02:50:01 -07:00
|
|
|
source: server?.name ?? 'music server',
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (server.type === ServerType.JELLYFIN) {
|
2023-06-07 01:10:24 -07:00
|
|
|
const jfLyrics = await api.controller
|
|
|
|
|
.getLyrics({
|
|
|
|
|
apiClientProps: { server, signal },
|
|
|
|
|
query: { songId: song.id },
|
|
|
|
|
})
|
|
|
|
|
.catch((err) => console.log(err));
|
2023-06-05 02:50:01 -07:00
|
|
|
|
|
|
|
|
if (jfLyrics) {
|
|
|
|
|
return {
|
|
|
|
|
artist: song.artists?.[0]?.name,
|
|
|
|
|
lyrics: jfLyrics,
|
|
|
|
|
name: song.name,
|
2023-06-05 09:00:12 -07:00
|
|
|
remote: false,
|
2023-06-05 02:50:01 -07:00
|
|
|
source: server?.name ?? 'music server',
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (fetch) {
|
|
|
|
|
const remoteLyricsResult: InternetProviderLyricResponse | null =
|
|
|
|
|
await lyricsIpc?.fetchRemoteLyrics(song);
|
|
|
|
|
|
|
|
|
|
if (remoteLyricsResult) {
|
|
|
|
|
return {
|
|
|
|
|
...remoteLyricsResult,
|
|
|
|
|
lyrics: formatLyrics(remoteLyricsResult.lyrics),
|
2023-06-05 09:00:12 -07:00
|
|
|
remote: true,
|
2023-06-05 02:50:01 -07:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
2023-06-02 23:54:34 -07:00
|
|
|
},
|
|
|
|
|
queryKey: queryKeys.songs.lyrics(server?.id || '', query),
|
2023-06-05 02:50:01 -07:00
|
|
|
staleTime: 1000 * 60 * 2,
|
2023-06-02 23:54:34 -07:00
|
|
|
});
|
|
|
|
|
};
|