fix furigana and romaji issues (#2219)

* fix furigana romaji not displaying

* fix lyrics export crash when generation is enabled
This commit is contained in:
York
2026-07-15 03:08:26 +08:00
committed by GitHub
parent f462ebafdb
commit 552a6104e0
3 changed files with 32 additions and 11 deletions
+18 -2
View File
@@ -35,6 +35,10 @@ const getKuroshiro = async () => {
}; };
export const convertFurigana = async (text: string): Promise<string> => { export const convertFurigana = async (text: string): Promise<string> => {
if (typeof text !== 'string' || !text) {
return text;
}
const KuroshiroClass = (Kuroshiro as any).default || Kuroshiro; const KuroshiroClass = (Kuroshiro as any).default || Kuroshiro;
// check if the text contains any Japanese kana (to distinguish Japanese from Chinese text, which shares Kanji) // check if the text contains any Japanese kana (to distinguish Japanese from Chinese text, which shares Kanji)
@@ -51,7 +55,11 @@ export const convertFurigana = async (text: string): Promise<string> => {
}; };
export const convertFuriganaFragment = async (text: string): Promise<string> => { export const convertFuriganaFragment = async (text: string): Promise<string> => {
if (!text || !hasJapanese(text)) { if (typeof text !== 'string' || !text) {
return text;
}
if (!hasJapanese(text)) {
return text; return text;
} }
@@ -65,6 +73,10 @@ export const convertFuriganaFragment = async (text: string): Promise<string> =>
}; };
export const convertRomaji = async (text: string): Promise<string> => { export const convertRomaji = async (text: string): Promise<string> => {
if (typeof text !== 'string' || !text) {
return text;
}
const KuroshiroClass = (Kuroshiro as any).default || Kuroshiro; const KuroshiroClass = (Kuroshiro as any).default || Kuroshiro;
if (!KuroshiroClass.Util.hasKana(text)) return ''; if (!KuroshiroClass.Util.hasKana(text)) return '';
@@ -79,7 +91,7 @@ export const convertRomaji = async (text: string): Promise<string> => {
}; };
export const parseLyricsTextTokens = async (text: string): Promise<LyricTextToken[]> => { export const parseLyricsTextTokens = async (text: string): Promise<LyricTextToken[]> => {
if (!text || !hasJapanese(text)) { if (typeof text !== 'string' || !text || !hasJapanese(text)) {
return []; return [];
} }
@@ -108,6 +120,10 @@ export const parseLyricsTextTokens = async (text: string): Promise<LyricTextToke
}; };
export const convertRomajiTokens = async (text: string): Promise<RomajiToken[]> => { export const convertRomajiTokens = async (text: string): Promise<RomajiToken[]> => {
if (typeof text !== 'string' || !text) {
return [];
}
const KuroshiroClass = (Kuroshiro as any).default || Kuroshiro; const KuroshiroClass = (Kuroshiro as any).default || Kuroshiro;
if (!KuroshiroClass.Util.hasKana(text)) { if (!KuroshiroClass.Util.hasKana(text)) {
@@ -4,6 +4,7 @@ import { useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import i18n from '/@/i18n/i18n'; import i18n from '/@/i18n/i18n';
import { normalizeLyrics } from '/@/renderer/features/lyrics/api/lyrics-utils';
import { Button } from '/@/shared/components/button/button'; import { Button } from '/@/shared/components/button/button';
import { Checkbox } from '/@/shared/components/checkbox/checkbox'; import { Checkbox } from '/@/shared/components/checkbox/checkbox';
import { Code } from '/@/shared/components/code/code'; import { Code } from '/@/shared/components/code/code';
@@ -31,8 +32,14 @@ export const LyricsExportForm = ({ lyrics, offsetMs, synced }: LyricsExportFormP
}); });
const displayedLyrics = useMemo(() => { const displayedLyrics = useMemo(() => {
if (form.values.synced && Array.isArray(lyrics.lyrics)) { if (Array.isArray(lyrics.lyrics)) {
const contents = lyrics.lyrics const normalizedLyrics = normalizeLyrics(lyrics.lyrics);
if (!form.values.synced) {
return normalizedLyrics.map((lyric) => lyric.text).join('\n') + '\n';
}
const contents = normalizedLyrics
.map( .map(
(lyric) => (lyric) =>
`[${formatDuration(lyric.startMs, { leading: true, ms: true })}]${lyric.text}`, `[${formatDuration(lyric.startMs, { leading: true, ms: true })}]${lyric.text}`,
@@ -44,12 +51,9 @@ export const LyricsExportForm = ({ lyrics, offsetMs, synced }: LyricsExportFormP
[offset:${form.values.offsetMs + (lyrics.offsetMs ?? 0)}] [offset:${form.values.offsetMs + (lyrics.offsetMs ?? 0)}]
${contents} ${contents}
`; `;
} else {
if (Array.isArray(lyrics.lyrics)) {
return lyrics.lyrics.map((lyric) => lyric.text).join('\n') + '\n';
}
return lyrics.lyrics;
} }
return lyrics.lyrics;
}, [ }, [
form.values.offsetMs, form.values.offsetMs,
form.values.synced, form.values.synced,
@@ -7,6 +7,7 @@ import {
LyricTextToken, LyricTextToken,
RomajiToken, RomajiToken,
} from '/@/renderer/features/lyrics/api/lyric-conversion'; } from '/@/renderer/features/lyrics/api/lyric-conversion';
import { normalizeLyrics } from '/@/renderer/features/lyrics/api/lyrics-utils';
import { LyricsResponse, SyncedCueLine, SynchronizedLyrics } from '/@/shared/types/domain-types'; import { LyricsResponse, SyncedCueLine, SynchronizedLyrics } from '/@/shared/types/domain-types';
const lyricsApi = isElectron() ? window.api.lyrics : null; const lyricsApi = isElectron() ? window.api.lyrics : null;
@@ -19,7 +20,7 @@ const convertSyncedLyricsFurigana = async (
} }
return Promise.all( return Promise.all(
lyrics.map(async (line) => ({ normalizeLyrics(lyrics).map(async (line) => ({
...line, ...line,
cueLines: line.cueLines cueLines: line.cueLines
? await Promise.all( ? await Promise.all(
@@ -53,7 +54,7 @@ const convertSyncedLyricsRomaji = async (
convert: (text: string) => Promise<string>, convert: (text: string) => Promise<string>,
): Promise<SynchronizedLyrics> => ): Promise<SynchronizedLyrics> =>
Promise.all( Promise.all(
lyrics.map(async (line) => ({ normalizeLyrics(lyrics).map(async (line) => ({
...line, ...line,
cueLines: line.cueLines cueLines: line.cueLines
? await Promise.all( ? await Promise.all(