mirror of
https://github.com/jeffvli/feishin.git
synced 2026-07-07 11:09:58 +02:00
feat: add Japanese Furigana support to lyrics display (#2161)
This commit is contained in:
@@ -291,6 +291,22 @@ export const LyricsSettingsForm = ({ settingsKey }: LyricsSettingsFormProps) =>
|
||||
isHidden: !isElectron(),
|
||||
title: t('setting.lyricFetchProvider'),
|
||||
},
|
||||
{
|
||||
control: (
|
||||
<Switch
|
||||
aria-label="Enable furigana"
|
||||
defaultChecked={lyricsSettings.enableFurigana}
|
||||
onChange={(e) =>
|
||||
updateLyricsSetting({ enableFurigana: e.currentTarget.checked })
|
||||
}
|
||||
/>
|
||||
),
|
||||
description: t('setting.enableFurigana', {
|
||||
context: 'description',
|
||||
}),
|
||||
isHidden: !isElectron(),
|
||||
title: t('setting.enableFurigana'),
|
||||
},
|
||||
{
|
||||
control: (
|
||||
<Switch
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import isElectron from 'is-electron';
|
||||
|
||||
import { LyricsResponse, SynchronizedLyricsArray } from '/@/shared/types/domain-types';
|
||||
|
||||
const lyricsApi = isElectron() ? window.api.lyrics : null;
|
||||
|
||||
export const useFuriganaLyrics = (lyrics: LyricsResponse | null | undefined, enabled: boolean) => {
|
||||
return useQuery({
|
||||
enabled: enabled && !!lyrics && !!lyricsApi,
|
||||
queryFn: async () => {
|
||||
if (!lyrics || !lyricsApi || !enabled) return lyrics;
|
||||
|
||||
if (typeof lyrics === 'string') {
|
||||
return await lyricsApi.convertFurigana(lyrics);
|
||||
} else if (Array.isArray(lyrics)) {
|
||||
const text = lyrics.map(([, line]) => line).join('\n');
|
||||
const converted = await lyricsApi.convertFurigana(text);
|
||||
const convertedLines = converted.split('\n');
|
||||
return lyrics.map(([time], i) => [
|
||||
time,
|
||||
convertedLines[i] ?? lyrics[i][1],
|
||||
]) as SynchronizedLyricsArray;
|
||||
}
|
||||
return lyrics;
|
||||
},
|
||||
queryKey: ['furigana', lyrics],
|
||||
staleTime: Infinity,
|
||||
});
|
||||
};
|
||||
@@ -3,6 +3,7 @@ import { ComponentPropsWithoutRef, memo, useMemo } from 'react';
|
||||
|
||||
import styles from './lyric-line.module.css';
|
||||
|
||||
import { sanitize } from '/@/renderer/utils/sanitize';
|
||||
import { Box } from '/@/shared/components/box/box';
|
||||
import { Stack } from '/@/shared/components/stack/stack';
|
||||
|
||||
@@ -28,7 +29,7 @@ export const LyricLine = memo(
|
||||
<Box className={clsx(styles.lyricLine, className)} style={style} {...props}>
|
||||
<Stack gap={0}>
|
||||
{lines.map((line, index) => (
|
||||
<span key={index}>{line}</span>
|
||||
<span dangerouslySetInnerHTML={{ __html: sanitize(line) }} key={index} />
|
||||
))}
|
||||
</Stack>
|
||||
</Box>
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
type LyricsQueryResult,
|
||||
} from '/@/renderer/features/lyrics/api/lyrics-api';
|
||||
import { openLyricsExportModal } from '/@/renderer/features/lyrics/components/lyrics-export-form';
|
||||
import { useFuriganaLyrics } from '/@/renderer/features/lyrics/hooks/use-furigana-lyrics';
|
||||
import { LyricsActions } from '/@/renderer/features/lyrics/lyrics-actions';
|
||||
import {
|
||||
SynchronizedLyrics,
|
||||
@@ -49,6 +50,7 @@ export const Lyrics = ({ fadeOutNoLyricsMessage = true, settingsKey = 'default'
|
||||
|
||||
const {
|
||||
enableAutoTranslation,
|
||||
enableFurigana,
|
||||
preferLocalLyrics,
|
||||
translationApiKey,
|
||||
translationApiProvider,
|
||||
@@ -116,7 +118,15 @@ export const Lyrics = ({ fadeOutNoLyricsMessage = true, settingsKey = 'default'
|
||||
return computeSelectedFromResult(data, preferLocalLyrics, indexToUse);
|
||||
}, [data, indexToUse, preferLocalLyrics]);
|
||||
|
||||
const displayLyrics = isLyricsDisabled ? null : lyrics;
|
||||
const { data: furiganaConvertedLyrics } = useFuriganaLyrics(lyrics?.lyrics, !!enableFurigana);
|
||||
|
||||
const displayLyrics = useMemo(() => {
|
||||
if (isLyricsDisabled || !lyrics) return null;
|
||||
if (enableFurigana && furiganaConvertedLyrics) {
|
||||
return { ...lyrics, lyrics: furiganaConvertedLyrics };
|
||||
}
|
||||
return lyrics;
|
||||
}, [enableFurigana, isLyricsDisabled, lyrics, furiganaConvertedLyrics]);
|
||||
|
||||
const currentOffsetMs = useMemo(() => {
|
||||
if (!data) return 0;
|
||||
|
||||
Reference in New Issue
Block a user