mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-03-01 18:22:41 -08:00
154 lines
5.3 KiB
TypeScript
154 lines
5.3 KiB
TypeScript
import type { TokenizerDepsRuntimeOptions } from '../../core/services/tokenizer';
|
|
|
|
type TokenizerMainDeps = TokenizerDepsRuntimeOptions & {
|
|
getJlptEnabled: NonNullable<TokenizerDepsRuntimeOptions['getJlptEnabled']>;
|
|
getFrequencyDictionaryEnabled: NonNullable<
|
|
TokenizerDepsRuntimeOptions['getFrequencyDictionaryEnabled']
|
|
>;
|
|
getFrequencyRank: NonNullable<TokenizerDepsRuntimeOptions['getFrequencyRank']>;
|
|
getMinSentenceWordsForNPlusOne: NonNullable<
|
|
TokenizerDepsRuntimeOptions['getMinSentenceWordsForNPlusOne']
|
|
>;
|
|
getYomitanGroupDebugEnabled: NonNullable<
|
|
TokenizerDepsRuntimeOptions['getYomitanGroupDebugEnabled']
|
|
>;
|
|
recordLookup: (hit: boolean) => void;
|
|
};
|
|
|
|
export function createBuildTokenizerDepsMainHandler(deps: TokenizerMainDeps) {
|
|
return (): TokenizerDepsRuntimeOptions => ({
|
|
getYomitanExt: () => deps.getYomitanExt(),
|
|
getYomitanParserWindow: () => deps.getYomitanParserWindow(),
|
|
setYomitanParserWindow: (window) => deps.setYomitanParserWindow(window),
|
|
getYomitanParserReadyPromise: () => deps.getYomitanParserReadyPromise(),
|
|
setYomitanParserReadyPromise: (promise: Promise<void> | null) =>
|
|
deps.setYomitanParserReadyPromise(promise),
|
|
getYomitanParserInitPromise: () => deps.getYomitanParserInitPromise(),
|
|
setYomitanParserInitPromise: (promise: Promise<boolean> | null) =>
|
|
deps.setYomitanParserInitPromise(promise),
|
|
isKnownWord: (text: string) => {
|
|
const hit = deps.isKnownWord(text);
|
|
deps.recordLookup(hit);
|
|
return hit;
|
|
},
|
|
getKnownWordMatchMode: () => deps.getKnownWordMatchMode(),
|
|
...(deps.getNPlusOneEnabled
|
|
? {
|
|
getNPlusOneEnabled: () => deps.getNPlusOneEnabled!(),
|
|
}
|
|
: {}),
|
|
getMinSentenceWordsForNPlusOne: () => deps.getMinSentenceWordsForNPlusOne(),
|
|
getJlptLevel: (text: string) => deps.getJlptLevel(text),
|
|
getJlptEnabled: () => deps.getJlptEnabled(),
|
|
getFrequencyDictionaryEnabled: () => deps.getFrequencyDictionaryEnabled(),
|
|
getFrequencyRank: (text: string) => deps.getFrequencyRank(text),
|
|
getYomitanGroupDebugEnabled: () => deps.getYomitanGroupDebugEnabled(),
|
|
getMecabTokenizer: () => deps.getMecabTokenizer(),
|
|
});
|
|
}
|
|
|
|
export function createCreateMecabTokenizerAndCheckMainHandler<TMecab>(deps: {
|
|
getMecabTokenizer: () => TMecab | null;
|
|
setMecabTokenizer: (tokenizer: TMecab) => void;
|
|
createMecabTokenizer: () => TMecab;
|
|
checkAvailability: (tokenizer: TMecab) => Promise<unknown>;
|
|
}) {
|
|
return async (): Promise<void> => {
|
|
let tokenizer = deps.getMecabTokenizer();
|
|
if (!tokenizer) {
|
|
tokenizer = deps.createMecabTokenizer();
|
|
deps.setMecabTokenizer(tokenizer);
|
|
}
|
|
await deps.checkAvailability(tokenizer);
|
|
};
|
|
}
|
|
|
|
export function createPrewarmSubtitleDictionariesMainHandler(deps: {
|
|
ensureJlptDictionaryLookup: () => Promise<void>;
|
|
ensureFrequencyDictionaryLookup: () => Promise<void>;
|
|
showMpvOsd?: (message: string) => void;
|
|
shouldShowOsdNotification?: () => boolean;
|
|
setInterval?: (callback: () => void, delayMs: number) => unknown;
|
|
clearInterval?: (timer: unknown) => void;
|
|
}) {
|
|
let prewarmed = false;
|
|
let prewarmPromise: Promise<void> | null = null;
|
|
let loadingOsdDepth = 0;
|
|
let loadingOsdFrame = 0;
|
|
let loadingOsdTimer: unknown = null;
|
|
const showMpvOsd = deps.showMpvOsd;
|
|
const shouldShowOsdNotification = deps.shouldShowOsdNotification ?? (() => false);
|
|
const setIntervalHandler =
|
|
deps.setInterval ??
|
|
((callback: () => void, delayMs: number): unknown => setInterval(callback, delayMs));
|
|
const clearIntervalHandler =
|
|
deps.clearInterval ??
|
|
((timer: unknown): void => clearInterval(timer as ReturnType<typeof setInterval>));
|
|
const spinnerFrames = ['|', '/', '-', '\\'];
|
|
|
|
const beginLoadingOsd = (): boolean => {
|
|
if (!showMpvOsd || !shouldShowOsdNotification()) {
|
|
return false;
|
|
}
|
|
loadingOsdDepth += 1;
|
|
if (loadingOsdDepth > 1) {
|
|
return true;
|
|
}
|
|
|
|
loadingOsdFrame = 0;
|
|
showMpvOsd(`Loading subtitle annotations ${spinnerFrames[loadingOsdFrame]}`);
|
|
loadingOsdFrame += 1;
|
|
loadingOsdTimer = setIntervalHandler(() => {
|
|
if (!showMpvOsd) {
|
|
return;
|
|
}
|
|
showMpvOsd(`Loading subtitle annotations ${spinnerFrames[loadingOsdFrame % spinnerFrames.length]}`);
|
|
loadingOsdFrame += 1;
|
|
}, 180);
|
|
return true;
|
|
};
|
|
|
|
const endLoadingOsd = (): void => {
|
|
if (!showMpvOsd || !shouldShowOsdNotification()) {
|
|
return;
|
|
}
|
|
|
|
loadingOsdDepth = Math.max(0, loadingOsdDepth - 1);
|
|
if (loadingOsdDepth > 0) {
|
|
return;
|
|
}
|
|
|
|
if (loadingOsdTimer) {
|
|
clearIntervalHandler(loadingOsdTimer);
|
|
loadingOsdTimer = null;
|
|
}
|
|
showMpvOsd('Subtitle annotations loaded');
|
|
};
|
|
|
|
return async (options?: { showLoadingOsd?: boolean }): Promise<void> => {
|
|
if (prewarmed) {
|
|
return;
|
|
}
|
|
const shouldTrackLoadingOsd = options?.showLoadingOsd === true;
|
|
const loadingOsdStarted = shouldTrackLoadingOsd ? beginLoadingOsd() : false;
|
|
|
|
if (!prewarmPromise) {
|
|
prewarmPromise = (async () => {
|
|
try {
|
|
await Promise.all([deps.ensureJlptDictionaryLookup(), deps.ensureFrequencyDictionaryLookup()]);
|
|
prewarmed = true;
|
|
} finally {
|
|
prewarmPromise = null;
|
|
}
|
|
})();
|
|
}
|
|
try {
|
|
await prewarmPromise;
|
|
} finally {
|
|
if (loadingOsdStarted) {
|
|
endLoadingOsd();
|
|
}
|
|
}
|
|
};
|
|
}
|