mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-04-26 16:19:26 -07:00
refactor(character-dictionary): extract applyCharacterDictionarySelection helper
- Add `applyCharacterDictionarySelection` in its own module with injected deps - Catches sync errors and emits a warning instead of propagating - Remove duplicated inline logic from IPC and CLI startup handlers in main.ts - Add unit test covering sync-failure resilience
This commit is contained in:
29
src/main.ts
29
src/main.ts
@@ -534,6 +534,7 @@ import {
|
|||||||
resolveSubtitleSourcePath,
|
resolveSubtitleSourcePath,
|
||||||
} from './main/runtime/subtitle-prefetch-source';
|
} from './main/runtime/subtitle-prefetch-source';
|
||||||
import { createSubtitlePrefetchInitController } from './main/runtime/subtitle-prefetch-init';
|
import { createSubtitlePrefetchInitController } from './main/runtime/subtitle-prefetch-init';
|
||||||
|
import { applyCharacterDictionarySelection } from './main/character-dictionary-selection';
|
||||||
import { codecToExtension, getSubsyncConfig } from './subsync/utils';
|
import { codecToExtension, getSubsyncConfig } from './subsync/utils';
|
||||||
|
|
||||||
if (process.platform === 'linux') {
|
if (process.platform === 'linux') {
|
||||||
@@ -4861,12 +4862,16 @@ const { registerIpcRuntimeHandlers } = composeIpcRuntimeHandlers({
|
|||||||
retryAnilistQueueNow: () => processNextAnilistRetryUpdate(),
|
retryAnilistQueueNow: () => processNextAnilistRetryUpdate(),
|
||||||
getCharacterDictionarySelection: () =>
|
getCharacterDictionarySelection: () =>
|
||||||
characterDictionaryRuntime.getManualSelectionSnapshot(),
|
characterDictionaryRuntime.getManualSelectionSnapshot(),
|
||||||
setCharacterDictionarySelection: async (mediaId: number) => {
|
setCharacterDictionarySelection: async (mediaId: number) =>
|
||||||
const result = await characterDictionaryRuntime.setManualSelection({ mediaId });
|
applyCharacterDictionarySelection(
|
||||||
resetAnilistMediaGuessState();
|
{ mediaId },
|
||||||
await characterDictionaryAutoSyncRuntime.runSyncNow();
|
{
|
||||||
return result;
|
setManualSelection: (request) => characterDictionaryRuntime.setManualSelection(request),
|
||||||
|
resetAnilistMediaGuessState,
|
||||||
|
runSyncNow: () => characterDictionaryAutoSyncRuntime.runSyncNow(),
|
||||||
|
warn: (message, error) => logger.warn(message, error),
|
||||||
},
|
},
|
||||||
|
),
|
||||||
appendClipboardVideoToQueue: () => appendClipboardVideoToQueue(),
|
appendClipboardVideoToQueue: () => appendClipboardVideoToQueue(),
|
||||||
...playlistBrowserMainDeps,
|
...playlistBrowserMainDeps,
|
||||||
getImmersionTracker: () => appState.immersionTracker,
|
getImmersionTracker: () => appState.immersionTracker,
|
||||||
@@ -4951,12 +4956,14 @@ const { handleCliCommand, handleInitialArgs } = composeCliStartupHandlers({
|
|||||||
},
|
},
|
||||||
getCharacterDictionarySelection: async (targetPath?: string) =>
|
getCharacterDictionarySelection: async (targetPath?: string) =>
|
||||||
characterDictionaryRuntime.getManualSelectionSnapshot(targetPath),
|
characterDictionaryRuntime.getManualSelectionSnapshot(targetPath),
|
||||||
setCharacterDictionarySelection: async (request) => {
|
setCharacterDictionarySelection: async (request) =>
|
||||||
const result = await characterDictionaryRuntime.setManualSelection(request);
|
applyCharacterDictionarySelection(request, {
|
||||||
resetAnilistMediaGuessState();
|
setManualSelection: (selectionRequest) =>
|
||||||
await characterDictionaryAutoSyncRuntime.runSyncNow();
|
characterDictionaryRuntime.setManualSelection(selectionRequest),
|
||||||
return result;
|
resetAnilistMediaGuessState,
|
||||||
},
|
runSyncNow: () => characterDictionaryAutoSyncRuntime.runSyncNow(),
|
||||||
|
warn: (message, error) => logger.warn(message, error),
|
||||||
|
}),
|
||||||
runJellyfinCommand: (argsFromCommand: CliArgs) => runJellyfinCommand(argsFromCommand),
|
runJellyfinCommand: (argsFromCommand: CliArgs) => runJellyfinCommand(argsFromCommand),
|
||||||
runStatsCommand: (argsFromCommand: CliArgs, source: CliCommandSource) =>
|
runStatsCommand: (argsFromCommand: CliArgs, source: CliCommandSource) =>
|
||||||
runStatsCliCommand(argsFromCommand, source),
|
runStatsCliCommand(argsFromCommand, source),
|
||||||
|
|||||||
27
src/main/character-dictionary-selection.test.ts
Normal file
27
src/main/character-dictionary-selection.test.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import test from 'node:test';
|
||||||
|
|
||||||
|
import { applyCharacterDictionarySelection } from './character-dictionary-selection';
|
||||||
|
|
||||||
|
test('applyCharacterDictionarySelection returns saved override when post-save sync fails', async () => {
|
||||||
|
const warnings: unknown[] = [];
|
||||||
|
const result = await applyCharacterDictionarySelection(
|
||||||
|
{ mediaId: 21355 },
|
||||||
|
{
|
||||||
|
setManualSelection: async (request) => ({
|
||||||
|
ok: true,
|
||||||
|
seriesKey: `series-${request.mediaId}`,
|
||||||
|
selected: { id: request.mediaId, title: 'Re:ZERO', episodes: 25 },
|
||||||
|
staleMediaIds: [10607],
|
||||||
|
}),
|
||||||
|
resetAnilistMediaGuessState: () => {},
|
||||||
|
runSyncNow: async () => {
|
||||||
|
throw new Error('sync failed');
|
||||||
|
},
|
||||||
|
warn: (...args) => warnings.push(args),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(result.selected.id, 21355);
|
||||||
|
assert.equal(warnings.length, 1);
|
||||||
|
});
|
||||||
29
src/main/character-dictionary-selection.ts
Normal file
29
src/main/character-dictionary-selection.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import type { CharacterDictionaryManualSelectionResult } from './character-dictionary-runtime/types';
|
||||||
|
|
||||||
|
export type CharacterDictionarySelectionRequest = {
|
||||||
|
targetPath?: string;
|
||||||
|
mediaId: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CharacterDictionarySelectionDeps = {
|
||||||
|
setManualSelection: (
|
||||||
|
request: CharacterDictionarySelectionRequest,
|
||||||
|
) => Promise<CharacterDictionaryManualSelectionResult>;
|
||||||
|
resetAnilistMediaGuessState: () => void;
|
||||||
|
runSyncNow: () => Promise<void>;
|
||||||
|
warn: (message: string, error?: unknown) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function applyCharacterDictionarySelection(
|
||||||
|
request: CharacterDictionarySelectionRequest,
|
||||||
|
deps: CharacterDictionarySelectionDeps,
|
||||||
|
): Promise<CharacterDictionaryManualSelectionResult> {
|
||||||
|
const result = await deps.setManualSelection(request);
|
||||||
|
deps.resetAnilistMediaGuessState();
|
||||||
|
try {
|
||||||
|
await deps.runSyncNow();
|
||||||
|
} catch (error) {
|
||||||
|
deps.warn('Character dictionary auto-sync failed after manual selection', error);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user