mirror of
https://github.com/jeffvli/feishin.git
synced 2026-07-22 10:26:33 +02:00
feat: support furigana and romaji generation on web (#2232)
* feat: support furigana and romaji generation on web
This commit is contained in:
@@ -17,6 +17,14 @@ export type RomajiToken = LyricTextToken & {
|
||||
let kuroshiroInstance: any = null;
|
||||
let initPromise: null | Promise<void> = null;
|
||||
|
||||
const getDictionaryPath = (): string | undefined => {
|
||||
if (typeof document === 'undefined') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return new URL('./assets/kuromoji/', document.baseURI).href;
|
||||
};
|
||||
|
||||
const getKuroshiro = async () => {
|
||||
if (initPromise) {
|
||||
await initPromise;
|
||||
@@ -26,8 +34,13 @@ const getKuroshiro = async () => {
|
||||
if (kuroshiroInstance) return kuroshiroInstance;
|
||||
|
||||
const KuroshiroClass = (Kuroshiro as any).default || Kuroshiro;
|
||||
const dictionaryPath = getDictionaryPath();
|
||||
const analyzer = dictionaryPath
|
||||
? new KuromojiAnalyzer({ dictPath: dictionaryPath })
|
||||
: new KuromojiAnalyzer();
|
||||
|
||||
kuroshiroInstance = new KuroshiroClass();
|
||||
initPromise = kuroshiroInstance.init(new KuromojiAnalyzer());
|
||||
initPromise = kuroshiroInstance.init(analyzer);
|
||||
await initPromise;
|
||||
|
||||
initPromise = null;
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import isElectron from 'is-electron';
|
||||
|
||||
import * as browserLyricsApi from '../../../../main/features/core/lyrics/furigana';
|
||||
|
||||
const lyricsApi = isElectron() ? window.api.lyrics : browserLyricsApi;
|
||||
|
||||
export const {
|
||||
convertFurigana,
|
||||
convertFuriganaFragment,
|
||||
convertRomaji,
|
||||
convertRomajiTokens,
|
||||
parseLyricsTextTokens,
|
||||
} = lyricsApi;
|
||||
@@ -0,0 +1,7 @@
|
||||
export const {
|
||||
convertFurigana,
|
||||
convertFuriganaFragment,
|
||||
convertRomaji,
|
||||
convertRomajiTokens,
|
||||
parseLyricsTextTokens,
|
||||
} = window.api.lyrics;
|
||||
@@ -372,7 +372,6 @@ export const LyricsSettingsForm = ({ settingsKey }: LyricsSettingsFormProps) =>
|
||||
description: t('setting.enableFurigana', {
|
||||
context: 'description',
|
||||
}),
|
||||
isHidden: !isElectron(),
|
||||
title: t('setting.enableFurigana'),
|
||||
},
|
||||
{
|
||||
@@ -386,7 +385,6 @@ export const LyricsSettingsForm = ({ settingsKey }: LyricsSettingsFormProps) =>
|
||||
description: t('setting.enableRomaji', {
|
||||
context: 'description',
|
||||
}),
|
||||
isHidden: !isElectron(),
|
||||
title: t('setting.enableRomaji'),
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import isElectron from 'is-electron';
|
||||
|
||||
import * as lyricsApi from '/@/lyrics-conversion-api';
|
||||
import {
|
||||
alignFuriganaToWordCues,
|
||||
alignRomajiTokensToWordCues,
|
||||
@@ -10,15 +10,9 @@ import {
|
||||
import { normalizeLyrics } from '/@/renderer/features/lyrics/api/lyrics-utils';
|
||||
import { LyricsResponse, SyncedCueLine, SynchronizedLyrics } from '/@/shared/types/domain-types';
|
||||
|
||||
const lyricsApi = isElectron() ? window.api.lyrics : null;
|
||||
|
||||
const convertSyncedLyricsFurigana = async (
|
||||
lyrics: SynchronizedLyrics,
|
||||
): Promise<SynchronizedLyrics> => {
|
||||
if (!lyricsApi) {
|
||||
return lyrics;
|
||||
}
|
||||
|
||||
return Promise.all(
|
||||
normalizeLyrics(lyrics).map(async (line) => ({
|
||||
...line,
|
||||
@@ -76,9 +70,9 @@ const convertSyncedLyricsRomaji = async (
|
||||
|
||||
export const useFuriganaLyrics = (lyrics: LyricsResponse | null | undefined, enabled: boolean) => {
|
||||
return useQuery({
|
||||
enabled: enabled && !!lyrics && !!lyricsApi,
|
||||
enabled: enabled && !!lyrics,
|
||||
queryFn: async () => {
|
||||
if (!lyrics || !lyricsApi || !enabled) return lyrics;
|
||||
if (!lyrics || !enabled) return lyrics;
|
||||
|
||||
if (typeof lyrics === 'string') {
|
||||
return await lyricsApi.convertFurigana(lyrics);
|
||||
@@ -97,9 +91,9 @@ export const useFuriganaLyrics = (lyrics: LyricsResponse | null | undefined, ena
|
||||
|
||||
export const useRomajiLyrics = (lyrics: LyricsResponse | null | undefined, enabled: boolean) => {
|
||||
return useQuery({
|
||||
enabled: enabled && !!lyrics && !!lyricsApi,
|
||||
enabled: enabled && !!lyrics,
|
||||
queryFn: async () => {
|
||||
if (!lyrics || !lyricsApi || !enabled) return lyrics;
|
||||
if (!lyrics || !enabled) return lyrics;
|
||||
|
||||
if (typeof lyrics === 'string') {
|
||||
return await lyricsApi.convertRomaji(lyrics);
|
||||
@@ -129,10 +123,6 @@ const buildSyncedRomajiLine = async (
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!lyricsApi) {
|
||||
return cueLines.map(() => null);
|
||||
}
|
||||
|
||||
const tokens = (await lyricsApi.convertRomajiTokens(cueLine.value)) as RomajiToken[];
|
||||
if (!tokens.length) {
|
||||
romajiCueLines.push(null);
|
||||
@@ -160,9 +150,9 @@ export const useSyncedRomajiLyrics = (
|
||||
enabled: boolean,
|
||||
) => {
|
||||
return useQuery({
|
||||
enabled: enabled && !!lyrics && !!lyricsApi,
|
||||
enabled: enabled && !!lyrics,
|
||||
queryFn: async (): Promise<null | SyncedRomajiLyrics> => {
|
||||
if (!lyrics || !lyricsApi || !enabled) {
|
||||
if (!lyrics || !enabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -104,7 +104,6 @@ export const LyricSettings = memo(() => {
|
||||
description: t('setting.enableFurigana', {
|
||||
context: 'description',
|
||||
}),
|
||||
isHidden: !isElectron(),
|
||||
title: t('setting.enableFurigana'),
|
||||
},
|
||||
{
|
||||
@@ -118,7 +117,6 @@ export const LyricSettings = memo(() => {
|
||||
description: t('setting.enableRomaji', {
|
||||
context: 'description',
|
||||
}),
|
||||
isHidden: !isElectron(),
|
||||
title: t('setting.enableRomaji'),
|
||||
},
|
||||
{
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
export const join = (...parts: string[]): string =>
|
||||
parts
|
||||
.filter(Boolean)
|
||||
.map((part, index) =>
|
||||
index === 0 ? part.replace(/\/+$/u, '') : part.replace(/^\/+|\/+$/gu, ''),
|
||||
)
|
||||
.join('/');
|
||||
|
||||
export default { join };
|
||||
Reference in New Issue
Block a user