mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-04-26 04:19:27 -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:
31
src/main.ts
31
src/main.ts
@@ -534,6 +534,7 @@ import {
|
||||
resolveSubtitleSourcePath,
|
||||
} from './main/runtime/subtitle-prefetch-source';
|
||||
import { createSubtitlePrefetchInitController } from './main/runtime/subtitle-prefetch-init';
|
||||
import { applyCharacterDictionarySelection } from './main/character-dictionary-selection';
|
||||
import { codecToExtension, getSubsyncConfig } from './subsync/utils';
|
||||
|
||||
if (process.platform === 'linux') {
|
||||
@@ -4861,12 +4862,16 @@ const { registerIpcRuntimeHandlers } = composeIpcRuntimeHandlers({
|
||||
retryAnilistQueueNow: () => processNextAnilistRetryUpdate(),
|
||||
getCharacterDictionarySelection: () =>
|
||||
characterDictionaryRuntime.getManualSelectionSnapshot(),
|
||||
setCharacterDictionarySelection: async (mediaId: number) => {
|
||||
const result = await characterDictionaryRuntime.setManualSelection({ mediaId });
|
||||
resetAnilistMediaGuessState();
|
||||
await characterDictionaryAutoSyncRuntime.runSyncNow();
|
||||
return result;
|
||||
},
|
||||
setCharacterDictionarySelection: async (mediaId: number) =>
|
||||
applyCharacterDictionarySelection(
|
||||
{ mediaId },
|
||||
{
|
||||
setManualSelection: (request) => characterDictionaryRuntime.setManualSelection(request),
|
||||
resetAnilistMediaGuessState,
|
||||
runSyncNow: () => characterDictionaryAutoSyncRuntime.runSyncNow(),
|
||||
warn: (message, error) => logger.warn(message, error),
|
||||
},
|
||||
),
|
||||
appendClipboardVideoToQueue: () => appendClipboardVideoToQueue(),
|
||||
...playlistBrowserMainDeps,
|
||||
getImmersionTracker: () => appState.immersionTracker,
|
||||
@@ -4951,12 +4956,14 @@ const { handleCliCommand, handleInitialArgs } = composeCliStartupHandlers({
|
||||
},
|
||||
getCharacterDictionarySelection: async (targetPath?: string) =>
|
||||
characterDictionaryRuntime.getManualSelectionSnapshot(targetPath),
|
||||
setCharacterDictionarySelection: async (request) => {
|
||||
const result = await characterDictionaryRuntime.setManualSelection(request);
|
||||
resetAnilistMediaGuessState();
|
||||
await characterDictionaryAutoSyncRuntime.runSyncNow();
|
||||
return result;
|
||||
},
|
||||
setCharacterDictionarySelection: async (request) =>
|
||||
applyCharacterDictionarySelection(request, {
|
||||
setManualSelection: (selectionRequest) =>
|
||||
characterDictionaryRuntime.setManualSelection(selectionRequest),
|
||||
resetAnilistMediaGuessState,
|
||||
runSyncNow: () => characterDictionaryAutoSyncRuntime.runSyncNow(),
|
||||
warn: (message, error) => logger.warn(message, error),
|
||||
}),
|
||||
runJellyfinCommand: (argsFromCommand: CliArgs) => runJellyfinCommand(argsFromCommand),
|
||||
runStatsCommand: (argsFromCommand: CliArgs, source: CliCommandSource) =>
|
||||
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