feishin/src/renderer/features/lyrics/lyrics.tsx

138 lines
4.2 KiB
TypeScript
Raw Normal View History

import { useEffect, useRef, useState } from 'react';
import isElectron from 'is-electron';
2023-05-22 17:38:31 -07:00
import { ErrorBoundary } from 'react-error-boundary';
import { ErrorFallback } from '/@/renderer/features/action-required';
2023-06-03 07:15:02 -07:00
import { getServerById, useCurrentServer, useCurrentSong } from '/@/renderer/store';
2023-06-02 23:54:34 -07:00
import { SynchronizedLyrics } from './synchronized-lyrics';
2023-05-22 17:38:31 -07:00
import { UnsynchronizedLyrics } from '/@/renderer/features/lyrics/unsynchronized-lyrics';
import { LyricLine } from '/@/renderer/features/lyrics/lyric-line';
2023-06-02 23:54:34 -07:00
import { Center, Group } from '@mantine/core';
import { RiInformationFill } from 'react-icons/ri';
import { TextTitle } from '/@/renderer/components';
2023-06-03 00:39:50 -07:00
import { LyricsResponse, SynchronizedLyricsArray } from '/@/renderer/api/types';
2023-06-02 23:54:34 -07:00
import { useSongLyrics } from '/@/renderer/features/lyrics/queries/lyric-query';
2023-05-22 17:38:31 -07:00
const lyrics = isElectron() ? window.electron.lyrics : null;
const ipc = isElectron() ? window.electron.ipc : null;
2023-05-22 17:38:31 -07:00
// use by https://github.com/ustbhuangyi/lyric-parser
2023-05-22 17:38:31 -07:00
const timeExp = /\[(\d{2,}):(\d{2})(?:\.(\d{2,3}))?]([^\n]+)\n/g;
export const Lyrics = () => {
const currentSong = useCurrentSong();
2023-06-03 07:15:02 -07:00
const currentServer = getServerById(currentSong?.serverId);
2023-05-22 17:38:31 -07:00
const [override, setOverride] = useState<string | null>(null);
const [source, setSource] = useState<string | null>(null);
2023-06-03 00:39:50 -07:00
const [songLyrics, setSongLyrics] = useState<LyricsResponse | null>(null);
2023-06-02 23:54:34 -07:00
const remoteLyrics = useSongLyrics({
query: { songId: currentSong?.id ?? '' },
serverId: currentServer?.id,
});
const songRef = useRef<string | null>(null);
useEffect(() => {
2023-06-03 10:12:46 -07:00
lyrics?.remoteLyricsListener(
2023-06-03 07:15:02 -07:00
(_event: any, songName: string, lyricSource: string, lyric: string) => {
if (songName === songRef.current) {
setSource(lyricSource);
setOverride(lyric);
}
},
);
return () => {
ipc?.removeAllListeners('lyric-get');
};
}, []);
useEffect(() => {
2023-06-03 07:15:02 -07:00
const hasTaggedLyrics = currentSong && currentSong.lyrics;
const hasLyricsResponse =
!remoteLyrics.isLoading && remoteLyrics?.isSuccess && remoteLyrics?.data !== null;
if (!hasTaggedLyrics && !hasLyricsResponse) {
lyrics?.fetchRemoteLyrics(currentSong);
}
songRef.current = currentSong?.name ?? null;
setOverride(null);
setSource(null);
2023-06-03 07:15:02 -07:00
}, [currentSong, remoteLyrics.isLoading, remoteLyrics?.data, remoteLyrics?.isSuccess]);
useEffect(() => {
let lyrics: string | null = null;
2023-05-22 17:38:31 -07:00
if (currentSong?.lyrics) {
lyrics = currentSong.lyrics;
2023-06-03 07:15:02 -07:00
setSource(currentSong?.name ?? 'music server');
} else if (override) {
lyrics = override;
2023-06-03 00:39:50 -07:00
} else if (remoteLyrics.data) {
2023-06-02 23:54:34 -07:00
setSource(currentServer?.name ?? 'music server');
2023-06-03 00:39:50 -07:00
setSongLyrics(remoteLyrics.data);
2023-06-02 23:54:34 -07:00
return;
}
2023-05-22 17:38:31 -07:00
if (lyrics) {
const synchronizedLines = lyrics.matchAll(timeExp);
2023-05-22 17:38:31 -07:00
const synchronizedTimes: 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;
synchronizedTimes.push([timeInMilis, text]);
}
if (synchronizedTimes.length === 0) {
setSongLyrics(lyrics);
} else {
setSongLyrics(synchronizedTimes);
2023-05-22 17:38:31 -07:00
}
} else {
setSongLyrics(null);
2023-05-22 17:38:31 -07:00
}
2023-06-03 00:39:50 -07:00
}, [currentServer?.name, currentSong, override, remoteLyrics.data]);
2023-05-22 17:38:31 -07:00
return (
<ErrorBoundary FallbackComponent={ErrorFallback}>
2023-06-02 23:54:34 -07:00
{!songLyrics && (
<Center>
<Group>
<RiInformationFill size="2rem" />
<TextTitle
order={3}
weight={700}
>
No lyrics found
</TextTitle>
</Group>
</Center>
)}
{source && (
<LyricLine
key="provided-by"
className="credit"
text={`Provided by: ${source}`}
/>
)}
2023-06-02 23:54:34 -07:00
{songLyrics &&
(Array.isArray(songLyrics) ? (
<SynchronizedLyrics lyrics={songLyrics} />
) : (
<UnsynchronizedLyrics lyrics={songLyrics} />
))}
2023-05-22 17:38:31 -07:00
</ErrorBoundary>
);
};