refactor(main): drop pass-through main-deps builders (#306)

* refactor(main): drop mining/anki main-deps builders

* refactor(anilist): drop main-deps builders

* refactor(jellyfin): drop main-deps builders

* refactor(overlay): drop overlay and shortcut main-deps builders

* refactor(main): drop ipc, tray, and osd main-deps builders

* refactor(startup): drop startup main-deps builders

* test(scripts): ignore stray local requests in AUR download test

Also apply prettier to launcher/mpv.test.ts so format:check:src passes.

* fix(shortcuts): forward overlay numeric selection hooks through the composer

composeShortcutRuntimes only passed the digit callbacks, so the visible-overlay
selection added in #78 never ran from the multi-copy/mine-sentence shortcuts.

* fix(stats): skip mpv auto-connect when stats starts the immersion tracker

The immersion-startup main-deps builder never forwarded shouldAutoConnectMpv,
so background stats and stats CLI startup always connected to mpv. Drop the
builder and pass the deps straight to the handler.

* build(deps): bump follow-redirects override to 1.16.2

Resolves GHSA-mrwp-f3q8-3g9p (high), which fails the quality-gate
bun audit --audit-level high step.

* test(subtitles): cover the null-tokenization settle path in prefetch test

The mock ignored the value the test resolved, so the null case never ran.

* test(e2e): surface mpv crashes and install current mpv on macOS CI

The harness IPC client never handled socket close, so a crashed mpv left
commands pending until the 120s test timeout with an empty mpv.log. Reject
pending commands on close, write mpv's own log via --log-file, and refresh
Homebrew on macOS CI so the installed mpv build no longer depends on the
runner image.

* test(e2e): render mpv through cocoa-cb OpenGL on macOS

mpv 0.41.0_12 with libplacebo 7.387.0 segfaults in gpu-next's MoltenVK path
on GitHub's paravirtual macOS GPU on the first frame, so the E2E session
never started.

* test(e2e): name mpv-stdio.log in the mpv close diagnostic
This commit is contained in:
sudacode authored and GitHub committed 2026-10-31 00:50:33 -07:00
1 parent 0129e03c0f
commit 2a0e931325
84 files changed
+719 -2754

No files matched your search

+234 -370
View File
@@ -155,7 +155,6 @@ import {
buildAnilistSetupUrl,
consumeAnilistSetupCallbackUrl,
createAnilistStateRuntime,
createBuildOpenAnilistSetupWindowMainDepsHandler,
createMaybeFocusExistingAnilistSetupWindowHandler,
createOpenAnilistSetupWindowHandler,
findAnilistSetupDeepLinkArgvUrl,
@@ -168,8 +167,6 @@ import { DEFAULT_MIN_WATCH_RATIO } from './shared/watch-threshold';
import { shouldShowTexthookerTrayEntry } from './main/runtime/tray-main-actions';
import {
createApplyJellyfinMpvDefaultsHandler,
createBuildApplyJellyfinMpvDefaultsMainDepsHandler,
createBuildGetDefaultSocketPathMainDepsHandler,
createGetDefaultSocketPathHandler,
buildJellyfinSetupFormHtml,
parseJellyfinSetupSubmissionUrl,
@@ -184,22 +181,7 @@ import {
createBuildConfigHotReloadRuntimeMainDepsHandler,
createBuildWatchConfigPathMainDepsHandler,
createWatchConfigPathHandler,
createBuildOverlayContentMeasurementStoreMainDepsHandler,
createBuildOverlayModalRuntimeMainDepsHandler,
createBuildAppendClipboardVideoToQueueMainDepsHandler,
createBuildHandleOverlayModalClosedMainDepsHandler,
createBuildLoadSubtitlePositionMainDepsHandler,
createBuildSaveSubtitlePositionMainDepsHandler,
createBuildFieldGroupingOverlayMainDepsHandler,
createBuildGetFieldGroupingResolverMainDepsHandler,
createBuildSetFieldGroupingResolverMainDepsHandler,
createBuildOverlayVisibilityRuntimeMainDepsHandler,
createBuildBroadcastRuntimeOptionsChangedMainDepsHandler,
createBuildGetRuntimeOptionsStateMainDepsHandler,
createBuildOpenRuntimeOptionsPaletteMainDepsHandler,
createBuildRestorePreviousSecondarySubVisibilityMainDepsHandler,
createBuildSendToActiveOverlayWindowMainDepsHandler,
createBuildSetOverlayDebugVisualizationEnabledMainDepsHandler,
createTrayRuntimeHandlers,
createOverlayVisibilityRuntime,
createGetRuntimeOptionsStateHandler,
@@ -219,12 +201,6 @@ import {
resolveSubtitleStyleForRenderer,
} from './main/runtime/domains/overlay';
import {
createBuildAnilistStateRuntimeMainDepsHandler,
createBuildConfigDerivedRuntimeMainDepsHandler,
createBuildImmersionMediaRuntimeMainDepsHandler,
createBuildMainSubsyncRuntimeMainDepsHandler,
createBuildSubtitleProcessingControllerMainDepsHandler,
createBuildMediaRuntimeMainDepsHandler,
createBuildDictionaryRootsMainHandler,
createBuildFrequencyDictionaryRootsMainHandler,
createBuildFrequencyDictionaryRuntimeMainDepsHandler,
@@ -244,14 +220,6 @@ import {
resolvePrimarySubtitle,
} from './main/runtime/primary-subtitle-text';
import {
createBuildCopyCurrentSubtitleMainDepsHandler,
createBuildHandleMineSentenceDigitMainDepsHandler,
createBuildHandleMultiCopyDigitMainDepsHandler,
createBuildMarkLastCardAsAudioCardMainDepsHandler,
createBuildMineSentenceCardMainDepsHandler,
createBuildRefreshKnownWordCacheMainDepsHandler,
createBuildTriggerFieldGroupingMainDepsHandler,
createBuildUpdateLastCardFromClipboardMainDepsHandler,
createMarkLastCardAsAudioCardHandler,
createMineSentenceCardHandler,
createRefreshKnownWordCacheHandler,
@@ -291,7 +259,6 @@ import {
createOverlayContentMeasurementStore,
createOverlayManager,
createOverlayWindow as createOverlayWindowCore,
createSubtitleProcessingController,
createTokenizerDepsRuntime,
cycleSecondarySubMode as cycleSecondarySubModeCore,
deleteYomitanDictionaryByTitle,
@@ -428,8 +395,10 @@ import {
shouldQuitOnWindowAllClosedForTrayState,
} from './main/runtime/startup-tray-policy';
import { createLogExportTrayRuntime } from './main/runtime/log-export-tray';
import { createImmersionTrackerStartupHandler } from './main/runtime/immersion-startup';
import { createBuildImmersionTrackerStartupMainDepsHandler } from './main/runtime/immersion-startup-main-deps';
import {
createImmersionTrackerStartupHandler,
type ImmersionTrackerStartupDeps,
} from './main/runtime/immersion-startup';
import {
createRunStatsCliCommandHandler,
writeStatsCliCommandResponse,
@@ -529,6 +498,7 @@ import {
getJlptDictionarySearchPaths,
} from './main/jlpt-runtime';
import { createMediaRuntimeService } from './main/media-runtime';
import { createPrefetchAwareSubtitleProcessingController } from './main/runtime/subtitle-processing-runtime';
import { createOverlayVisibilityRuntimeService } from './main/overlay-visibility-runtime';
import { createDiscordPresenceRuntime } from './main/runtime/discord-presence-runtime';
import { createCharacterDictionaryRuntimeService } from './main/character-dictionary-runtime';
@@ -707,15 +677,10 @@ let backgroundWarmupsStarted = false;
let yomitanLoadInFlight: Promise<Extension | null> | null = null;
let notifyAnilistTokenStoreWarning: (message: string) => void = () => {};
const buildApplyJellyfinMpvDefaultsMainDepsHandler =
createBuildApplyJellyfinMpvDefaultsMainDepsHandler({
sendMpvCommandRuntime: (client, command) => sendMpvCommandRuntime(client, command),
jellyfinLangPref: JELLYFIN_LANG_PREF,
});
const applyJellyfinMpvDefaultsMainDeps = buildApplyJellyfinMpvDefaultsMainDepsHandler();
const applyJellyfinMpvDefaultsHandler = createApplyJellyfinMpvDefaultsHandler(
applyJellyfinMpvDefaultsMainDeps,
);
const applyJellyfinMpvDefaultsHandler = createApplyJellyfinMpvDefaultsHandler({
sendMpvCommandRuntime: (client, command) => sendMpvCommandRuntime(client, command),
jellyfinLangPref: JELLYFIN_LANG_PREF,
});
const isDev = process.argv.includes('--dev') || process.argv.includes('--debug');
const texthookerService = new Texthooker(() => {
@@ -758,11 +723,9 @@ const texthookerService = new Texthooker(() => {
});
let syncOverlayShortcutsForModal: (isActive: boolean) => void = () => {};
let syncOverlayVisibilityForModal: () => void = () => {};
const buildGetDefaultSocketPathMainDepsHandler = createBuildGetDefaultSocketPathMainDepsHandler({
const getDefaultSocketPathHandler = createGetDefaultSocketPathHandler({
platform: process.platform,
});
const getDefaultSocketPathMainDeps = buildGetDefaultSocketPathMainDepsHandler();
const getDefaultSocketPathHandler = createGetDefaultSocketPathHandler(getDefaultSocketPathMainDeps);
type BootServices = MainBootServicesResult<
ConfigService,
@@ -852,45 +815,46 @@ const bootServices = createMainBootServices({
}),
createOverlayModalInputState,
createOverlayContentMeasurementStore: ({ logger }) => {
const buildHandler = createBuildOverlayContentMeasurementStoreMainDepsHandler({
return createOverlayContentMeasurementStore({
now: () => Date.now(),
warn: (message: string) => logger.warn(message),
});
return createOverlayContentMeasurementStore(buildHandler());
},
getSyncOverlayShortcutsForModal: () => syncOverlayShortcutsForModal,
getSyncOverlayVisibilityForModal: () => syncOverlayVisibilityForModal,
createOverlayModalRuntime: ({ overlayManager, overlayModalInputState }) => {
const buildHandler = createBuildOverlayModalRuntimeMainDepsHandler({
getMainWindow: () => overlayManager.getMainWindow(),
getModalWindow: () => overlayManager.getModalWindow(),
createModalWindow: () => createModalWindow(),
getModalGeometry: () => overlayGeometryRuntime.getCurrentOverlayGeometry(),
setModalWindowBounds: (geometry) => overlayManager.setModalWindowBounds(geometry),
});
return createOverlayModalRuntimeService(buildHandler(), {
onModalStateChange: (isActive: boolean) =>
overlayModalInputState.handleModalInputStateChange(isActive),
// On macOS, after the last modal closes the post-close visibility sync hides the overlay
// when neither it nor mpv is focused, and keyboard focus is left in limbo (mpv keys like
// shift-to-unpause stop working). Programmatically focusing mpv from our background helper
// is refused by macOS (most visibly in native fullscreen), so instead resign SubMiner's
// active status — exactly what a manual click does — handing focus back to the previously
// active app (mpv). The overlay is already hidden at this point, so app.hide() hides nothing
// visible; once mpv is focused the tracker re-shows the overlay above it.
onFinalModalClosed: () => {
void restoreMacOSMpvFocusAfterModalClose({
platform: process.platform,
focusMpv: async () => {
app.hide();
},
getWindowTracker: () => appState.windowTracker,
updateVisibleOverlayVisibility: () =>
overlayVisibilityRuntime.updateVisibleOverlayVisibility(),
warn: (message, details) => logger.warn(message, details),
});
return createOverlayModalRuntimeService(
{
getMainWindow: () => overlayManager.getMainWindow(),
getModalWindow: () => overlayManager.getModalWindow(),
createModalWindow: () => createModalWindow(),
getModalGeometry: () => overlayGeometryRuntime.getCurrentOverlayGeometry(),
setModalWindowBounds: (geometry) => overlayManager.setModalWindowBounds(geometry),
},
});
{
onModalStateChange: (isActive: boolean) =>
overlayModalInputState.handleModalInputStateChange(isActive),
// On macOS, after the last modal closes the post-close visibility sync hides the overlay
// when neither it nor mpv is focused, and keyboard focus is left in limbo (mpv keys like
// shift-to-unpause stop working). Programmatically focusing mpv from our background helper
// is refused by macOS (most visibly in native fullscreen), so instead resign SubMiner's
// active status — exactly what a manual click does — handing focus back to the previously
// active app (mpv). The overlay is already hidden at this point, so app.hide() hides nothing
// visible; once mpv is focused the tracker re-shows the overlay above it.
onFinalModalClosed: () => {
void restoreMacOSMpvFocusAfterModalClose({
platform: process.platform,
focusMpv: async () => {
app.hide();
},
getWindowTracker: () => appState.windowTracker,
updateVisibleOverlayVisibility: () =>
overlayVisibilityRuntime.updateVisibleOverlayVisibility(),
warn: (message, details) => logger.warn(message, details),
});
},
},
);
},
createAppState,
}) as BootServices;
@@ -1717,7 +1681,7 @@ function syncOverlayMpvSubtitleSuppression(): void {
restoreOverlayMpvSubtitles();
}
const buildImmersionMediaRuntimeMainDepsHandler = createBuildImmersionMediaRuntimeMainDepsHandler({
const immersionMediaRuntime = createImmersionMediaRuntime({
getResolvedConfig: () => configService.getConfig(),
defaultImmersionDbPath: DEFAULT_IMMERSION_DB_PATH,
getTracker: () => appState.immersionTracker,
@@ -1727,7 +1691,23 @@ const buildImmersionMediaRuntimeMainDepsHandler = createBuildImmersionMediaRunti
logDebug: (message) => logger.debug(message),
logInfo: (message) => logger.info(message),
});
const buildAnilistStateRuntimeMainDepsHandler = createBuildAnilistStateRuntimeMainDepsHandler({
const anilistRateLimiter = createAnilistRateLimiter();
const statsCoverArtFetcher = createCoverArtFetcher(
anilistRateLimiter,
createLogger('main:stats-cover-art'),
{
liveAction: createLiveActionMetadataResolver(
createTmdbClient({
resolveApiKey: createTmdbApiKeyResolver(
() => configService.getConfig().tmdb,
() => bundledTmdbApiKey,
),
}),
createLogger('main:tmdb'),
),
},
);
const anilistStateRuntime = createAnilistStateRuntime({
getClientSecretState: () => appState.anilistClientSecretState,
setClientSecretState: (next) => {
appState.anilistClientSecretState = transitionAnilistClientSecretState(
@@ -1748,14 +1728,14 @@ const buildAnilistStateRuntimeMainDepsHandler = createBuildAnilistStateRuntimeMa
anilistCachedAccessToken = null;
},
});
const buildConfigDerivedRuntimeMainDepsHandler = createBuildConfigDerivedRuntimeMainDepsHandler({
const configDerivedRuntime = createConfigDerivedRuntime({
getResolvedConfig: () => configService.getConfig(),
getRuntimeOptionsManager: () => appState.runtimeOptionsManager,
defaultJimakuLanguagePreference: DEFAULT_CONFIG.jimaku.languagePreference,
defaultJimakuMaxEntryResults: DEFAULT_CONFIG.jimaku.maxEntryResults,
defaultJimakuApiBaseUrl: DEFAULT_CONFIG.jimaku.apiBaseUrl,
});
const buildMainSubsyncRuntimeMainDepsHandler = createBuildMainSubsyncRuntimeMainDepsHandler({
const subsyncRuntime = createMainSubsyncRuntime({
getMpvClient: () => appState.mpvClient,
getResolvedConfig: () => configService.getConfig(),
getSubsyncInProgress: () => appState.subsyncInProgress,
@@ -1771,28 +1751,6 @@ const buildMainSubsyncRuntimeMainDepsHandler = createBuildMainSubsyncRuntimeMain
);
},
});
const immersionMediaRuntime = createImmersionMediaRuntime(
buildImmersionMediaRuntimeMainDepsHandler(),
);
const anilistRateLimiter = createAnilistRateLimiter();
const statsCoverArtFetcher = createCoverArtFetcher(
anilistRateLimiter,
createLogger('main:stats-cover-art'),
{
liveAction: createLiveActionMetadataResolver(
createTmdbClient({
resolveApiKey: createTmdbApiKeyResolver(
() => configService.getConfig().tmdb,
() => bundledTmdbApiKey,
),
}),
createLogger('main:tmdb'),
),
},
);
const anilistStateRuntime = createAnilistStateRuntime(buildAnilistStateRuntimeMainDepsHandler());
const configDerivedRuntime = createConfigDerivedRuntime(buildConfigDerivedRuntimeMainDepsHandler());
const subsyncRuntime = createMainSubsyncRuntime(buildMainSubsyncRuntimeMainDepsHandler());
const currentMediaTokenizationGate = createCurrentMediaTokenizationGate();
const startupOsdSequencer = createStartupOsdSequencer({
getNotificationType: () => overlayNotificationsRuntime.getConfiguredStatusNotificationType(),
@@ -2061,30 +2019,18 @@ function signalCurrentSubtitleAutoplayReady(): void {
);
}
}
const buildSubtitleProcessingControllerMainDepsHandler =
createBuildSubtitleProcessingControllerMainDepsHandler({
tokenizeSubtitle: async (text: string) =>
tokenizeSubtitleDeferred ? await tokenizeSubtitleDeferred(text) : { text, tokens: null },
// Controller emits never release the prefetch pause: the first emit for an
// uncached line is the provisional plain payload, sent before tokenization
// starts, so resuming on it would put prefetch back in contention with the
// on-screen line for the single parser window.
emitSubtitle: (payload) => emitSubtitlePayload(payload, { resumePrefetch: false }),
// The pause is released once the controller has no work left, which covers
// the runs that end without an emit (suppressed duplicate, failed
// tokenization) as well as the ones that deliver a payload.
onProcessingSettled: () => {
subtitlePrefetchService?.resume();
},
logDebug: (message) => {
logger.debug(`[subtitle-processing] ${message}`);
},
now: () => Date.now(),
});
const subtitleProcessingControllerMainDeps = buildSubtitleProcessingControllerMainDepsHandler();
const subtitleProcessingController = createSubtitleProcessingController(
subtitleProcessingControllerMainDeps,
);
const subtitleProcessingController = createPrefetchAwareSubtitleProcessingController({
tokenizeSubtitle: async (text: string) =>
tokenizeSubtitleDeferred ? await tokenizeSubtitleDeferred(text) : { text, tokens: null },
emitSubtitlePayload: (payload, options) => emitSubtitlePayload(payload, options),
resumePrefetch: () => {
subtitlePrefetchService?.resume();
},
logDebug: (message) => {
logger.debug(`[subtitle-processing] ${message}`);
},
now: () => Date.now(),
});
let subtitlePrefetchService: SubtitlePrefetchService | null = null;
let lastObservedTimePos = 0;
@@ -2645,79 +2591,68 @@ const frequencyDictionaryRuntime = createFrequencyDictionaryRuntimeService(
})(),
);
const buildGetFieldGroupingResolverMainDepsHandler =
createBuildGetFieldGroupingResolverMainDepsHandler({
getResolver: () => appState.fieldGroupingResolver,
});
const getFieldGroupingResolverMainDeps = buildGetFieldGroupingResolverMainDepsHandler();
const getFieldGroupingResolverHandler = createGetFieldGroupingResolverHandler(
getFieldGroupingResolverMainDeps,
);
const getFieldGroupingResolverHandler = createGetFieldGroupingResolverHandler({
getResolver: () => appState.fieldGroupingResolver,
});
const buildSetFieldGroupingResolverMainDepsHandler =
createBuildSetFieldGroupingResolverMainDepsHandler({
setResolver: (resolver) => {
appState.fieldGroupingResolver = resolver;
},
nextSequence: () => {
appState.fieldGroupingResolverSequence += 1;
return appState.fieldGroupingResolverSequence;
},
getSequence: () => appState.fieldGroupingResolverSequence,
});
const setFieldGroupingResolverMainDeps = buildSetFieldGroupingResolverMainDepsHandler();
const setFieldGroupingResolverHandler = createSetFieldGroupingResolverHandler(
setFieldGroupingResolverMainDeps,
);
const setFieldGroupingResolverHandler = createSetFieldGroupingResolverHandler({
setResolver: (resolver) => {
appState.fieldGroupingResolver = resolver;
},
nextSequence: () => {
appState.fieldGroupingResolverSequence += 1;
return appState.fieldGroupingResolverSequence;
},
getSequence: () => appState.fieldGroupingResolverSequence,
});
const fieldGroupingOverlayRuntime = createFieldGroupingOverlayRuntime<OverlayHostedModal>(
createBuildFieldGroupingOverlayMainDepsHandler<OverlayHostedModal>({
getMainWindow: () => overlayManager.getMainWindow(),
getVisibleOverlayVisible: () => overlayManager.getVisibleOverlayVisible(),
setVisibleOverlayVisible: (visible) => setVisibleOverlayVisible(visible),
getResolver: () => getFieldGroupingResolverHandler(),
setResolver: (resolver) => setFieldGroupingResolverHandler(resolver),
getRestoreVisibleOverlayOnModalClose: () =>
overlayModalRuntime.getRestoreVisibleOverlayOnModalClose(),
waitForModalOpen: (modal, timeoutMs) => overlayModalRuntime.waitForModalOpen(modal, timeoutMs),
handleOverlayModalClosed: (modal) => overlayModalRuntime.handleOverlayModalClosed(modal),
logWarn: (message) => logger.warn(message),
ensureOverlayStartupPrereqs: () => ensureOverlayStartupPrereqs(),
ensureOverlayWindowsReadyForVisibilityActions: () =>
ensureOverlayWindowsReadyForVisibilityActions(),
sendToActiveOverlayWindow: (channel, payload, runtimeOptions) =>
overlayModalRuntime.sendToActiveOverlayWindow(channel, payload, runtimeOptions),
})(),
);
const fieldGroupingOverlayRuntime = createFieldGroupingOverlayRuntime<OverlayHostedModal>({
getMainWindow: () => overlayManager.getMainWindow(),
getVisibleOverlayVisible: () => overlayManager.getVisibleOverlayVisible(),
setVisibleOverlayVisible: (visible) => setVisibleOverlayVisible(visible),
getResolver: () => getFieldGroupingResolverHandler(),
setResolver: (resolver) => setFieldGroupingResolverHandler(resolver),
getRestoreVisibleOverlayOnModalClose: () =>
overlayModalRuntime.getRestoreVisibleOverlayOnModalClose(),
// These are optional on the runtime options, so a missing forward compiles silently, but
// dropping them leaves the field grouping modal with no modal-open ack/retry, no teardown on
// failure, and no warn logging.
waitForModalOpen: (modal, timeoutMs) => overlayModalRuntime.waitForModalOpen(modal, timeoutMs),
handleOverlayModalClosed: (modal) => overlayModalRuntime.handleOverlayModalClosed(modal),
logWarn: (message) => logger.warn(message),
ensureOverlayStartupPrereqs: () => ensureOverlayStartupPrereqs(),
ensureOverlayWindowsReadyForVisibilityActions: () =>
ensureOverlayWindowsReadyForVisibilityActions(),
sendToVisibleOverlay: (channel, payload, runtimeOptions) =>
overlayModalRuntime.sendToActiveOverlayWindow(channel, payload, runtimeOptions),
});
const createFieldGroupingCallback = fieldGroupingOverlayRuntime.createFieldGroupingCallback;
const SUBTITLE_POSITIONS_DIR = path.join(CONFIG_DIR, 'subtitle-positions');
const mediaRuntime = createMediaRuntimeService(
createBuildMediaRuntimeMainDepsHandler({
isRemoteMediaPath: (mediaPath) => isRemoteMediaPath(mediaPath),
loadSubtitlePosition: () => loadSubtitlePosition(),
getCurrentMediaPath: () => appState.currentMediaPath,
getPendingSubtitlePosition: () => appState.pendingSubtitlePosition,
getSubtitlePositionsDir: () => SUBTITLE_POSITIONS_DIR,
setCurrentMediaPath: (nextPath: string | null) => {
appState.currentMediaPath = nextPath;
},
clearPendingSubtitlePosition: () => {
appState.pendingSubtitlePosition = null;
},
setSubtitlePosition: (position: SubtitlePosition | null) => {
appState.subtitlePosition = position;
},
broadcastToOverlayWindows: (channel, payload) => {
overlayManager.broadcastToOverlayWindows(channel, payload);
},
getCurrentMediaTitle: () => appState.currentMediaTitle,
setCurrentMediaTitle: (title) => {
appState.currentMediaTitle = title;
},
})(),
);
const mediaRuntime = createMediaRuntimeService({
isRemoteMediaPath: (mediaPath) => isRemoteMediaPath(mediaPath),
loadSubtitlePosition: () => loadSubtitlePosition(),
getCurrentMediaPath: () => appState.currentMediaPath,
getPendingSubtitlePosition: () => appState.pendingSubtitlePosition,
getSubtitlePositionsDir: () => SUBTITLE_POSITIONS_DIR,
setCurrentMediaPath: (nextPath: string | null) => {
appState.currentMediaPath = nextPath;
},
clearPendingSubtitlePosition: () => {
appState.pendingSubtitlePosition = null;
},
setSubtitlePosition: (position: SubtitlePosition | null) => {
appState.subtitlePosition = position;
},
broadcastSubtitlePosition: (position) => {
overlayManager.broadcastToOverlayWindows('subtitle-position:set', position);
},
getCurrentMediaTitle: () => appState.currentMediaTitle,
setCurrentMediaTitle: (title) => {
appState.currentMediaTitle = title;
},
});
const characterDictionaryRuntime = createCharacterDictionaryRuntimeService({
userDataPath: USER_DATA_PATH,
@@ -2993,20 +2928,10 @@ const visibleOverlayInteractionRuntime = createVisibleOverlayInteractionRuntime(
getOverlayForegroundSeparateWindows: () => getOverlayForegroundSeparateWindows(),
});
const buildGetRuntimeOptionsStateMainDepsHandler = createBuildGetRuntimeOptionsStateMainDepsHandler(
{
getRuntimeOptionsManager: () => appState.runtimeOptionsManager,
},
);
const getRuntimeOptionsStateMainDeps = buildGetRuntimeOptionsStateMainDepsHandler();
const getRuntimeOptionsStateHandler = createGetRuntimeOptionsStateHandler(
getRuntimeOptionsStateMainDeps,
);
const getRuntimeOptionsStateHandler = createGetRuntimeOptionsStateHandler({
getRuntimeOptionsManager: () => appState.runtimeOptionsManager,
});
const buildRestorePreviousSecondarySubVisibilityMainDepsHandler =
createBuildRestorePreviousSecondarySubVisibilityMainDepsHandler({
getMpvClient: () => appState.mpvClient,
});
syncOverlayVisibilityForModal = () => {
overlayVisibilityRuntime.updateVisibleOverlayVisibility();
};
@@ -3031,42 +2956,31 @@ const {
maybeStartOverlayLoadingOsd,
} = overlayNotificationsRuntime;
const buildBroadcastRuntimeOptionsChangedMainDepsHandler =
createBuildBroadcastRuntimeOptionsChangedMainDepsHandler({
const overlayVisibilityComposer = composeOverlayVisibilityRuntime({
overlayVisibilityRuntime,
restorePreviousSecondarySubVisibilityMainDeps: {
getMpvClient: () => appState.mpvClient,
},
broadcastRuntimeOptionsChangedMainDeps: {
broadcastRuntimeOptionsChangedRuntime,
getRuntimeOptionsState: () => getRuntimeOptionsStateHandler(),
broadcastToOverlayWindows: (channel, ...args) =>
overlayManager.broadcastToOverlayWindows(channel, ...args),
});
const buildSendToActiveOverlayWindowMainDepsHandler =
createBuildSendToActiveOverlayWindowMainDepsHandler({
},
sendToActiveOverlayWindowMainDeps: {
sendToActiveOverlayWindowRuntime: (channel, payload, runtimeOptions) =>
overlayModalRuntime.sendToActiveOverlayWindow(channel, payload, runtimeOptions),
});
const buildSetOverlayDebugVisualizationEnabledMainDepsHandler =
createBuildSetOverlayDebugVisualizationEnabledMainDepsHandler({
},
setOverlayDebugVisualizationEnabledMainDeps: {
setOverlayDebugVisualizationEnabledRuntime,
getCurrentEnabled: () => appState.overlayDebugVisualizationEnabled,
setCurrentEnabled: (next) => {
appState.overlayDebugVisualizationEnabled = next;
},
});
const buildOpenRuntimeOptionsPaletteMainDepsHandler =
createBuildOpenRuntimeOptionsPaletteMainDepsHandler({
},
openRuntimeOptionsPaletteMainDeps: {
openRuntimeOptionsPaletteRuntime: () => overlayModalRuntime.openRuntimeOptionsPalette(),
});
const overlayVisibilityComposer = composeOverlayVisibilityRuntime({
overlayVisibilityRuntime,
restorePreviousSecondarySubVisibilityMainDeps:
buildRestorePreviousSecondarySubVisibilityMainDepsHandler(),
broadcastRuntimeOptionsChangedMainDeps: buildBroadcastRuntimeOptionsChangedMainDepsHandler(),
sendToActiveOverlayWindowMainDeps: buildSendToActiveOverlayWindowMainDepsHandler(),
setOverlayDebugVisualizationEnabledMainDeps:
buildSetOverlayDebugVisualizationEnabledMainDepsHandler(),
openRuntimeOptionsPaletteMainDeps: buildOpenRuntimeOptionsPaletteMainDepsHandler(),
},
});
function createOverlayHostedModalOpenDeps(): {
@@ -3881,56 +3795,50 @@ const {
const maybeFocusExistingAnilistSetupWindow = createMaybeFocusExistingAnilistSetupWindowHandler({
getSetupWindow: () => appState.anilistSetupWindow,
});
const buildOpenAnilistSetupWindowMainDepsHandler = createBuildOpenAnilistSetupWindowMainDepsHandler(
{
maybeFocusExistingSetupWindow: maybeFocusExistingAnilistSetupWindow,
createSetupWindow: createCreateAnilistSetupWindowHandler({
createBrowserWindow: (options) => new BrowserWindow(options),
const openAnilistSetupWindowHandler = createOpenAnilistSetupWindowHandler({
maybeFocusExistingSetupWindow: maybeFocusExistingAnilistSetupWindow,
createSetupWindow: createCreateAnilistSetupWindowHandler({
createBrowserWindow: (options) => new BrowserWindow(options),
}),
buildAuthorizeUrl: () =>
buildAnilistSetupUrl({
authorizeUrl: ANILIST_SETUP_CLIENT_ID_URL,
clientId: ANILIST_DEFAULT_CLIENT_ID,
responseType: ANILIST_SETUP_RESPONSE_TYPE,
}),
buildAuthorizeUrl: () =>
buildAnilistSetupUrl({
authorizeUrl: ANILIST_SETUP_CLIENT_ID_URL,
clientId: ANILIST_DEFAULT_CLIENT_ID,
responseType: ANILIST_SETUP_RESPONSE_TYPE,
}),
consumeCallbackUrl: (rawUrl) => consumeAnilistSetupTokenFromUrl(rawUrl),
openSetupInBrowser: (authorizeUrl) =>
openAnilistSetupInBrowser({
authorizeUrl,
openExternal: (url) => shell.openExternal(url),
logError: (message, error) => logger.error(message, error),
}),
loadManualTokenEntry: (setupWindow, authorizeUrl) =>
loadAnilistManualTokenEntry({
setupWindow: setupWindow as BrowserWindow,
authorizeUrl,
developerSettingsUrl: ANILIST_DEVELOPER_SETTINGS_URL,
logWarn: (message, data) => logger.warn(message, data),
}),
redirectUri: ANILIST_REDIRECT_URI,
developerSettingsUrl: ANILIST_DEVELOPER_SETTINGS_URL,
isAllowedExternalUrl: (url) => isAllowedAnilistExternalUrl(url),
isAllowedNavigationUrl: (url) => isAllowedAnilistSetupNavigationUrl(url),
logWarn: (message, details) => logger.warn(message, details),
logError: (message, details) => logger.error(message, details),
clearSetupWindow: () => {
appState.anilistSetupWindow = null;
},
setSetupPageOpened: (opened) => {
appState.anilistSetupPageOpened = opened;
},
setSetupWindow: (setupWindow) => {
appState.anilistSetupWindow = setupWindow as BrowserWindow;
},
openExternal: (url) => {
void shell.openExternal(url);
},
consumeCallbackUrl: (rawUrl) => consumeAnilistSetupTokenFromUrl(rawUrl),
openSetupInBrowser: (authorizeUrl) =>
openAnilistSetupInBrowser({
authorizeUrl,
openExternal: (url) => shell.openExternal(url),
logError: (message, error) => logger.error(message, error),
}),
loadManualTokenEntry: (setupWindow, authorizeUrl) =>
loadAnilistManualTokenEntry({
setupWindow: setupWindow as BrowserWindow,
authorizeUrl,
developerSettingsUrl: ANILIST_DEVELOPER_SETTINGS_URL,
logWarn: (message, data) => logger.warn(message, data),
}),
redirectUri: ANILIST_REDIRECT_URI,
developerSettingsUrl: ANILIST_DEVELOPER_SETTINGS_URL,
isAllowedExternalUrl: (url) => isAllowedAnilistExternalUrl(url),
isAllowedNavigationUrl: (url) => isAllowedAnilistSetupNavigationUrl(url),
logWarn: (message, details) => logger.warn(message, details),
logError: (message, details) => logger.error(message, details),
clearSetupWindow: () => {
appState.anilistSetupWindow = null;
},
);
const openAnilistSetupWindowHandler = createOpenAnilistSetupWindowHandler(
buildOpenAnilistSetupWindowMainDepsHandler(),
);
setSetupPageOpened: (opened) => {
appState.anilistSetupPageOpened = opened;
},
setSetupWindow: (setupWindow) => {
appState.anilistSetupWindow = setupWindow as BrowserWindow;
},
openExternal: (url) => {
void shell.openExternal(url);
},
});
const {
refreshAnilistClientSecretState,
@@ -4191,7 +4099,7 @@ const rememberAnilistAttemptedUpdate = (key: string): void => {
);
};
const buildLoadSubtitlePositionMainDepsHandler = createBuildLoadSubtitlePositionMainDepsHandler({
const loadSubtitlePosition = createLoadSubtitlePositionHandler({
loadSubtitlePositionCore: () =>
loadSubtitlePositionCore({
currentMediaPath: appState.currentMediaPath,
@@ -4202,10 +4110,8 @@ const buildLoadSubtitlePositionMainDepsHandler = createBuildLoadSubtitlePosition
appState.subtitlePosition = position;
},
});
const loadSubtitlePositionMainDeps = buildLoadSubtitlePositionMainDepsHandler();
const loadSubtitlePosition = createLoadSubtitlePositionHandler(loadSubtitlePositionMainDeps);
const buildSaveSubtitlePositionMainDepsHandler = createBuildSaveSubtitlePositionMainDepsHandler({
const saveSubtitlePosition = createSaveSubtitlePositionHandler({
saveSubtitlePositionCore: (position) => {
saveSubtitlePositionCore({
position,
@@ -4223,8 +4129,6 @@ const buildSaveSubtitlePositionMainDepsHandler = createBuildSaveSubtitlePosition
appState.subtitlePosition = position;
},
});
const saveSubtitlePositionMainDeps = buildSaveSubtitlePositionMainDepsHandler();
const saveSubtitlePosition = createSaveSubtitlePositionHandler(saveSubtitlePositionMainDeps);
registerSubminerProtocolClient();
let flushPendingMpvLogWrites = (): void => {};
@@ -4377,9 +4281,7 @@ const resolveLegacyVocabularyPos = async (row: {
return null;
};
const immersionTrackerStartupMainDeps: Parameters<
typeof createBuildImmersionTrackerStartupMainDepsHandler
>[0] = {
const immersionTrackerStartupMainDeps: ImmersionTrackerStartupDeps = {
getResolvedConfig: () => configService.getConfig(),
getConfiguredDbPath: () => immersionMediaRuntime.getConfiguredDbPath(),
createTrackerService: (params) =>
@@ -4437,7 +4339,7 @@ const immersionTrackerStartupMainDeps: Parameters<
logWarn: (message, details) => logger.warn(message, details),
};
const createImmersionTrackerStartup = createImmersionTrackerStartupHandler(
createBuildImmersionTrackerStartupMainDepsHandler(immersionTrackerStartupMainDeps)(),
immersionTrackerStartupMainDeps,
);
const recordSubtitleMiningContext = (context: SubtitleMiningContext | null): void => {
pendingSubtitleMiningContext = context;
@@ -5538,8 +5440,8 @@ const {
buildRegisterGlobalShortcutsMainDeps: (getConfiguredShortcutsHandler) => ({
getConfiguredShortcuts: () => getConfiguredShortcutsHandler(),
registerGlobalShortcutsCore,
toggleVisibleOverlay: () => toggleVisibleOverlay(),
openYomitanSettings: () => openDictionarySettings(),
onToggleVisibleOverlay: () => toggleVisibleOverlay(),
onOpenYomitanSettings: () => openDictionarySettings(),
isDev,
getMainWindow: () => overlayManager.getMainWindow(),
}),
@@ -5668,8 +5570,8 @@ const cycleSecondarySubMode = createCycleSecondarySubModeRuntimeHandler({
setLastSecondarySubToggleAtMs: (timestampMs: number) => {
appState.lastSecondarySubToggleAtMs = timestampMs;
},
broadcastToOverlayWindows: (channel, mode) => {
overlayManager.broadcastToOverlayWindows(channel, mode);
broadcastSecondarySubMode: (mode) => {
overlayManager.broadcastToOverlayWindows('secondary-subtitle:mode', mode);
},
showMpvOsd: (text: string) => showConfiguredPlaybackFeedback(text),
},
@@ -5680,90 +5582,62 @@ function setSecondarySubMode(mode: SecondarySubMode): void {
appState.secondarySubMode = mode;
}
const buildUpdateLastCardFromClipboardMainDepsHandler =
createBuildUpdateLastCardFromClipboardMainDepsHandler({
getAnkiIntegration: () => appState.ankiIntegration,
readClipboardText: () => clipboard.readText(),
showMpvOsd: (text) => overlayNotificationsRuntime.showConfiguredStatusNotification(text),
updateLastCardFromClipboardCore,
});
const updateLastCardFromClipboardMainDeps = buildUpdateLastCardFromClipboardMainDepsHandler();
const updateLastCardFromClipboardHandler = createUpdateLastCardFromClipboardHandler(
updateLastCardFromClipboardMainDeps,
);
const updateLastCardFromClipboardHandler = createUpdateLastCardFromClipboardHandler({
getAnkiIntegration: () => appState.ankiIntegration,
readClipboardText: () => clipboard.readText(),
showMpvOsd: (text) => overlayNotificationsRuntime.showConfiguredStatusNotification(text),
updateLastCardFromClipboardCore,
});
const buildRefreshKnownWordCacheMainDepsHandler = createBuildRefreshKnownWordCacheMainDepsHandler({
const refreshKnownWordCacheHandler = createRefreshKnownWordCacheHandler({
getAnkiIntegration: () => appState.ankiIntegration,
missingIntegrationMessage: 'AnkiConnect integration not enabled',
});
const refreshKnownWordCacheMainDeps = buildRefreshKnownWordCacheMainDepsHandler();
const refreshKnownWordCacheHandler = createRefreshKnownWordCacheHandler(
refreshKnownWordCacheMainDeps,
);
const buildTriggerFieldGroupingMainDepsHandler = createBuildTriggerFieldGroupingMainDepsHandler({
const triggerFieldGroupingHandler = createTriggerFieldGroupingHandler({
getAnkiIntegration: () => appState.ankiIntegration,
showMpvOsd: (text) => overlayNotificationsRuntime.showConfiguredStatusNotification(text),
triggerFieldGroupingCore,
});
const triggerFieldGroupingMainDeps = buildTriggerFieldGroupingMainDepsHandler();
const triggerFieldGroupingHandler = createTriggerFieldGroupingHandler(triggerFieldGroupingMainDeps);
const buildMarkLastCardAsAudioCardMainDepsHandler =
createBuildMarkLastCardAsAudioCardMainDepsHandler({
getAnkiIntegration: () => appState.ankiIntegration,
showMpvOsd: (text) => overlayNotificationsRuntime.showConfiguredStatusNotification(text),
markLastCardAsAudioCardCore,
});
const markLastCardAsAudioCardMainDeps = buildMarkLastCardAsAudioCardMainDepsHandler();
const markLastCardAsAudioCardHandler = createMarkLastCardAsAudioCardHandler(
markLastCardAsAudioCardMainDeps,
);
const markLastCardAsAudioCardHandler = createMarkLastCardAsAudioCardHandler({
getAnkiIntegration: () => appState.ankiIntegration,
showMpvOsd: (text) => overlayNotificationsRuntime.showConfiguredStatusNotification(text),
markLastCardAsAudioCardCore,
});
const buildMineSentenceCardMainDepsHandler = createBuildMineSentenceCardMainDepsHandler({
const mineSentenceCardHandler = createMineSentenceCardHandler({
getAnkiIntegration: () => appState.ankiIntegration,
getMpvClient: () => appState.mpvClient,
getPrimarySubtitle: () => captureCurrentPrimarySubtitleMiningContext(),
showMpvOsd: (text) => overlayNotificationsRuntime.showConfiguredStatusNotification(text),
mineSentenceCardCore,
});
const mineSentenceCardHandler = createMineSentenceCardHandler(
buildMineSentenceCardMainDepsHandler(),
);
const buildHandleMultiCopyDigitMainDepsHandler = createBuildHandleMultiCopyDigitMainDepsHandler({
const handleMultiCopyDigitHandler = createHandleMultiCopyDigitHandler({
getSubtitleTimingTracker: () => appState.subtitleTimingTracker,
writeClipboardText: (text) => clipboard.writeText(text),
showMpvOsd: (text) => showConfiguredPlaybackFeedback(text),
handleMultiCopyDigitCore,
});
const handleMultiCopyDigitMainDeps = buildHandleMultiCopyDigitMainDepsHandler();
const handleMultiCopyDigitHandler = createHandleMultiCopyDigitHandler(handleMultiCopyDigitMainDeps);
const buildCopyCurrentSubtitleMainDepsHandler = createBuildCopyCurrentSubtitleMainDepsHandler({
const copyCurrentSubtitleHandler = createCopyCurrentSubtitleHandler({
getSubtitleTimingTracker: () => appState.subtitleTimingTracker,
writeClipboardText: (text) => clipboard.writeText(text),
showMpvOsd: (text) => overlayNotificationsRuntime.showConfiguredStatusNotification(text),
copyCurrentSubtitleCore,
});
const copyCurrentSubtitleMainDeps = buildCopyCurrentSubtitleMainDepsHandler();
const copyCurrentSubtitleHandler = createCopyCurrentSubtitleHandler(copyCurrentSubtitleMainDeps);
const buildHandleMineSentenceDigitMainDepsHandler =
createBuildHandleMineSentenceDigitMainDepsHandler({
getSubtitleTimingTracker: () => appState.subtitleTimingTracker,
getAnkiIntegration: () => appState.ankiIntegration,
getCurrentSecondarySubText: () => appState.mpvClient?.currentSecondarySubText || undefined,
showMpvOsd: (text) => overlayNotificationsRuntime.showConfiguredStatusNotification(text),
logError: (message, err) => {
logger.error(message, err);
},
handleMineSentenceDigitCore,
});
const handleMineSentenceDigitMainDeps = buildHandleMineSentenceDigitMainDepsHandler();
const handleMineSentenceDigitHandler = createHandleMineSentenceDigitHandler(
handleMineSentenceDigitMainDeps,
);
const handleMineSentenceDigitHandler = createHandleMineSentenceDigitHandler({
getSubtitleTimingTracker: () => appState.subtitleTimingTracker,
getAnkiIntegration: () => appState.ankiIntegration,
getCurrentSecondarySubText: () => appState.mpvClient?.currentSecondarySubText || undefined,
showMpvOsd: (text) => overlayNotificationsRuntime.showConfiguredStatusNotification(text),
logError: (message, err) => {
logger.error(message, err);
},
handleMineSentenceDigitCore,
});
const {
setVisibleOverlayVisible: setVisibleOverlayVisibleHandler,
toggleVisibleOverlay: toggleVisibleOverlayHandler,
@@ -5779,29 +5653,19 @@ const {
getVisibleOverlayVisible: () => overlayManager.getVisibleOverlayVisible(),
});
const buildHandleOverlayModalClosedMainDepsHandler =
createBuildHandleOverlayModalClosedMainDepsHandler({
handleOverlayModalClosedRuntime: (modal) => overlayModalRuntime.handleOverlayModalClosed(modal),
});
const handleOverlayModalClosedMainDeps = buildHandleOverlayModalClosedMainDepsHandler();
const handleOverlayModalClosedHandler = createHandleOverlayModalClosedHandler(
handleOverlayModalClosedMainDeps,
);
const handleOverlayModalClosedHandler = createHandleOverlayModalClosedHandler({
handleOverlayModalClosedRuntime: (modal) => overlayModalRuntime.handleOverlayModalClosed(modal),
});
const buildAppendClipboardVideoToQueueMainDepsHandler =
createBuildAppendClipboardVideoToQueueMainDepsHandler({
appendClipboardVideoToQueueRuntime,
getMpvClient: () => appState.mpvClient,
readClipboardText: () => clipboard.readText(),
showMpvOsd: (text) => overlayNotificationsRuntime.showConfiguredStatusNotification(text),
sendMpvCommand: (command) => {
sendMpvCommandRuntime(appState.mpvClient, command);
},
});
const appendClipboardVideoToQueueMainDeps = buildAppendClipboardVideoToQueueMainDepsHandler();
const appendClipboardVideoToQueueHandler = createAppendClipboardVideoToQueueHandler(
appendClipboardVideoToQueueMainDeps,
);
const appendClipboardVideoToQueueHandler = createAppendClipboardVideoToQueueHandler({
appendClipboardVideoToQueueRuntime,
getMpvClient: () => appState.mpvClient,
readClipboardText: () => clipboard.readText(),
showMpvOsd: (text) => overlayNotificationsRuntime.showConfiguredStatusNotification(text),
sendMpvCommand: (command) => {
sendMpvCommandRuntime(appState.mpvClient, command);
},
});
async function dispatchSessionAction(request: SessionActionDispatchRequest): Promise<void> {
await dispatchSessionActionCore(request, {
-19
View File
@@ -425,25 +425,6 @@ test('subtitle annotation updates invalidate prefetched tokenizations before ref
);
});
test('subtitle processing controller resumes prefetch on settle, not on its emits', () => {
const source = readMainSource();
const depsBlock = source.match(
/createBuildSubtitleProcessingControllerMainDepsHandler\(\{(?<body>[\s\S]*?)\n \}\);/,
)?.groups?.body;
assert.ok(depsBlock);
// A controller emit can be the provisional plain payload sent before the
// scan runs, so it must not release the prefetch pause.
assert.match(
depsBlock,
/emitSubtitle: \(payload\) => emitSubtitlePayload\(payload, \{ resumePrefetch: false \}\),/,
);
assert.match(
depsBlock,
/onProcessingSettled: \(\) => \{\s+subtitlePrefetchService\?\.resume\(\);/,
);
});
test('annotation upgrades skip the duplicate basic websocket event', () => {
const source = readMainSource();
const emitBlock = source.match(
@@ -1,37 +0,0 @@
import type {
createEnsureAnilistMediaGuessHandler,
createMaybeProbeAnilistDurationHandler,
} from './anilist-media-guess';
type MaybeProbeAnilistDurationMainDeps = Parameters<
typeof createMaybeProbeAnilistDurationHandler
>[0];
type EnsureAnilistMediaGuessMainDeps = Parameters<typeof createEnsureAnilistMediaGuessHandler>[0];
export function createBuildMaybeProbeAnilistDurationMainDepsHandler(
deps: MaybeProbeAnilistDurationMainDeps,
) {
return (): MaybeProbeAnilistDurationMainDeps => ({
getState: () => deps.getState(),
setState: (state) => deps.setState(state),
durationRetryIntervalMs: deps.durationRetryIntervalMs,
now: () => deps.now(),
requestMpvDuration: () => deps.requestMpvDuration(),
logWarn: (message: string, error: unknown) => deps.logWarn(message, error),
});
}
export function createBuildEnsureAnilistMediaGuessMainDepsHandler(
deps: EnsureAnilistMediaGuessMainDeps,
) {
return (): EnsureAnilistMediaGuessMainDeps => ({
getState: () => deps.getState(),
setState: (state) => deps.setState(state),
resolveMediaPathForJimaku: (currentMediaPath) =>
deps.resolveMediaPathForJimaku(currentMediaPath),
getCurrentMediaPath: () => deps.getCurrentMediaPath(),
getCurrentMediaTitle: () => deps.getCurrentMediaTitle(),
guessAnilistMediaInfo: (mediaPath, mediaTitle) =>
deps.guessAnilistMediaInfo(mediaPath, mediaTitle),
});
}
@@ -1,90 +0,0 @@
import type {
createGetAnilistMediaGuessRuntimeStateHandler,
createGetCurrentAnilistMediaKeyHandler,
createRecordAnilistMediaDurationHandler,
createResetAnilistMediaGuessStateHandler,
createResetAnilistMediaTrackingHandler,
createSetAnilistMediaGuessRuntimeStateHandler,
} from './anilist-media-state';
type GetCurrentAnilistMediaKeyMainDeps = Parameters<
typeof createGetCurrentAnilistMediaKeyHandler
>[0];
type ResetAnilistMediaTrackingMainDeps = Parameters<
typeof createResetAnilistMediaTrackingHandler
>[0];
type GetAnilistMediaGuessRuntimeStateMainDeps = Parameters<
typeof createGetAnilistMediaGuessRuntimeStateHandler
>[0];
type SetAnilistMediaGuessRuntimeStateMainDeps = Parameters<
typeof createSetAnilistMediaGuessRuntimeStateHandler
>[0];
type RecordAnilistMediaDurationMainDeps = Parameters<
typeof createRecordAnilistMediaDurationHandler
>[0];
type ResetAnilistMediaGuessStateMainDeps = Parameters<
typeof createResetAnilistMediaGuessStateHandler
>[0];
export function createBuildGetCurrentAnilistMediaKeyMainDepsHandler(
deps: GetCurrentAnilistMediaKeyMainDeps,
) {
return (): GetCurrentAnilistMediaKeyMainDeps => ({
getCurrentMediaPath: () => deps.getCurrentMediaPath(),
});
}
export function createBuildResetAnilistMediaTrackingMainDepsHandler(
deps: ResetAnilistMediaTrackingMainDeps,
) {
return (): ResetAnilistMediaTrackingMainDeps => ({
setMediaKey: (value) => deps.setMediaKey(value),
setMediaDurationSec: (value) => deps.setMediaDurationSec(value),
setMediaGuess: (value) => deps.setMediaGuess(value),
setMediaGuessPromise: (value) => deps.setMediaGuessPromise(value),
setLastDurationProbeAtMs: (value: number) => deps.setLastDurationProbeAtMs(value),
});
}
export function createBuildGetAnilistMediaGuessRuntimeStateMainDepsHandler(
deps: GetAnilistMediaGuessRuntimeStateMainDeps,
) {
return (): GetAnilistMediaGuessRuntimeStateMainDeps => ({
getMediaKey: () => deps.getMediaKey(),
getMediaDurationSec: () => deps.getMediaDurationSec(),
getMediaGuess: () => deps.getMediaGuess(),
getMediaGuessPromise: () => deps.getMediaGuessPromise(),
getLastDurationProbeAtMs: () => deps.getLastDurationProbeAtMs(),
});
}
export function createBuildSetAnilistMediaGuessRuntimeStateMainDepsHandler(
deps: SetAnilistMediaGuessRuntimeStateMainDeps,
) {
return (): SetAnilistMediaGuessRuntimeStateMainDeps => ({
setMediaKey: (value) => deps.setMediaKey(value),
setMediaDurationSec: (value) => deps.setMediaDurationSec(value),
setMediaGuess: (value) => deps.setMediaGuess(value),
setMediaGuessPromise: (value) => deps.setMediaGuessPromise(value),
setLastDurationProbeAtMs: (value: number) => deps.setLastDurationProbeAtMs(value),
});
}
export function createBuildRecordAnilistMediaDurationMainDepsHandler(
deps: RecordAnilistMediaDurationMainDeps,
) {
return (): RecordAnilistMediaDurationMainDeps => ({
getCurrentMediaKey: () => deps.getCurrentMediaKey(),
getState: () => deps.getState(),
setState: (state) => deps.setState(state),
});
}
export function createBuildResetAnilistMediaGuessStateMainDepsHandler(
deps: ResetAnilistMediaGuessStateMainDeps,
) {
return (): ResetAnilistMediaGuessStateMainDeps => ({
setMediaGuess: (value) => deps.setMediaGuess(value),
setMediaGuessPromise: (value) => deps.setMediaGuessPromise(value),
});
}
@@ -1,86 +0,0 @@
import type {
createMaybeRunAnilistPostWatchUpdateHandler,
createProcessNextAnilistRetryUpdateHandler,
} from './anilist-post-watch';
type ProcessNextAnilistRetryUpdateMainDeps = Parameters<
typeof createProcessNextAnilistRetryUpdateHandler
>[0];
type MaybeRunAnilistPostWatchUpdateMainDeps = Parameters<
typeof createMaybeRunAnilistPostWatchUpdateHandler
>[0];
export function createBuildProcessNextAnilistRetryUpdateMainDepsHandler(
deps: ProcessNextAnilistRetryUpdateMainDeps,
) {
return (): ProcessNextAnilistRetryUpdateMainDeps => ({
nextReady: () => deps.nextReady(),
refreshRetryQueueState: () => deps.refreshRetryQueueState(),
setLastAttemptAt: (value: number) => deps.setLastAttemptAt(value),
setLastError: (value: string | null) => deps.setLastError(value),
refreshAnilistClientSecretState: () => deps.refreshAnilistClientSecretState(),
updateAnilistPostWatchProgress: (
accessToken: string,
title: string,
episode: number,
season?: number | null,
mediaId?: number | null,
) => deps.updateAnilistPostWatchProgress(accessToken, title, episode, season, mediaId),
markSuccess: (key: string) => deps.markSuccess(key),
rememberAttemptedUpdateKey: (key: string) => deps.rememberAttemptedUpdateKey(key),
markFailure: (key: string, message: string) => deps.markFailure(key, message),
logInfo: (message: string) => deps.logInfo(message),
now: () => deps.now(),
});
}
export function createBuildMaybeRunAnilistPostWatchUpdateMainDepsHandler(
deps: MaybeRunAnilistPostWatchUpdateMainDeps,
) {
return (): MaybeRunAnilistPostWatchUpdateMainDeps => ({
getInFlight: () => deps.getInFlight(),
setInFlight: (value: boolean) => deps.setInFlight(value),
getResolvedConfig: () => deps.getResolvedConfig(),
isAnilistTrackingEnabled: (config) => deps.isAnilistTrackingEnabled(config),
getCurrentMediaKey: () => deps.getCurrentMediaKey(),
hasMpvClient: () => deps.hasMpvClient(),
getTrackedMediaKey: () => deps.getTrackedMediaKey(),
resetTrackedMedia: (mediaKey) => deps.resetTrackedMedia(mediaKey),
getWatchedSeconds: () => deps.getWatchedSeconds(),
maybeProbeAnilistDuration: (mediaKey: string, options) =>
deps.maybeProbeAnilistDuration(mediaKey, options),
ensureAnilistMediaGuess: (mediaKey: string) => deps.ensureAnilistMediaGuess(mediaKey),
hasAttemptedUpdateKey: (key: string) => deps.hasAttemptedUpdateKey(key),
processNextAnilistRetryUpdate: () => deps.processNextAnilistRetryUpdate(),
refreshAnilistClientSecretState: () => deps.refreshAnilistClientSecretState(),
enqueueRetry: (
key: string,
title: string,
episode: number,
season?: number | null,
mediaId?: number | null,
) => deps.enqueueRetry(key, title, episode, season, mediaId),
getCurrentMediaTitle: deps.getCurrentMediaTitle
? () => deps.getCurrentMediaTitle!()
: undefined,
resolvePinnedAnilistMediaId: deps.resolvePinnedAnilistMediaId
? (input) => deps.resolvePinnedAnilistMediaId!(input)
: undefined,
markRetryFailure: (key: string, message: string) => deps.markRetryFailure(key, message),
markRetrySuccess: (key: string) => deps.markRetrySuccess(key),
refreshRetryQueueState: () => deps.refreshRetryQueueState(),
updateAnilistPostWatchProgress: (
accessToken: string,
title: string,
episode: number,
season?: number | null,
mediaId?: number | null,
) => deps.updateAnilistPostWatchProgress(accessToken, title, episode, season, mediaId),
rememberAttemptedUpdateKey: (key: string) => deps.rememberAttemptedUpdateKey(key),
showMpvOsd: (message: string) => deps.showMpvOsd(message),
logInfo: (message: string) => deps.logInfo(message),
logWarn: (message: string) => deps.logWarn(message),
minWatchSeconds: deps.minWatchSeconds,
minWatchRatio: deps.minWatchRatio,
});
}
@@ -1,69 +0,0 @@
import type {
createConsumeAnilistSetupTokenFromUrlHandler,
createHandleAnilistSetupProtocolUrlHandler,
createNotifyAnilistSetupHandler,
createRegisterSubminerProtocolClientHandler,
} from './anilist-setup-protocol';
type NotifyAnilistSetupMainDeps = Parameters<typeof createNotifyAnilistSetupHandler>[0];
type ConsumeAnilistSetupTokenMainDeps = Parameters<
typeof createConsumeAnilistSetupTokenFromUrlHandler
>[0];
type HandleAnilistSetupProtocolUrlMainDeps = Parameters<
typeof createHandleAnilistSetupProtocolUrlHandler
>[0];
type RegisterSubminerProtocolClientMainDeps = Parameters<
typeof createRegisterSubminerProtocolClientHandler
>[0];
export function createBuildNotifyAnilistSetupMainDepsHandler(deps: NotifyAnilistSetupMainDeps) {
return (): NotifyAnilistSetupMainDeps => ({
getNotificationType: deps.getNotificationType ? () => deps.getNotificationType?.() : undefined,
hasMpvClient: () => deps.hasMpvClient(),
showMpvOsd: (message: string) => deps.showMpvOsd(message),
showOverlayNotification: deps.showOverlayNotification
? (payload) => deps.showOverlayNotification?.(payload)
: undefined,
showDesktopNotification: (title: string, options: { body: string }) =>
deps.showDesktopNotification(title, options),
logInfo: (message: string) => deps.logInfo(message),
});
}
export function createBuildConsumeAnilistSetupTokenFromUrlMainDepsHandler(
deps: ConsumeAnilistSetupTokenMainDeps,
) {
return (): ConsumeAnilistSetupTokenMainDeps => ({
consumeAnilistSetupCallbackUrl: (input) => deps.consumeAnilistSetupCallbackUrl(input),
saveToken: (token: string) => deps.saveToken(token),
setCachedToken: (token: string) => deps.setCachedToken(token),
setResolvedState: (resolvedAt: number) => deps.setResolvedState(resolvedAt),
setSetupPageOpened: (opened: boolean) => deps.setSetupPageOpened(opened),
onSuccess: () => deps.onSuccess(),
closeWindow: () => deps.closeWindow(),
});
}
export function createBuildHandleAnilistSetupProtocolUrlMainDepsHandler(
deps: HandleAnilistSetupProtocolUrlMainDeps,
) {
return (): HandleAnilistSetupProtocolUrlMainDeps => ({
consumeAnilistSetupTokenFromUrl: (rawUrl: string) =>
deps.consumeAnilistSetupTokenFromUrl(rawUrl),
logWarn: (message: string, details: unknown) => deps.logWarn(message, details),
});
}
export function createBuildRegisterSubminerProtocolClientMainDepsHandler(
deps: RegisterSubminerProtocolClientMainDeps,
) {
return (): RegisterSubminerProtocolClientMainDeps => ({
isDefaultApp: () => deps.isDefaultApp(),
getArgv: () => deps.getArgv(),
execPath: deps.execPath,
resolvePath: (value: string) => deps.resolvePath(value),
setAsDefaultProtocolClient: (scheme: string, path?: string, args?: string[]) =>
deps.setAsDefaultProtocolClient(scheme, path, args),
logDebug: (message: string, details?: unknown) => deps.logDebug(message, details),
});
}
@@ -1,27 +0,0 @@
import type { createOpenAnilistSetupWindowHandler } from './anilist-setup-window';
type OpenAnilistSetupWindowMainDeps = Parameters<typeof createOpenAnilistSetupWindowHandler>[0];
export function createBuildOpenAnilistSetupWindowMainDepsHandler(
deps: OpenAnilistSetupWindowMainDeps,
) {
return (): OpenAnilistSetupWindowMainDeps => ({
maybeFocusExistingSetupWindow: () => deps.maybeFocusExistingSetupWindow(),
createSetupWindow: () => deps.createSetupWindow(),
buildAuthorizeUrl: () => deps.buildAuthorizeUrl(),
consumeCallbackUrl: (rawUrl: string) => deps.consumeCallbackUrl(rawUrl),
openSetupInBrowser: (authorizeUrl: string) => deps.openSetupInBrowser(authorizeUrl),
loadManualTokenEntry: (setupWindow, authorizeUrl: string) =>
deps.loadManualTokenEntry(setupWindow, authorizeUrl),
redirectUri: deps.redirectUri,
developerSettingsUrl: deps.developerSettingsUrl,
isAllowedExternalUrl: (url: string) => deps.isAllowedExternalUrl(url),
isAllowedNavigationUrl: (url: string) => deps.isAllowedNavigationUrl(url),
logWarn: (message: string, details?: unknown) => deps.logWarn(message, details),
logError: (message: string, details: unknown) => deps.logError(message, details),
clearSetupWindow: () => deps.clearSetupWindow(),
setSetupPageOpened: (opened: boolean) => deps.setSetupPageOpened(opened),
setSetupWindow: (setupWindow) => deps.setSetupWindow(setupWindow),
openExternal: (url: string) => deps.openExternal(url),
});
}
@@ -1,23 +0,0 @@
import type { createRefreshAnilistClientSecretStateHandler } from './anilist-token-refresh';
type RefreshAnilistClientSecretStateMainDeps = Parameters<
typeof createRefreshAnilistClientSecretStateHandler
>[0];
export function createBuildRefreshAnilistClientSecretStateMainDepsHandler(
deps: RefreshAnilistClientSecretStateMainDeps,
) {
return (): RefreshAnilistClientSecretStateMainDeps => ({
getResolvedConfig: () => deps.getResolvedConfig(),
isAnilistTrackingEnabled: (config) => deps.isAnilistTrackingEnabled(config),
getCachedAccessToken: () => deps.getCachedAccessToken(),
setCachedAccessToken: (token) => deps.setCachedAccessToken(token),
saveStoredToken: (token: string) => deps.saveStoredToken(token),
loadStoredToken: () => deps.loadStoredToken(),
setClientSecretState: (state) => deps.setClientSecretState(state),
getAnilistSetupPageOpened: () => deps.getAnilistSetupPageOpened(),
setAnilistSetupPageOpened: (opened: boolean) => deps.setAnilistSetupPageOpened(opened),
openAnilistSetupWindow: () => deps.openAnilistSetupWindow(),
now: () => deps.now(),
});
}
@@ -1,98 +0,0 @@
import type { createRefreshKnownWordCacheHandler, PrimarySubtitle } from './anki-actions';
type RefreshKnownWordCacheMainDeps = Parameters<typeof createRefreshKnownWordCacheHandler>[0];
export function createBuildUpdateLastCardFromClipboardMainDepsHandler<TAnki>(deps: {
getAnkiIntegration: () => TAnki;
readClipboardText: () => string;
showMpvOsd: (text: string) => void;
updateLastCardFromClipboardCore: (options: {
ankiIntegration: TAnki;
readClipboardText: () => string;
showMpvOsd: (text: string) => void;
}) => Promise<void>;
}) {
return () => ({
getAnkiIntegration: () => deps.getAnkiIntegration(),
readClipboardText: () => deps.readClipboardText(),
showMpvOsd: (text: string) => deps.showMpvOsd(text),
updateLastCardFromClipboardCore: (options: {
ankiIntegration: TAnki;
readClipboardText: () => string;
showMpvOsd: (text: string) => void;
}) => deps.updateLastCardFromClipboardCore(options),
});
}
export function createBuildRefreshKnownWordCacheMainDepsHandler(
deps: RefreshKnownWordCacheMainDeps,
) {
return (): RefreshKnownWordCacheMainDeps => ({
getAnkiIntegration: () => deps.getAnkiIntegration(),
missingIntegrationMessage: deps.missingIntegrationMessage,
});
}
export function createBuildTriggerFieldGroupingMainDepsHandler<TAnki>(deps: {
getAnkiIntegration: () => TAnki;
showMpvOsd: (text: string) => void;
triggerFieldGroupingCore: (options: {
ankiIntegration: TAnki;
showMpvOsd: (text: string) => void;
}) => Promise<void>;
}) {
return () => ({
getAnkiIntegration: () => deps.getAnkiIntegration(),
showMpvOsd: (text: string) => deps.showMpvOsd(text),
triggerFieldGroupingCore: (options: {
ankiIntegration: TAnki;
showMpvOsd: (text: string) => void;
}) => deps.triggerFieldGroupingCore(options),
});
}
export function createBuildMarkLastCardAsAudioCardMainDepsHandler<TAnki>(deps: {
getAnkiIntegration: () => TAnki;
showMpvOsd: (text: string) => void;
markLastCardAsAudioCardCore: (options: {
ankiIntegration: TAnki;
showMpvOsd: (text: string) => void;
}) => Promise<void>;
}) {
return () => ({
getAnkiIntegration: () => deps.getAnkiIntegration(),
showMpvOsd: (text: string) => deps.showMpvOsd(text),
markLastCardAsAudioCardCore: (options: {
ankiIntegration: TAnki;
showMpvOsd: (text: string) => void;
}) => deps.markLastCardAsAudioCardCore(options),
});
}
export function createBuildMineSentenceCardMainDepsHandler<TAnki, TMpv>(deps: {
getAnkiIntegration: () => TAnki;
getMpvClient: () => TMpv;
getPrimarySubtitle?: () => PrimarySubtitle | null;
showMpvOsd: (text: string) => void;
mineSentenceCardCore: (options: {
ankiIntegration: TAnki;
mpvClient: TMpv;
primarySubtitle?: PrimarySubtitle;
showMpvOsd: (text: string) => void;
}) => Promise<boolean>;
}) {
return () => ({
getAnkiIntegration: () => deps.getAnkiIntegration(),
getMpvClient: () => deps.getMpvClient(),
...(deps.getPrimarySubtitle
? { getPrimarySubtitle: () => deps.getPrimarySubtitle?.() ?? null }
: {}),
showMpvOsd: (text: string) => deps.showMpvOsd(text),
mineSentenceCardCore: (options: {
ankiIntegration: TAnki;
mpvClient: TMpv;
primarySubtitle?: PrimarySubtitle;
showMpvOsd: (text: string) => void;
}) => deps.mineSentenceCardCore(options),
});
}
-51
View File
@@ -1,51 +0,0 @@
import type { AppReadyRuntimeDepsFactoryInput } from '../app-lifecycle';
export function createBuildAppReadyRuntimeMainDepsHandler(deps: AppReadyRuntimeDepsFactoryInput) {
return (): AppReadyRuntimeDepsFactoryInput => ({
ensureDefaultConfigBootstrap: deps.ensureDefaultConfigBootstrap,
loadSubtitlePosition: deps.loadSubtitlePosition,
resolveKeybindings: deps.resolveKeybindings,
createMpvClient: deps.createMpvClient,
reloadConfig: deps.reloadConfig,
getResolvedConfig: deps.getResolvedConfig,
getConfigWarnings: deps.getConfigWarnings,
logConfigWarning: deps.logConfigWarning,
initRuntimeOptionsManager: deps.initRuntimeOptionsManager,
setSecondarySubMode: deps.setSecondarySubMode,
defaultSecondarySubMode: deps.defaultSecondarySubMode,
defaultWebsocketPort: deps.defaultWebsocketPort,
defaultAnnotationWebsocketPort: deps.defaultAnnotationWebsocketPort,
defaultTexthookerPort: deps.defaultTexthookerPort,
hasMpvWebsocketPlugin: deps.hasMpvWebsocketPlugin,
startSubtitleWebsocket: deps.startSubtitleWebsocket,
startAnnotationWebsocket: deps.startAnnotationWebsocket,
startTexthooker: deps.startTexthooker,
log: deps.log,
setLogLevel: deps.setLogLevel,
setLogRotation: deps.setLogRotation,
setLogFileToggles: deps.setLogFileToggles,
createMecabTokenizerAndCheck: deps.createMecabTokenizerAndCheck,
createSubtitleTimingTracker: deps.createSubtitleTimingTracker,
createImmersionTracker: deps.createImmersionTracker,
startJellyfinRemoteSession: deps.startJellyfinRemoteSession,
loadYomitanExtension: deps.loadYomitanExtension,
ensureYomitanExtensionLoaded: deps.ensureYomitanExtensionLoaded,
handleFirstRunSetup: deps.handleFirstRunSetup,
prewarmSubtitleDictionaries: deps.prewarmSubtitleDictionaries,
startBackgroundWarmups: deps.startBackgroundWarmups,
texthookerOnlyMode: deps.texthookerOnlyMode,
shouldAutoInitializeOverlayRuntimeFromConfig: deps.shouldAutoInitializeOverlayRuntimeFromConfig,
setVisibleOverlayVisible: deps.setVisibleOverlayVisible,
initializeOverlayRuntime: deps.initializeOverlayRuntime,
runHeadlessInitialCommand: deps.runHeadlessInitialCommand,
handleInitialArgs: deps.handleInitialArgs,
onCriticalConfigErrors: deps.onCriticalConfigErrors,
logDebug: deps.logDebug,
now: deps.now,
shouldRunHeadlessInitialCommand: deps.shouldRunHeadlessInitialCommand,
shouldUseMinimalStartup: deps.shouldUseMinimalStartup,
shouldSkipHeavyStartup: deps.shouldSkipHeavyStartup,
shouldHandleInitialArgsBeforeDeferredOverlayWarmup:
deps.shouldHandleInitialArgsBeforeDeferredOverlayWarmup,
});
}
@@ -1,20 +0,0 @@
import type { CliArgs } from '../../cli/args';
export function createBuildHandleTexthookerOnlyModeTransitionMainDepsHandler(deps: {
isTexthookerOnlyMode: () => boolean;
setTexthookerOnlyMode: (enabled: boolean) => void;
commandNeedsOverlayStartupPrereqs: (args: CliArgs) => boolean;
ensureOverlayStartupPrereqs: () => void;
startBackgroundWarmups: () => void;
logInfo: (message: string) => void;
}) {
return () => ({
isTexthookerOnlyMode: () => deps.isTexthookerOnlyMode(),
setTexthookerOnlyMode: (enabled: boolean) => deps.setTexthookerOnlyMode(enabled),
commandNeedsOverlayStartupPrereqs: (args: CliArgs) =>
deps.commandNeedsOverlayStartupPrereqs(args),
ensureOverlayStartupPrereqs: () => deps.ensureOverlayStartupPrereqs(),
startBackgroundWarmups: () => deps.startBackgroundWarmups(),
logInfo: (message: string) => deps.logInfo(message),
});
}
@@ -1,9 +1,8 @@
import type { CliArgs, CliCommandSource } from '../../cli/args';
import { createHandleTexthookerOnlyModeTransitionHandler } from './cli-command-prechecks';
import { createBuildHandleTexthookerOnlyModeTransitionMainDepsHandler } from './cli-command-prechecks-main-deps';
type HandleTexthookerOnlyModeTransitionMainDeps = Parameters<
typeof createBuildHandleTexthookerOnlyModeTransitionMainDepsHandler
typeof createHandleTexthookerOnlyModeTransitionHandler
>[0];
export function createCliCommandRuntimeHandler<TCliContext>(deps: {
@@ -17,9 +16,7 @@ export function createCliCommandRuntimeHandler<TCliContext>(deps: {
) => void;
}) {
const handleTexthookerOnlyModeTransitionHandler = createHandleTexthookerOnlyModeTransitionHandler(
createBuildHandleTexthookerOnlyModeTransitionMainDepsHandler(
deps.handleTexthookerOnlyModeTransitionMainDeps,
)(),
deps.handleTexthookerOnlyModeTransitionMainDeps,
);
return (args: CliArgs, source: CliCommandSource = 'initial'): void => {
@@ -1,8 +1,4 @@
import {
createBuildConsumeAnilistSetupTokenFromUrlMainDepsHandler,
createBuildHandleAnilistSetupProtocolUrlMainDepsHandler,
createBuildNotifyAnilistSetupMainDepsHandler,
createBuildRegisterSubminerProtocolClientMainDepsHandler,
createConsumeAnilistSetupTokenFromUrlHandler,
createHandleAnilistSetupProtocolUrlHandler,
createNotifyAnilistSetupHandler,
@@ -16,12 +12,10 @@ type HandleProtocolHandler = ReturnType<typeof createHandleAnilistSetupProtocolU
type RegisterClientHandler = ReturnType<typeof createRegisterSubminerProtocolClientHandler>;
export type AnilistSetupComposerOptions = ComposerInputs<{
notifyDeps: Parameters<typeof createBuildNotifyAnilistSetupMainDepsHandler>[0];
consumeTokenDeps: Parameters<typeof createBuildConsumeAnilistSetupTokenFromUrlMainDepsHandler>[0];
handleProtocolDeps: Parameters<typeof createBuildHandleAnilistSetupProtocolUrlMainDepsHandler>[0];
registerProtocolClientDeps: Parameters<
typeof createBuildRegisterSubminerProtocolClientMainDepsHandler
>[0];
notifyDeps: Parameters<typeof createNotifyAnilistSetupHandler>[0];
consumeTokenDeps: Parameters<typeof createConsumeAnilistSetupTokenFromUrlHandler>[0];
handleProtocolDeps: Parameters<typeof createHandleAnilistSetupProtocolUrlHandler>[0];
registerProtocolClientDeps: Parameters<typeof createRegisterSubminerProtocolClientHandler>[0];
}>;
export type AnilistSetupComposerResult = ComposerOutputs<{
@@ -34,17 +28,15 @@ export type AnilistSetupComposerResult = ComposerOutputs<{
export function composeAnilistSetupHandlers(
options: AnilistSetupComposerOptions,
): AnilistSetupComposerResult {
const notifyAnilistSetup = createNotifyAnilistSetupHandler(
createBuildNotifyAnilistSetupMainDepsHandler(options.notifyDeps)(),
);
const notifyAnilistSetup = createNotifyAnilistSetupHandler(options.notifyDeps);
const consumeAnilistSetupTokenFromUrl = createConsumeAnilistSetupTokenFromUrlHandler(
createBuildConsumeAnilistSetupTokenFromUrlMainDepsHandler(options.consumeTokenDeps)(),
options.consumeTokenDeps,
);
const handleAnilistSetupProtocolUrl = createHandleAnilistSetupProtocolUrlHandler(
createBuildHandleAnilistSetupProtocolUrlMainDepsHandler(options.handleProtocolDeps)(),
options.handleProtocolDeps,
);
const registerSubminerProtocolClient = createRegisterSubminerProtocolClientHandler(
createBuildRegisterSubminerProtocolClientMainDepsHandler(options.registerProtocolClientDeps)(),
options.registerProtocolClientDeps,
);
return {
@@ -1,15 +1,4 @@
import {
createBuildEnsureAnilistMediaGuessMainDepsHandler,
createBuildGetAnilistMediaGuessRuntimeStateMainDepsHandler,
createBuildGetCurrentAnilistMediaKeyMainDepsHandler,
createBuildMaybeProbeAnilistDurationMainDepsHandler,
createBuildMaybeRunAnilistPostWatchUpdateMainDepsHandler,
createBuildProcessNextAnilistRetryUpdateMainDepsHandler,
createBuildRecordAnilistMediaDurationMainDepsHandler,
createBuildRefreshAnilistClientSecretStateMainDepsHandler,
createBuildResetAnilistMediaGuessStateMainDepsHandler,
createBuildResetAnilistMediaTrackingMainDepsHandler,
createBuildSetAnilistMediaGuessRuntimeStateMainDepsHandler,
createEnsureAnilistMediaGuessHandler,
createGetAnilistMediaGuessRuntimeStateHandler,
createGetCurrentAnilistMediaKeyHandler,
@@ -25,36 +14,22 @@ import {
import type { ComposerInputs, ComposerOutputs } from './contracts';
export type AnilistTrackingComposerOptions = ComposerInputs<{
refreshClientSecretMainDeps: Parameters<
typeof createBuildRefreshAnilistClientSecretStateMainDepsHandler
>[0];
getCurrentMediaKeyMainDeps: Parameters<
typeof createBuildGetCurrentAnilistMediaKeyMainDepsHandler
>[0];
resetMediaTrackingMainDeps: Parameters<
typeof createBuildResetAnilistMediaTrackingMainDepsHandler
>[0];
refreshClientSecretMainDeps: Parameters<typeof createRefreshAnilistClientSecretStateHandler>[0];
getCurrentMediaKeyMainDeps: Parameters<typeof createGetCurrentAnilistMediaKeyHandler>[0];
resetMediaTrackingMainDeps: Parameters<typeof createResetAnilistMediaTrackingHandler>[0];
getMediaGuessRuntimeStateMainDeps: Parameters<
typeof createBuildGetAnilistMediaGuessRuntimeStateMainDepsHandler
typeof createGetAnilistMediaGuessRuntimeStateHandler
>[0];
setMediaGuessRuntimeStateMainDeps: Parameters<
typeof createBuildSetAnilistMediaGuessRuntimeStateMainDepsHandler
>[0];
recordMediaDurationMainDeps: Parameters<
typeof createBuildRecordAnilistMediaDurationMainDepsHandler
>[0];
resetMediaGuessStateMainDeps: Parameters<
typeof createBuildResetAnilistMediaGuessStateMainDepsHandler
>[0];
maybeProbeDurationMainDeps: Parameters<
typeof createBuildMaybeProbeAnilistDurationMainDepsHandler
>[0];
ensureMediaGuessMainDeps: Parameters<typeof createBuildEnsureAnilistMediaGuessMainDepsHandler>[0];
processNextRetryUpdateMainDeps: Parameters<
typeof createBuildProcessNextAnilistRetryUpdateMainDepsHandler
typeof createSetAnilistMediaGuessRuntimeStateHandler
>[0];
recordMediaDurationMainDeps: Parameters<typeof createRecordAnilistMediaDurationHandler>[0];
resetMediaGuessStateMainDeps: Parameters<typeof createResetAnilistMediaGuessStateHandler>[0];
maybeProbeDurationMainDeps: Parameters<typeof createMaybeProbeAnilistDurationHandler>[0];
ensureMediaGuessMainDeps: Parameters<typeof createEnsureAnilistMediaGuessHandler>[0];
processNextRetryUpdateMainDeps: Parameters<typeof createProcessNextAnilistRetryUpdateHandler>[0];
maybeRunPostWatchUpdateMainDeps: Parameters<
typeof createBuildMaybeRunAnilistPostWatchUpdateMainDepsHandler
typeof createMaybeRunAnilistPostWatchUpdateHandler
>[0];
}>;
@@ -80,47 +55,37 @@ export function composeAnilistTrackingHandlers(
options: AnilistTrackingComposerOptions,
): AnilistTrackingComposerResult {
const refreshAnilistClientSecretState = createRefreshAnilistClientSecretStateHandler(
createBuildRefreshAnilistClientSecretStateMainDepsHandler(
options.refreshClientSecretMainDeps,
)(),
options.refreshClientSecretMainDeps,
);
const getCurrentAnilistMediaKey = createGetCurrentAnilistMediaKeyHandler(
createBuildGetCurrentAnilistMediaKeyMainDepsHandler(options.getCurrentMediaKeyMainDeps)(),
options.getCurrentMediaKeyMainDeps,
);
const resetAnilistMediaTracking = createResetAnilistMediaTrackingHandler(
createBuildResetAnilistMediaTrackingMainDepsHandler(options.resetMediaTrackingMainDeps)(),
options.resetMediaTrackingMainDeps,
);
const getAnilistMediaGuessRuntimeState = createGetAnilistMediaGuessRuntimeStateHandler(
createBuildGetAnilistMediaGuessRuntimeStateMainDepsHandler(
options.getMediaGuessRuntimeStateMainDeps,
)(),
options.getMediaGuessRuntimeStateMainDeps,
);
const setAnilistMediaGuessRuntimeState = createSetAnilistMediaGuessRuntimeStateHandler(
createBuildSetAnilistMediaGuessRuntimeStateMainDepsHandler(
options.setMediaGuessRuntimeStateMainDeps,
)(),
options.setMediaGuessRuntimeStateMainDeps,
);
const recordAnilistMediaDuration = createRecordAnilistMediaDurationHandler(
createBuildRecordAnilistMediaDurationMainDepsHandler(options.recordMediaDurationMainDeps)(),
options.recordMediaDurationMainDeps,
);
const resetAnilistMediaGuessState = createResetAnilistMediaGuessStateHandler(
createBuildResetAnilistMediaGuessStateMainDepsHandler(options.resetMediaGuessStateMainDeps)(),
options.resetMediaGuessStateMainDeps,
);
const maybeProbeAnilistDuration = createMaybeProbeAnilistDurationHandler(
createBuildMaybeProbeAnilistDurationMainDepsHandler(options.maybeProbeDurationMainDeps)(),
options.maybeProbeDurationMainDeps,
);
const ensureAnilistMediaGuess = createEnsureAnilistMediaGuessHandler(
createBuildEnsureAnilistMediaGuessMainDepsHandler(options.ensureMediaGuessMainDeps)(),
options.ensureMediaGuessMainDeps,
);
const processNextAnilistRetryUpdate = createProcessNextAnilistRetryUpdateHandler(
createBuildProcessNextAnilistRetryUpdateMainDepsHandler(
options.processNextRetryUpdateMainDeps,
)(),
options.processNextRetryUpdateMainDeps,
);
const maybeRunAnilistPostWatchUpdate = createMaybeRunAnilistPostWatchUpdateHandler(
createBuildMaybeRunAnilistPostWatchUpdateMainDepsHandler(
options.maybeRunPostWatchUpdateMainDeps,
)(),
options.maybeRunPostWatchUpdateMainDeps,
);
return {
@@ -1,27 +1,22 @@
import { createAppReadyRuntimeRunner } from '../../app-lifecycle';
import { createBuildAppReadyRuntimeMainDepsHandler } from '../app-ready-main-deps';
import {
createBuildCriticalConfigErrorMainDepsHandler,
createBuildReloadConfigMainDepsHandler,
} from '../startup-config-main-deps';
createAppReadyRuntimeRunner,
type AppReadyRuntimeDepsFactoryInput,
} from '../../app-lifecycle';
import { createCriticalConfigErrorHandler, createReloadConfigHandler } from '../startup-config';
import { createBuildImmersionTrackerStartupMainDepsHandler } from '../immersion-startup-main-deps';
import { createImmersionTrackerStartupHandler } from '../immersion-startup';
import type { ComposerInputs, ComposerOutputs } from './contracts';
type ReloadConfigMainDeps = Parameters<typeof createBuildReloadConfigMainDepsHandler>[0];
type CriticalConfigErrorMainDeps = Parameters<
typeof createBuildCriticalConfigErrorMainDepsHandler
>[0];
type AppReadyRuntimeMainDeps = Parameters<typeof createBuildAppReadyRuntimeMainDepsHandler>[0];
type ReloadConfigMainDeps = Parameters<typeof createReloadConfigHandler>[0];
type CriticalConfigErrorMainDeps = Parameters<typeof createCriticalConfigErrorHandler>[0];
export type AppReadyComposerOptions = ComposerInputs<{
reloadConfigMainDeps: ReloadConfigMainDeps;
criticalConfigErrorMainDeps: CriticalConfigErrorMainDeps;
appReadyRuntimeMainDeps: Omit<AppReadyRuntimeMainDeps, 'reloadConfig' | 'onCriticalConfigErrors'>;
immersionTrackerStartupMainDeps: Parameters<
typeof createBuildImmersionTrackerStartupMainDepsHandler
>[0];
appReadyRuntimeMainDeps: Omit<
AppReadyRuntimeDepsFactoryInput,
'reloadConfig' | 'onCriticalConfigErrors'
>;
immersionTrackerStartupMainDeps: Parameters<typeof createImmersionTrackerStartupHandler>[0];
}>;
export type AppReadyComposerResult = ComposerOutputs<{
@@ -31,27 +26,17 @@ export type AppReadyComposerResult = ComposerOutputs<{
}>;
export function composeAppReadyRuntime(options: AppReadyComposerOptions): AppReadyComposerResult {
const reloadConfig = createReloadConfigHandler(
createBuildReloadConfigMainDepsHandler(options.reloadConfigMainDeps)(),
);
const criticalConfigError = createCriticalConfigErrorHandler(
createBuildCriticalConfigErrorMainDepsHandler(options.criticalConfigErrorMainDeps)(),
);
const reloadConfig = createReloadConfigHandler(options.reloadConfigMainDeps);
const criticalConfigError = createCriticalConfigErrorHandler(options.criticalConfigErrorMainDeps);
const appReadyRuntimeRunner = createAppReadyRuntimeRunner(
createBuildAppReadyRuntimeMainDepsHandler({
...options.appReadyRuntimeMainDeps,
reloadConfig,
createImmersionTracker:
options.appReadyRuntimeMainDeps.createImmersionTracker ??
createImmersionTrackerStartupHandler(
createBuildImmersionTrackerStartupMainDepsHandler(
options.immersionTrackerStartupMainDeps,
)(),
),
onCriticalConfigErrors: criticalConfigError,
})(),
);
const appReadyRuntimeRunner = createAppReadyRuntimeRunner({
...options.appReadyRuntimeMainDeps,
reloadConfig,
createImmersionTracker:
options.appReadyRuntimeMainDeps.createImmersionTracker ??
createImmersionTrackerStartupHandler(options.immersionTrackerStartupMainDeps),
onCriticalConfigErrors: criticalConfigError,
});
return {
reloadConfig,
@@ -1,9 +1,7 @@
import type { RegisterIpcRuntimeServicesParams } from '../../ipc-runtime';
import type { MpvCommandFromIpcRuntimeDeps } from '../../ipc-mpv-command';
import type { SubsyncManualRunRequest, SubsyncResult } from '../../../types';
import {
createBuildMpvCommandFromIpcRuntimeMainDepsHandler,
createIpcRuntimeHandlers,
} from '../domains/ipc';
import { createIpcRuntimeHandlers } from '../domains/ipc';
import type { ComposerInputs, ComposerOutputs } from './contracts';
type MpvCommand = (string | number)[];
@@ -17,7 +15,7 @@ type IpcRuntimeDeps = Parameters<
>[0];
export type IpcRuntimeComposerOptions = ComposerInputs<{
mpvCommandMainDeps: Parameters<typeof createBuildMpvCommandFromIpcRuntimeMainDepsHandler>[0];
mpvCommandMainDeps: MpvCommandFromIpcRuntimeDeps;
handleMpvCommandFromIpcRuntime: IpcRuntimeDeps['handleMpvCommandFromIpcDeps']['handleMpvCommandFromIpcRuntime'];
runSubsyncManualFromIpc: RunSubsyncManual;
registration: {
@@ -37,16 +35,13 @@ export type IpcRuntimeComposerResult = ComposerOutputs<{
export function composeIpcRuntimeHandlers(
options: IpcRuntimeComposerOptions,
): IpcRuntimeComposerResult {
const mpvCommandFromIpcRuntimeMainDeps = createBuildMpvCommandFromIpcRuntimeMainDepsHandler(
options.mpvCommandMainDeps,
)();
const { handleMpvCommandFromIpc, runSubsyncManualFromIpc } = createIpcRuntimeHandlers<
SubsyncManualRunRequest,
SubsyncResult
>({
handleMpvCommandFromIpcDeps: {
handleMpvCommandFromIpcRuntime: options.handleMpvCommandFromIpcRuntime,
buildMpvCommandDeps: () => mpvCommandFromIpcRuntimeMainDeps,
buildMpvCommandDeps: () => options.mpvCommandMainDeps,
},
runSubsyncManualFromIpcDeps: {
runManualFromIpc: (request) => options.runSubsyncManualFromIpc(request),
@@ -1,21 +1,9 @@
import {
buildJellyfinSetupFormHtml,
createEnsureMpvConnectedForJellyfinPlaybackHandler,
createBuildEnsureMpvConnectedForJellyfinPlaybackMainDepsHandler,
createBuildGetJellyfinClientInfoMainDepsHandler,
createBuildGetResolvedJellyfinConfigMainDepsHandler,
createBuildHandleJellyfinAuthCommandsMainDepsHandler,
createBuildHandleJellyfinListCommandsMainDepsHandler,
createBuildHandleJellyfinPlayCommandMainDepsHandler,
createBuildHandleJellyfinRemoteAnnounceCommandMainDepsHandler,
createBuildLaunchMpvIdleForJellyfinPlaybackMainDepsHandler,
createBuildOpenJellyfinSetupWindowMainDepsHandler,
createBuildPlayJellyfinItemInMpvMainDepsHandler,
createBuildPreloadJellyfinExternalSubtitlesMainDepsHandler,
createBuildRunJellyfinCommandMainDepsHandler,
createBuildStartJellyfinRemoteSessionMainDepsHandler,
createBuildStopJellyfinRemoteSessionMainDepsHandler,
createBuildWaitForMpvConnectedMainDepsHandler,
createGetJellyfinClientInfoHandler,
createGetResolvedJellyfinConfigHandler,
createHandleJellyfinAuthCommands,
@@ -40,23 +28,13 @@ import {
import type { ComposerInputs, ComposerOutputs } from './contracts';
type EnsureMpvConnectedMainDeps = Parameters<
typeof createBuildEnsureMpvConnectedForJellyfinPlaybackMainDepsHandler
>[0];
type PlayJellyfinItemMainDeps = Parameters<
typeof createBuildPlayJellyfinItemInMpvMainDepsHandler
>[0];
type HandlePlayCommandMainDeps = Parameters<
typeof createBuildHandleJellyfinPlayCommandMainDepsHandler
>[0];
type HandleRemoteAnnounceMainDeps = Parameters<
typeof createBuildHandleJellyfinRemoteAnnounceCommandMainDepsHandler
>[0];
type StartRemoteSessionMainDeps = Parameters<
typeof createBuildStartJellyfinRemoteSessionMainDepsHandler
>[0];
type RunJellyfinCommandMainDeps = Parameters<
typeof createBuildRunJellyfinCommandMainDepsHandler
typeof createEnsureMpvConnectedForJellyfinPlaybackHandler
>[0];
type PlayJellyfinItemMainDeps = Parameters<typeof createPlayJellyfinItemInMpvHandler>[0];
type HandlePlayCommandMainDeps = Parameters<typeof createHandleJellyfinPlayCommand>[0];
type HandleRemoteAnnounceMainDeps = Parameters<typeof createHandleJellyfinRemoteAnnounceCommand>[0];
type StartRemoteSessionMainDeps = Parameters<typeof createStartJellyfinRemoteSessionHandler>[0];
type RunJellyfinCommandMainDeps = Parameters<typeof createRunJellyfinCommandHandler>[0];
type OpenJellyfinSetupWindowMainDeps = Parameters<
typeof createBuildOpenJellyfinSetupWindowMainDepsHandler
>[0];
@@ -68,16 +46,16 @@ export type JellyfinRuntimeComposerOptions = ComposerInputs<{
getJellyfinClientInfoMainDeps: Parameters<
typeof createBuildGetJellyfinClientInfoMainDepsHandler
>[0];
waitForMpvConnectedMainDeps: Parameters<typeof createBuildWaitForMpvConnectedMainDepsHandler>[0];
waitForMpvConnectedMainDeps: Parameters<typeof createWaitForMpvConnectedHandler>[0];
launchMpvIdleForJellyfinPlaybackMainDeps: Parameters<
typeof createBuildLaunchMpvIdleForJellyfinPlaybackMainDepsHandler
typeof createLaunchMpvIdleForJellyfinPlaybackHandler
>[0];
ensureMpvConnectedForJellyfinPlaybackMainDeps: Omit<
EnsureMpvConnectedMainDeps,
'waitForMpvConnected' | 'launchMpvIdleForJellyfinPlayback'
>;
preloadJellyfinExternalSubtitlesMainDeps: Parameters<
typeof createBuildPreloadJellyfinExternalSubtitlesMainDepsHandler
typeof createPreloadJellyfinExternalSubtitlesHandler
>[0];
playJellyfinItemInMpvMainDeps: Omit<
PlayJellyfinItemMainDeps,
@@ -87,12 +65,8 @@ export type JellyfinRuntimeComposerOptions = ComposerInputs<{
JellyfinRemoteComposerOptions,
'getClientInfo' | 'getJellyfinConfig' | 'playJellyfinItem'
>;
handleJellyfinAuthCommandsMainDeps: Parameters<
typeof createBuildHandleJellyfinAuthCommandsMainDepsHandler
>[0];
handleJellyfinListCommandsMainDeps: Parameters<
typeof createBuildHandleJellyfinListCommandsMainDepsHandler
>[0];
handleJellyfinAuthCommandsMainDeps: Parameters<typeof createHandleJellyfinAuthCommands>[0];
handleJellyfinListCommandsMainDeps: Parameters<typeof createHandleJellyfinListCommands>[0];
handleJellyfinPlayCommandMainDeps: Omit<HandlePlayCommandMainDeps, 'playJellyfinItemInMpv'>;
handleJellyfinRemoteAnnounceCommandMainDeps: Omit<
HandleRemoteAnnounceMainDeps,
@@ -106,9 +80,7 @@ export type JellyfinRuntimeComposerOptions = ComposerInputs<{
| 'handlePlaystate'
| 'handleGeneralCommand'
>;
stopJellyfinRemoteSessionMainDeps: Parameters<
typeof createBuildStopJellyfinRemoteSessionMainDepsHandler
>[0];
stopJellyfinRemoteSessionMainDeps: Parameters<typeof createStopJellyfinRemoteSessionHandler>[0];
runJellyfinCommandMainDeps: Omit<
RunJellyfinCommandMainDeps,
| 'getJellyfinConfig'
@@ -177,36 +149,26 @@ export function composeJellyfinRuntimeHandlers(
createBuildGetJellyfinClientInfoMainDepsHandler(options.getJellyfinClientInfoMainDeps)(),
);
const waitForMpvConnected = createWaitForMpvConnectedHandler(
createBuildWaitForMpvConnectedMainDepsHandler(options.waitForMpvConnectedMainDeps)(),
);
const waitForMpvConnected = createWaitForMpvConnectedHandler(options.waitForMpvConnectedMainDeps);
const launchMpvIdleForJellyfinPlayback = createLaunchMpvIdleForJellyfinPlaybackHandler(
createBuildLaunchMpvIdleForJellyfinPlaybackMainDepsHandler(
options.launchMpvIdleForJellyfinPlaybackMainDeps,
)(),
);
const ensureMpvConnectedForJellyfinPlayback = createEnsureMpvConnectedForJellyfinPlaybackHandler(
createBuildEnsureMpvConnectedForJellyfinPlaybackMainDepsHandler({
...options.ensureMpvConnectedForJellyfinPlaybackMainDeps,
waitForMpvConnected: (timeoutMs) => waitForMpvConnected(timeoutMs),
launchMpvIdleForJellyfinPlayback: () => launchMpvIdleForJellyfinPlayback(),
})(),
options.launchMpvIdleForJellyfinPlaybackMainDeps,
);
const ensureMpvConnectedForJellyfinPlayback = createEnsureMpvConnectedForJellyfinPlaybackHandler({
...options.ensureMpvConnectedForJellyfinPlaybackMainDeps,
waitForMpvConnected: (timeoutMs) => waitForMpvConnected(timeoutMs),
launchMpvIdleForJellyfinPlayback: () => launchMpvIdleForJellyfinPlayback(),
});
const preloadJellyfinExternalSubtitles = createPreloadJellyfinExternalSubtitlesHandler(
createBuildPreloadJellyfinExternalSubtitlesMainDepsHandler(
options.preloadJellyfinExternalSubtitlesMainDeps,
)(),
);
const playJellyfinItemInMpv = createPlayJellyfinItemInMpvHandler(
createBuildPlayJellyfinItemInMpvMainDepsHandler({
...options.playJellyfinItemInMpvMainDeps,
ensureMpvConnectedForPlayback: () => ensureMpvConnectedForJellyfinPlayback(),
preloadExternalSubtitles: (params) => {
void preloadJellyfinExternalSubtitles(params);
},
})(),
options.preloadJellyfinExternalSubtitlesMainDeps,
);
const playJellyfinItemInMpv = createPlayJellyfinItemInMpvHandler({
...options.playJellyfinItemInMpvMainDeps,
ensureMpvConnectedForPlayback: () => ensureMpvConnectedForJellyfinPlayback(),
preloadExternalSubtitles: (params) => {
void preloadJellyfinExternalSubtitles(params);
},
});
const {
reportJellyfinRemoteProgress,
@@ -223,59 +185,45 @@ export function composeJellyfinRuntimeHandlers(
});
const handleJellyfinAuthCommands = createHandleJellyfinAuthCommands(
createBuildHandleJellyfinAuthCommandsMainDepsHandler(
options.handleJellyfinAuthCommandsMainDeps,
)(),
options.handleJellyfinAuthCommandsMainDeps,
);
const handleJellyfinListCommands = createHandleJellyfinListCommands(
createBuildHandleJellyfinListCommandsMainDepsHandler(
options.handleJellyfinListCommandsMainDeps,
)(),
);
const handleJellyfinPlayCommand = createHandleJellyfinPlayCommand(
createBuildHandleJellyfinPlayCommandMainDepsHandler({
...options.handleJellyfinPlayCommandMainDeps,
playJellyfinItemInMpv: (params) =>
playJellyfinItemInMpv(params as Parameters<typeof playJellyfinItemInMpv>[0]),
})(),
options.handleJellyfinListCommandsMainDeps,
);
const handleJellyfinPlayCommand = createHandleJellyfinPlayCommand({
...options.handleJellyfinPlayCommandMainDeps,
playJellyfinItemInMpv: (params) =>
playJellyfinItemInMpv(params as Parameters<typeof playJellyfinItemInMpv>[0]),
});
let startJellyfinRemoteSession!: ReturnType<typeof createStartJellyfinRemoteSessionHandler>;
const handleJellyfinRemoteAnnounceCommand = createHandleJellyfinRemoteAnnounceCommand(
createBuildHandleJellyfinRemoteAnnounceCommandMainDepsHandler({
...options.handleJellyfinRemoteAnnounceCommandMainDeps,
startJellyfinRemoteSession: (startOptions) => startJellyfinRemoteSession(startOptions),
})(),
);
const handleJellyfinRemoteAnnounceCommand = createHandleJellyfinRemoteAnnounceCommand({
...options.handleJellyfinRemoteAnnounceCommandMainDeps,
startJellyfinRemoteSession: (startOptions) => startJellyfinRemoteSession(startOptions),
});
startJellyfinRemoteSession = createStartJellyfinRemoteSessionHandler(
createBuildStartJellyfinRemoteSessionMainDepsHandler({
...options.startJellyfinRemoteSessionMainDeps,
getJellyfinConfig: () => getResolvedJellyfinConfig(),
getClientInfo: () => getJellyfinClientInfo(),
handlePlay: (payload) => handleJellyfinRemotePlay(payload),
handlePlaystate: (payload) => handleJellyfinRemotePlaystate(payload),
handleGeneralCommand: (payload) => handleJellyfinRemoteGeneralCommand(payload),
})(),
);
startJellyfinRemoteSession = createStartJellyfinRemoteSessionHandler({
...options.startJellyfinRemoteSessionMainDeps,
getJellyfinConfig: () => getResolvedJellyfinConfig(),
getClientInfo: () => getJellyfinClientInfo(),
handlePlay: (payload) => handleJellyfinRemotePlay(payload),
handlePlaystate: (payload) => handleJellyfinRemotePlaystate(payload),
handleGeneralCommand: (payload) => handleJellyfinRemoteGeneralCommand(payload),
});
const stopJellyfinRemoteSession = createStopJellyfinRemoteSessionHandler(
createBuildStopJellyfinRemoteSessionMainDepsHandler(
options.stopJellyfinRemoteSessionMainDeps,
)(),
options.stopJellyfinRemoteSessionMainDeps,
);
const runJellyfinCommand = createRunJellyfinCommandHandler(
createBuildRunJellyfinCommandMainDepsHandler({
...options.runJellyfinCommandMainDeps,
getJellyfinConfig: () => getResolvedJellyfinConfig(),
getJellyfinClientInfo: (jellyfinConfig) => getJellyfinClientInfo(jellyfinConfig),
handleAuthCommands: (params) => handleJellyfinAuthCommands(params),
handleRemoteAnnounceCommand: (args) => handleJellyfinRemoteAnnounceCommand(args),
handleListCommands: (params) => handleJellyfinListCommands(params),
handlePlayCommand: (params) => handleJellyfinPlayCommand(params),
})(),
);
const runJellyfinCommand = createRunJellyfinCommandHandler({
...options.runJellyfinCommandMainDeps,
getJellyfinConfig: () => getResolvedJellyfinConfig(),
getJellyfinClientInfo: (jellyfinConfig) => getJellyfinClientInfo(jellyfinConfig),
handleAuthCommands: (params) => handleJellyfinAuthCommands(params),
handleRemoteAnnounceCommand: (args) => handleJellyfinRemoteAnnounceCommand(args),
handleListCommands: (params) => handleJellyfinListCommands(params),
handlePlayCommand: (params) => handleJellyfinPlayCommand(params),
});
const maybeFocusExistingJellyfinSetupWindow = createMaybeFocusExistingJellyfinSetupWindowHandler(
options.maybeFocusExistingJellyfinSetupWindowMainDeps,
@@ -4,17 +4,12 @@ import { createBuildMpvClientRuntimeServiceFactoryDepsHandler } from '../mpv-cli
import { createMpvClientRuntimeServiceFactory } from '../mpv-client-runtime-service';
import type { MpvClientRuntimeServiceOptions } from '../mpv-client-runtime-service';
import type { Config } from '../../../types';
import { createBuildUpdateMpvSubtitleRenderMetricsMainDepsHandler } from '../mpv-subtitle-render-metrics-main-deps';
import { createUpdateMpvSubtitleRenderMetricsHandler } from '../mpv-subtitle-render-metrics';
import {
createBuildTokenizerDepsMainHandler,
createCreateMecabTokenizerAndCheckMainHandler,
createPrewarmSubtitleDictionariesMainHandler,
} from '../subtitle-tokenization-main-deps';
import {
createBuildLaunchBackgroundWarmupTaskMainDepsHandler,
createBuildStartBackgroundWarmupsMainDepsHandler,
} from '../startup-warmups-main-deps';
import {
createLaunchBackgroundWarmupTaskHandler as createLaunchBackgroundWarmupTaskFromStartup,
createStartBackgroundWarmupsHandler as createStartBackgroundWarmupsFromStartup,
@@ -38,7 +33,7 @@ type MpvClientRuntimeServiceFactoryMainDeps<TMpvClient extends RuntimeMpvClient>
'bindEventHandlers'
>;
type UpdateMpvSubtitleRenderMetricsMainDeps = Parameters<
typeof createBuildUpdateMpvSubtitleRenderMetricsMainDepsHandler
typeof createUpdateMpvSubtitleRenderMetricsHandler
>[0];
type BuildTokenizerDepsMainDeps = Parameters<typeof createBuildTokenizerDepsMainHandler>[0];
type TokenizerMainDeps = BuiltMainDeps<typeof createBuildTokenizerDepsMainHandler>;
@@ -49,10 +44,10 @@ type PrewarmSubtitleDictionariesMainDeps = Parameters<
typeof createPrewarmSubtitleDictionariesMainHandler
>[0];
type LaunchBackgroundWarmupTaskMainDeps = Parameters<
typeof createBuildLaunchBackgroundWarmupTaskMainDepsHandler
typeof createLaunchBackgroundWarmupTaskFromStartup
>[0];
type StartBackgroundWarmupsMainDeps = Omit<
Parameters<typeof createBuildStartBackgroundWarmupsMainDepsHandler>[0],
Parameters<typeof createStartBackgroundWarmupsFromStartup>[0],
'launchTask' | 'createMecabTokenizerAndCheck' | 'prewarmSubtitleDictionaries'
>;
@@ -120,9 +115,7 @@ export function composeMpvRuntimeHandlers<
createMpvClientRuntimeServiceFactory(buildMpvClientRuntimeServiceFactoryMainDepsHandler())();
const updateMpvSubtitleRenderMetrics = createUpdateMpvSubtitleRenderMetricsHandler(
createBuildUpdateMpvSubtitleRenderMetricsMainDepsHandler(
options.updateMpvSubtitleRenderMetricsMainDeps,
)(),
options.updateMpvSubtitleRenderMetricsMainDeps,
);
const buildTokenizerDepsHandler = createBuildTokenizerDepsMainHandler(
@@ -257,48 +250,44 @@ export function composeMpvRuntimeHandlers<
};
const launchBackgroundWarmupTask = createLaunchBackgroundWarmupTaskFromStartup(
createBuildLaunchBackgroundWarmupTaskMainDepsHandler(
options.warmups.launchBackgroundWarmupTaskMainDeps,
)(),
);
const startBackgroundWarmups = createStartBackgroundWarmupsFromStartup(
createBuildStartBackgroundWarmupsMainDepsHandler({
...options.warmups.startBackgroundWarmupsMainDeps,
launchTask: (label, task) => launchBackgroundWarmupTask(label, task),
createMecabTokenizerAndCheck: () => createMecabTokenizerAndCheck(),
prewarmSubtitleDictionaries: () => prewarmSubtitleDictionaries(),
onYomitanExtensionWarmupScheduled: (promise) => {
if (tokenizationPrerequisiteWarmupCompleted) {
return;
}
const finalizedPromise = promise
.then(() => {
markTokenizationPrerequisiteWarmupCompleted();
})
.finally(() => {
if (tokenizationPrerequisiteWarmupInFlight === finalizedPromise) {
tokenizationPrerequisiteWarmupInFlight = null;
}
});
tokenizationPrerequisiteWarmupInFlight = finalizedPromise;
},
onTokenizationWarmupScheduled: (promise) => {
if (tokenizationWarmupCompleted || !backgroundWarmupCoversOnDemandTokenization()) {
return;
}
const finalizedPromise = promise
.then(() => {
markTokenizationWarmupCompleted();
})
.finally(() => {
if (tokenizationWarmupInFlight === finalizedPromise) {
tokenizationWarmupInFlight = null;
}
});
tokenizationWarmupInFlight = finalizedPromise;
},
})(),
options.warmups.launchBackgroundWarmupTaskMainDeps,
);
const startBackgroundWarmups = createStartBackgroundWarmupsFromStartup({
...options.warmups.startBackgroundWarmupsMainDeps,
launchTask: (label, task) => launchBackgroundWarmupTask(label, task),
createMecabTokenizerAndCheck: () => createMecabTokenizerAndCheck(),
prewarmSubtitleDictionaries: () => prewarmSubtitleDictionaries(),
onYomitanExtensionWarmupScheduled: (promise) => {
if (tokenizationPrerequisiteWarmupCompleted) {
return;
}
const finalizedPromise = promise
.then(() => {
markTokenizationPrerequisiteWarmupCompleted();
})
.finally(() => {
if (tokenizationPrerequisiteWarmupInFlight === finalizedPromise) {
tokenizationPrerequisiteWarmupInFlight = null;
}
});
tokenizationPrerequisiteWarmupInFlight = finalizedPromise;
},
onTokenizationWarmupScheduled: (promise) => {
if (tokenizationWarmupCompleted || !backgroundWarmupCoversOnDemandTokenization()) {
return;
}
const finalizedPromise = promise
.then(() => {
markTokenizationWarmupCompleted();
})
.finally(() => {
if (tokenizationWarmupInFlight === finalizedPromise) {
tokenizationWarmupInFlight = null;
}
});
tokenizationWarmupInFlight = finalizedPromise;
},
});
return {
bindMpvClientEventHandlers: (client) => bindMpvClientEventHandlers(client),
@@ -5,29 +5,22 @@ import {
createSendToActiveOverlayWindowHandler,
createSetOverlayDebugVisualizationEnabledHandler,
} from '../overlay-runtime-main-actions';
import {
createBuildBroadcastRuntimeOptionsChangedMainDepsHandler,
createBuildOpenRuntimeOptionsPaletteMainDepsHandler,
createBuildRestorePreviousSecondarySubVisibilityMainDepsHandler,
createBuildSendToActiveOverlayWindowMainDepsHandler,
createBuildSetOverlayDebugVisualizationEnabledMainDepsHandler,
} from '../overlay-runtime-main-actions-main-deps';
import type { ComposerInputs, ComposerOutputs } from './contracts';
type RestorePreviousSecondarySubVisibilityMainDeps = Parameters<
typeof createBuildRestorePreviousSecondarySubVisibilityMainDepsHandler
typeof createRestorePreviousSecondarySubVisibilityHandler
>[0];
type BroadcastRuntimeOptionsChangedMainDeps = Parameters<
typeof createBuildBroadcastRuntimeOptionsChangedMainDepsHandler
typeof createBroadcastRuntimeOptionsChangedHandler
>[0];
type SendToActiveOverlayWindowMainDeps = Parameters<
typeof createBuildSendToActiveOverlayWindowMainDepsHandler
typeof createSendToActiveOverlayWindowHandler
>[0];
type SetOverlayDebugVisualizationEnabledMainDeps = Parameters<
typeof createBuildSetOverlayDebugVisualizationEnabledMainDepsHandler
typeof createSetOverlayDebugVisualizationEnabledHandler
>[0];
type OpenRuntimeOptionsPaletteMainDeps = Parameters<
typeof createBuildOpenRuntimeOptionsPaletteMainDepsHandler
typeof createOpenRuntimeOptionsPaletteHandler
>[0];
export type OverlayVisibilityRuntimeComposerOptions = ComposerInputs<{
@@ -61,29 +54,19 @@ export function composeOverlayVisibilityRuntime(
updateVisibleOverlayVisibility: () =>
options.overlayVisibilityRuntime.updateVisibleOverlayVisibility(),
restorePreviousSecondarySubVisibility: createRestorePreviousSecondarySubVisibilityHandler(
createBuildRestorePreviousSecondarySubVisibilityMainDepsHandler(
options.restorePreviousSecondarySubVisibilityMainDeps,
)(),
options.restorePreviousSecondarySubVisibilityMainDeps,
),
broadcastRuntimeOptionsChanged: createBroadcastRuntimeOptionsChangedHandler(
createBuildBroadcastRuntimeOptionsChangedMainDepsHandler(
options.broadcastRuntimeOptionsChangedMainDeps,
)(),
options.broadcastRuntimeOptionsChangedMainDeps,
),
sendToActiveOverlayWindow: createSendToActiveOverlayWindowHandler(
createBuildSendToActiveOverlayWindowMainDepsHandler(
options.sendToActiveOverlayWindowMainDeps,
)(),
options.sendToActiveOverlayWindowMainDeps,
),
setOverlayDebugVisualizationEnabled: createSetOverlayDebugVisualizationEnabledHandler(
createBuildSetOverlayDebugVisualizationEnabledMainDepsHandler(
options.setOverlayDebugVisualizationEnabledMainDeps,
)(),
options.setOverlayDebugVisualizationEnabledMainDeps,
),
openRuntimeOptionsPalette: createOpenRuntimeOptionsPaletteHandler(
createBuildOpenRuntimeOptionsPaletteMainDepsHandler(
options.openRuntimeOptionsPaletteMainDeps,
)(),
options.openRuntimeOptionsPaletteMainDeps,
),
};
}
@@ -0,0 +1,69 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import {
composeShortcutRuntimes,
type ShortcutsRuntimeComposerOptions,
} from './shortcuts-runtime-composer';
type GlobalShortcutsOptions = ShortcutsRuntimeComposerOptions['globalShortcuts'];
function createComposerOptions(tryBeginOverlaySelection: (timeoutMs: number) => boolean) {
const registeredAccelerators: string[] = [];
const options: ShortcutsRuntimeComposerOptions = {
// Global and overlay shortcut handlers are only constructed here, never invoked.
globalShortcuts: {
getConfiguredShortcutsMainDeps:
{} as GlobalShortcutsOptions['getConfiguredShortcutsMainDeps'],
buildRegisterGlobalShortcutsMainDeps: () =>
({}) as ReturnType<GlobalShortcutsOptions['buildRegisterGlobalShortcutsMainDeps']>,
buildRefreshGlobalAndOverlayShortcutsMainDeps: () =>
({}) as ReturnType<GlobalShortcutsOptions['buildRefreshGlobalAndOverlayShortcutsMainDeps']>,
},
overlayShortcutsRuntimeMainDeps:
{} as ShortcutsRuntimeComposerOptions['overlayShortcutsRuntimeMainDeps'],
numericShortcutRuntimeMainDeps: {
globalShortcut: {
register: (accelerator) => {
registeredAccelerators.push(accelerator);
return true;
},
unregister: () => {},
},
showMpvOsd: () => {},
setTimer: () => setTimeout(() => {}, 0),
clearTimer: (timer) => clearTimeout(timer),
},
numericSessions: {
onMultiCopyDigit: () => {},
onMineSentenceDigit: () => {},
tryBeginMultiCopyOverlaySelection: tryBeginOverlaySelection,
tryBeginMineSentenceOverlaySelection: tryBeginOverlaySelection,
},
};
return { options, registeredAccelerators };
}
test('numeric sessions delegate to the visible overlay when it accepts the selection', () => {
const timeouts: number[] = [];
const { options, registeredAccelerators } = createComposerOptions((timeoutMs) => {
timeouts.push(timeoutMs);
return true;
});
const runtimes = composeShortcutRuntimes(options);
runtimes.startPendingMultiCopy(3000);
runtimes.startPendingMineSentenceMultiple(4000);
assert.deepEqual(timeouts, [3000, 4000]);
assert.deepEqual(registeredAccelerators, []);
});
test('numeric sessions fall back to global digit shortcuts when the overlay declines', () => {
const { options, registeredAccelerators } = createComposerOptions(() => false);
const runtimes = composeShortcutRuntimes(options);
runtimes.startPendingMultiCopy(3000);
assert.ok(registeredAccelerators.length > 0);
runtimes.cancelPendingMultiCopy();
});
@@ -1,6 +1,5 @@
import { createNumericShortcutRuntime } from '../../../core/services/numeric-shortcut';
import {
createBuildNumericShortcutRuntimeMainDepsHandler,
createGlobalShortcutsRuntimeHandlers,
createNumericShortcutSessionRuntimeHandlers,
createOverlayShortcutsRuntimeHandlers,
@@ -8,9 +7,7 @@ import {
import type { ComposerInputs, ComposerOutputs } from './contracts';
type GlobalShortcutsOptions = Parameters<typeof createGlobalShortcutsRuntimeHandlers>[0];
type NumericShortcutRuntimeMainDeps = Parameters<
typeof createBuildNumericShortcutRuntimeMainDepsHandler
>[0];
type NumericShortcutRuntimeMainDeps = Parameters<typeof createNumericShortcutRuntime>[0];
type NumericSessionOptions = Omit<
Parameters<typeof createNumericShortcutSessionRuntimeHandlers>[0],
'multiCopySession' | 'mineSentenceSession'
@@ -37,15 +34,13 @@ export function composeShortcutRuntimes(
): ShortcutsRuntimeComposerResult {
const globalShortcuts = createGlobalShortcutsRuntimeHandlers(options.globalShortcuts);
const numericShortcutRuntimeMainDeps = createBuildNumericShortcutRuntimeMainDepsHandler(
const numericShortcutRuntime = createNumericShortcutRuntime(
options.numericShortcutRuntimeMainDeps,
)();
const numericShortcutRuntime = createNumericShortcutRuntime(numericShortcutRuntimeMainDeps);
);
const numericSessions = createNumericShortcutSessionRuntimeHandlers({
...options.numericSessions,
multiCopySession: numericShortcutRuntime.createSession(),
mineSentenceSession: numericShortcutRuntime.createSession(),
onMultiCopyDigit: options.numericSessions.onMultiCopyDigit,
onMineSentenceDigit: options.numericSessions.onMineSentenceDigit,
});
const overlayShortcuts = createOverlayShortcutsRuntimeHandlers({
-6
View File
@@ -1,15 +1,9 @@
export * from '../anilist-media-guess';
export * from '../anilist-media-guess-main-deps';
export * from '../anilist-media-state';
export * from '../anilist-media-state-main-deps';
export * from '../anilist-post-watch';
export * from '../anilist-post-watch-main-deps';
export * from '../anilist-setup';
export * from '../anilist-setup-protocol';
export * from '../anilist-setup-protocol-main-deps';
export * from '../anilist-setup-window';
export * from '../anilist-setup-window-main-deps';
export * from '../anilist-state';
export * from '../anilist-token-refresh';
export * from '../anilist-token-refresh-main-deps';
export * from '../protocol-url-handlers';
-1
View File
@@ -1,5 +1,4 @@
export * from '../cli-command-context-factory';
export * from '../cli-command-runtime-handler';
export * from '../initial-args-runtime-handler';
export * from '../ipc-mpv-command-main-deps';
export * from '../ipc-runtime-handlers';
-7
View File
@@ -1,25 +1,18 @@
export * from '../jellyfin-cli-auth';
export * from '../jellyfin-cli-list';
export * from '../jellyfin-cli-main-deps';
export * from '../jellyfin-cli-play';
export * from '../jellyfin-cli-remote-announce';
export * from '../jellyfin-client-info';
export * from '../jellyfin-client-info-main-deps';
export * from '../jellyfin-command-dispatch';
export * from '../jellyfin-command-dispatch-main-deps';
export * from '../jellyfin-device-identity';
export * from '../jellyfin-playback-launch';
export * from '../jellyfin-playback-launch-main-deps';
export * from '../jellyfin-remote-commands';
export * from '../jellyfin-remote-connection';
export * from '../jellyfin-remote-connection-main-deps';
export * from '../jellyfin-remote-main-deps';
export * from '../jellyfin-remote-playback';
export * from '../jellyfin-remote-session-lifecycle';
export * from '../jellyfin-remote-session-main-deps';
export * from '../jellyfin-setup-window';
export * from '../jellyfin-setup-window-main-deps';
export * from '../jellyfin-subtitle-preload';
export * from '../jellyfin-subtitle-preload-main-deps';
export * from '../mpv-jellyfin-defaults';
export * from '../mpv-jellyfin-defaults-main-deps';
-2
View File
@@ -1,4 +1,2 @@
export * from '../anki-actions';
export * from '../anki-actions-main-deps';
export * from '../mining-actions';
export * from '../mining-actions-main-deps';
-1
View File
@@ -4,6 +4,5 @@ export * from '../mpv-main-event-bindings';
export * from '../mpv-main-event-main-deps';
export * from '../mpv-osd-runtime-handlers';
export * from '../mpv-subtitle-render-metrics';
export * from '../mpv-subtitle-render-metrics-main-deps';
export * from '../secondary-sub-mode-runtime-handler';
export * from '../subtitle-tokenization-main-deps';
-6
View File
@@ -1,21 +1,15 @@
export * from '../config-hot-reload-handlers';
export * from '../config-hot-reload-main-deps';
export * from '../field-grouping-overlay-main-deps';
export * from '../field-grouping-resolver';
export * from '../field-grouping-resolver-main-deps';
export * from '../overlay-bootstrap-main-deps';
export * from '../overlay-main-actions';
export * from '../overlay-main-actions-main-deps';
export * from '../overlay-runtime-bootstrap-handlers';
export * from '../overlay-runtime-main-actions';
export * from '../overlay-runtime-main-actions-main-deps';
export * from '../overlay-visibility-runtime';
export * from '../overlay-visibility-runtime-main-deps';
export * from '../overlay-window-layout';
export * from '../overlay-window-layout-main-deps';
export * from '../overlay-window-runtime-handlers';
export * from '../subtitle-position';
export * from '../subtitle-position-main-deps';
export * from '../tray-runtime';
export * from '../tray-runtime-handlers';
export * from '../yomitan-extension-runtime';
-1
View File
@@ -1,5 +1,4 @@
export * from '../global-shortcuts-runtime-handlers';
export * from '../numeric-shortcut-runtime-main-deps';
export * from '../numeric-shortcut-session-runtime-handlers';
export * from '../overlay-shortcuts-runtime-handlers';
export * from '../overlay-shortcuts-runtime-main-deps';
-7
View File
@@ -1,19 +1,12 @@
export * from '../app-lifecycle-actions';
export * from '../app-lifecycle-main-activate';
export * from '../app-lifecycle-main-cleanup';
export * from '../app-ready-main-deps';
export * from '../clipboard-queue';
export * from '../config-derived';
export * from '../dictionary-runtime-main-deps';
export * from '../immersion-media';
export * from '../immersion-startup';
export * from '../immersion-startup-main-deps';
export * from '../media-runtime-main-deps';
export * from '../runtime-bootstrap-main-deps';
export * from '../startup-config';
export * from '../startup-config-main-deps';
export * from '../startup-runtime-handlers';
export * from '../startup-warmups';
export * from '../startup-warmups-main-deps';
export * from '../subtitle-processing-main-deps';
export * from '../subsync-runtime';
@@ -1,46 +0,0 @@
import type { FieldGroupingOverlayRuntimeOptions } from '../../core/services/field-grouping-overlay';
type FieldGroupingOverlayMainDeps<TModal extends string> = Omit<
FieldGroupingOverlayRuntimeOptions<TModal>,
'sendToVisibleOverlay'
> & {
sendToActiveOverlayWindow: (
channel: string,
payload?: unknown,
runtimeOptions?: { restoreOnModalClose?: TModal; preferModalWindow?: boolean },
) => boolean;
};
type BuiltFieldGroupingOverlayMainDeps<TModal extends string> =
FieldGroupingOverlayRuntimeOptions<TModal> & {
sendToVisibleOverlay: NonNullable<
FieldGroupingOverlayRuntimeOptions<TModal>['sendToVisibleOverlay']
>;
};
export function createBuildFieldGroupingOverlayMainDepsHandler<TModal extends string>(
deps: FieldGroupingOverlayMainDeps<TModal>,
) {
return (): BuiltFieldGroupingOverlayMainDeps<TModal> => ({
getMainWindow: () => deps.getMainWindow(),
getVisibleOverlayVisible: () => deps.getVisibleOverlayVisible(),
setVisibleOverlayVisible: (visible: boolean) => deps.setVisibleOverlayVisible(visible),
getResolver: () => deps.getResolver(),
setResolver: (resolver) => deps.setResolver(resolver),
getRestoreVisibleOverlayOnModalClose: () => deps.getRestoreVisibleOverlayOnModalClose(),
// These are optional on the runtime options, so a missing forward compiles silently — but
// dropping them left the field grouping modal with no modal-open ack/retry, no teardown on
// failure, and no warn logging, which is why the earlier recovery fixes never took effect.
waitForModalOpen: deps.waitForModalOpen,
handleOverlayModalClosed: deps.handleOverlayModalClosed,
logWarn: deps.logWarn,
ensureOverlayStartupPrereqs: deps.ensureOverlayStartupPrereqs,
ensureOverlayWindowsReadyForVisibilityActions:
deps.ensureOverlayWindowsReadyForVisibilityActions,
sendToVisibleOverlay: (
channel: string,
payload?: unknown,
runtimeOptions?: { restoreOnModalClose?: TModal; preferModalWindow?: boolean },
) => deps.sendToActiveOverlayWindow(channel, payload, runtimeOptions),
});
}
@@ -1,25 +0,0 @@
import type {
createGetFieldGroupingResolverHandler,
createSetFieldGroupingResolverHandler,
} from './field-grouping-resolver';
type GetFieldGroupingResolverMainDeps = Parameters<typeof createGetFieldGroupingResolverHandler>[0];
type SetFieldGroupingResolverMainDeps = Parameters<typeof createSetFieldGroupingResolverHandler>[0];
export function createBuildGetFieldGroupingResolverMainDepsHandler(
deps: GetFieldGroupingResolverMainDeps,
) {
return (): GetFieldGroupingResolverMainDeps => ({
getResolver: () => deps.getResolver(),
});
}
export function createBuildSetFieldGroupingResolverMainDepsHandler(
deps: SetFieldGroupingResolverMainDeps,
) {
return (): SetFieldGroupingResolverMainDeps => ({
setResolver: (resolver) => deps.setResolver(resolver),
nextSequence: () => deps.nextSequence(),
getSequence: () => deps.getSequence(),
});
}
@@ -1,47 +0,0 @@
import type { Config } from '../../types';
import type { ConfiguredShortcuts } from '../../core/utils/shortcut-config';
import type { RegisterGlobalShortcutsServiceOptions } from '../../core/services/shortcut';
export function createBuildGetConfiguredShortcutsMainDepsHandler(deps: {
getResolvedConfig: () => Config;
defaultConfig: Config;
resolveConfiguredShortcuts: (config: Config, defaultConfig: Config) => ConfiguredShortcuts;
}) {
return () => ({
getResolvedConfig: () => deps.getResolvedConfig(),
defaultConfig: deps.defaultConfig,
resolveConfiguredShortcuts: (config: Config, defaultConfig: Config) =>
deps.resolveConfiguredShortcuts(config, defaultConfig),
});
}
export function createBuildRegisterGlobalShortcutsMainDepsHandler(deps: {
getConfiguredShortcuts: () => RegisterGlobalShortcutsServiceOptions['shortcuts'];
registerGlobalShortcutsCore: (options: RegisterGlobalShortcutsServiceOptions) => void;
toggleVisibleOverlay: () => void;
openYomitanSettings: () => void;
isDev: boolean;
getMainWindow: RegisterGlobalShortcutsServiceOptions['getMainWindow'];
}) {
return () => ({
getConfiguredShortcuts: () => deps.getConfiguredShortcuts(),
registerGlobalShortcutsCore: (options: RegisterGlobalShortcutsServiceOptions) =>
deps.registerGlobalShortcutsCore(options),
onToggleVisibleOverlay: () => deps.toggleVisibleOverlay(),
onOpenYomitanSettings: () => deps.openYomitanSettings(),
isDev: deps.isDev,
getMainWindow: deps.getMainWindow,
});
}
export function createBuildRefreshGlobalAndOverlayShortcutsMainDepsHandler(deps: {
unregisterAllGlobalShortcuts: () => void;
registerGlobalShortcuts: () => void;
syncOverlayShortcuts: () => void;
}) {
return () => ({
unregisterAllGlobalShortcuts: () => deps.unregisterAllGlobalShortcuts(),
registerGlobalShortcuts: () => deps.registerGlobalShortcuts(),
syncOverlayShortcuts: () => deps.syncOverlayShortcuts(),
});
}
@@ -4,20 +4,11 @@ import {
createRefreshGlobalAndOverlayShortcutsHandler,
createRegisterGlobalShortcutsHandler,
} from './global-shortcuts';
import {
createBuildGetConfiguredShortcutsMainDepsHandler,
createBuildRefreshGlobalAndOverlayShortcutsMainDepsHandler,
createBuildRegisterGlobalShortcutsMainDepsHandler,
} from './global-shortcuts-main-deps';
type GetConfiguredShortcutsMainDeps = Parameters<
typeof createBuildGetConfiguredShortcutsMainDepsHandler
>[0];
type RegisterGlobalShortcutsMainDeps = Parameters<
typeof createBuildRegisterGlobalShortcutsMainDepsHandler
>[0];
type GetConfiguredShortcutsMainDeps = Parameters<typeof createGetConfiguredShortcutsHandler>[0];
type RegisterGlobalShortcutsMainDeps = Parameters<typeof createRegisterGlobalShortcutsHandler>[0];
type RefreshGlobalAndOverlayShortcutsMainDeps = Parameters<
typeof createBuildRefreshGlobalAndOverlayShortcutsMainDepsHandler
typeof createRefreshGlobalAndOverlayShortcutsHandler
>[0];
export function createGlobalShortcutsRuntimeHandlers(deps: {
@@ -29,28 +20,18 @@ export function createGlobalShortcutsRuntimeHandlers(deps: {
registerGlobalShortcuts: () => void,
) => RefreshGlobalAndOverlayShortcutsMainDeps;
}) {
const getConfiguredShortcutsMainDeps = createBuildGetConfiguredShortcutsMainDepsHandler(
deps.getConfiguredShortcutsMainDeps,
)();
const getConfiguredShortcutsHandler = createGetConfiguredShortcutsHandler(
getConfiguredShortcutsMainDeps,
deps.getConfiguredShortcutsMainDeps,
);
const getConfiguredShortcuts = () => getConfiguredShortcutsHandler();
const registerGlobalShortcutsMainDeps = createBuildRegisterGlobalShortcutsMainDepsHandler(
deps.buildRegisterGlobalShortcutsMainDeps(getConfiguredShortcuts),
)();
const registerGlobalShortcutsHandler = createRegisterGlobalShortcutsHandler(
registerGlobalShortcutsMainDeps,
deps.buildRegisterGlobalShortcutsMainDeps(getConfiguredShortcuts),
);
const registerGlobalShortcuts = () => registerGlobalShortcutsHandler();
const refreshGlobalAndOverlayShortcutsMainDeps =
createBuildRefreshGlobalAndOverlayShortcutsMainDepsHandler(
deps.buildRefreshGlobalAndOverlayShortcutsMainDeps(registerGlobalShortcuts),
)();
const refreshGlobalAndOverlayShortcutsHandler = createRefreshGlobalAndOverlayShortcutsHandler(
refreshGlobalAndOverlayShortcutsMainDeps,
deps.buildRefreshGlobalAndOverlayShortcutsMainDeps(registerGlobalShortcuts),
);
const refreshGlobalAndOverlayShortcuts = () => refreshGlobalAndOverlayShortcutsHandler();
@@ -1,22 +0,0 @@
import type {
ImmersionTrackerStartupDeps,
createImmersionTrackerStartupHandler,
} from './immersion-startup';
type ImmersionTrackerStartupMainDeps = Parameters<typeof createImmersionTrackerStartupHandler>[0];
export function createBuildImmersionTrackerStartupMainDepsHandler(
deps: ImmersionTrackerStartupMainDeps,
) {
return (): ImmersionTrackerStartupDeps => ({
getResolvedConfig: () => deps.getResolvedConfig(),
getConfiguredDbPath: () => deps.getConfiguredDbPath(),
createTrackerService: (params) => deps.createTrackerService(params),
setTracker: (tracker) => deps.setTracker(tracker),
getMpvClient: () => deps.getMpvClient(),
seedTrackerFromCurrentMedia: () => deps.seedTrackerFromCurrentMedia(),
logInfo: (message: string) => deps.logInfo(message),
logDebug: (message: string) => deps.logDebug(message),
logWarn: (message: string, details: unknown) => deps.logWarn(message, details),
});
}
@@ -1,38 +0,0 @@
import type { CliArgs } from '../../cli/args';
export function createBuildHandleInitialArgsMainDepsHandler(deps: {
getInitialArgs: () => CliArgs | null;
isBackgroundMode: () => boolean;
shouldEnsureTrayOnStartup: () => boolean;
shouldRunHeadlessInitialCommand: (args: CliArgs) => boolean;
ensureTray: () => void;
isTexthookerOnlyMode: () => boolean;
hasImmersionTracker: () => boolean;
getMpvClient: () => { connected: boolean; connect: () => void } | null;
commandNeedsOverlayStartupPrereqs: (args: CliArgs) => boolean;
commandNeedsOverlayRuntime: (args: CliArgs) => boolean;
ensureOverlayStartupPrereqs: () => void;
isOverlayRuntimeInitialized: () => boolean;
initializeOverlayRuntime: () => void;
logInfo: (message: string) => void;
handleCliCommand: (args: CliArgs, source: 'initial') => void;
}) {
return () => ({
getInitialArgs: () => deps.getInitialArgs(),
isBackgroundMode: () => deps.isBackgroundMode(),
shouldEnsureTrayOnStartup: () => deps.shouldEnsureTrayOnStartup(),
shouldRunHeadlessInitialCommand: (args: CliArgs) => deps.shouldRunHeadlessInitialCommand(args),
ensureTray: () => deps.ensureTray(),
isTexthookerOnlyMode: () => deps.isTexthookerOnlyMode(),
hasImmersionTracker: () => deps.hasImmersionTracker(),
getMpvClient: () => deps.getMpvClient(),
commandNeedsOverlayStartupPrereqs: (args: CliArgs) =>
deps.commandNeedsOverlayStartupPrereqs(args),
commandNeedsOverlayRuntime: (args: CliArgs) => deps.commandNeedsOverlayRuntime(args),
ensureOverlayStartupPrereqs: () => deps.ensureOverlayStartupPrereqs(),
isOverlayRuntimeInitialized: () => deps.isOverlayRuntimeInitialized(),
initializeOverlayRuntime: () => deps.initializeOverlayRuntime(),
logInfo: (message: string) => deps.logInfo(message),
handleCliCommand: (args: CliArgs, source: 'initial') => deps.handleCliCommand(args, source),
});
}
@@ -1,10 +1,7 @@
import { createHandleInitialArgsHandler } from './initial-args-handler';
import { createBuildHandleInitialArgsMainDepsHandler } from './initial-args-main-deps';
type InitialArgsMainDeps = Parameters<typeof createBuildHandleInitialArgsMainDepsHandler>[0];
type InitialArgsMainDeps = Parameters<typeof createHandleInitialArgsHandler>[0];
export function createInitialArgsRuntimeHandler(deps: InitialArgsMainDeps) {
const buildHandleInitialArgsMainDepsHandler = createBuildHandleInitialArgsMainDepsHandler(deps);
const handleInitialArgsMainDeps = buildHandleInitialArgsMainDepsHandler();
return createHandleInitialArgsHandler(handleInitialArgsMainDeps);
return createHandleInitialArgsHandler(deps);
}
@@ -1,21 +0,0 @@
import type { createHandleMpvCommandFromIpcHandler } from './ipc-bridge-actions';
type HandleMpvCommandFromIpcMainDeps = Parameters<typeof createHandleMpvCommandFromIpcHandler>[0];
export function createBuildHandleMpvCommandFromIpcMainDepsHandler(
deps: HandleMpvCommandFromIpcMainDeps,
) {
return (): HandleMpvCommandFromIpcMainDeps => ({
handleMpvCommandFromIpcRuntime: (command, options) =>
deps.handleMpvCommandFromIpcRuntime(command, options),
buildMpvCommandDeps: () => deps.buildMpvCommandDeps(),
});
}
export function createBuildRunSubsyncManualFromIpcMainDepsHandler<TRequest, TResult>(deps: {
runManualFromIpc: (request: TRequest) => Promise<TResult>;
}) {
return () => ({
runManualFromIpc: (request: TRequest) => deps.runManualFromIpc(request),
});
}
@@ -1,30 +0,0 @@
import type { MpvCommandFromIpcRuntimeDeps } from '../ipc-mpv-command';
export function createBuildMpvCommandFromIpcRuntimeMainDepsHandler(
deps: MpvCommandFromIpcRuntimeDeps,
) {
return (): MpvCommandFromIpcRuntimeDeps => {
const showPlaybackFeedback = deps.showPlaybackFeedback;
const showRawMpvOsd = deps.showRawMpvOsd;
return {
triggerSubsyncFromConfig: () => deps.triggerSubsyncFromConfig(),
openRuntimeOptionsPalette: () => deps.openRuntimeOptionsPalette(),
openJimaku: () => deps.openJimaku(),
openTsukihime: () => deps.openTsukihime(),
openYoutubeTrackPicker: () => deps.openYoutubeTrackPicker(),
openPlaylistBrowser: () => deps.openPlaylistBrowser(),
cycleRuntimeOption: (id, direction) => deps.cycleRuntimeOption(id, direction),
showMpvOsd: (text: string) => deps.showMpvOsd(text),
...(showRawMpvOsd ? { showRawMpvOsd: (text: string) => showRawMpvOsd(text) } : {}),
...(showPlaybackFeedback
? { showPlaybackFeedback: (text: string) => showPlaybackFeedback(text) }
: {}),
replayCurrentSubtitle: () => deps.replayCurrentSubtitle(),
playNextSubtitle: () => deps.playNextSubtitle(),
sendMpvCommand: (command: (string | number)[]) => deps.sendMpvCommand(command),
getMpvClient: () => deps.getMpvClient(),
isMpvConnected: () => deps.isMpvConnected(),
hasRuntimeOptionsManager: () => deps.hasRuntimeOptionsManager(),
};
};
}
+6 -23
View File
@@ -2,35 +2,18 @@ import {
createHandleMpvCommandFromIpcHandler,
createRunSubsyncManualFromIpcHandler,
} from './ipc-bridge-actions';
import {
createBuildHandleMpvCommandFromIpcMainDepsHandler,
createBuildRunSubsyncManualFromIpcMainDepsHandler,
} from './ipc-bridge-actions-main-deps';
type HandleMpvCommandFromIpcMainDeps = Parameters<
typeof createBuildHandleMpvCommandFromIpcMainDepsHandler
>[0];
type RunSubsyncManualFromIpcMainDeps<TRequest, TResult> = Parameters<
typeof createBuildRunSubsyncManualFromIpcMainDepsHandler<TRequest, TResult>
>[0];
export function createIpcRuntimeHandlers<TRequest, TResult>(deps: {
handleMpvCommandFromIpcDeps: HandleMpvCommandFromIpcMainDeps;
runSubsyncManualFromIpcDeps: RunSubsyncManualFromIpcMainDeps<TRequest, TResult>;
handleMpvCommandFromIpcDeps: Parameters<typeof createHandleMpvCommandFromIpcHandler>[0];
runSubsyncManualFromIpcDeps: Parameters<
typeof createRunSubsyncManualFromIpcHandler<TRequest, TResult>
>[0];
}) {
const handleMpvCommandFromIpcMainDeps = createBuildHandleMpvCommandFromIpcMainDepsHandler(
deps.handleMpvCommandFromIpcDeps,
)();
const handleMpvCommandFromIpc = createHandleMpvCommandFromIpcHandler(
handleMpvCommandFromIpcMainDeps,
deps.handleMpvCommandFromIpcDeps,
);
const runSubsyncManualFromIpcMainDeps = createBuildRunSubsyncManualFromIpcMainDepsHandler<
TRequest,
TResult
>(deps.runSubsyncManualFromIpcDeps)();
const runSubsyncManualFromIpc = createRunSubsyncManualFromIpcHandler<TRequest, TResult>(
runSubsyncManualFromIpcMainDeps,
deps.runSubsyncManualFromIpcDeps,
);
return {
@@ -1,59 +0,0 @@
import type { createHandleJellyfinAuthCommands } from './jellyfin-cli-auth';
import type { createHandleJellyfinListCommands } from './jellyfin-cli-list';
import type { createHandleJellyfinPlayCommand } from './jellyfin-cli-play';
import type { createHandleJellyfinRemoteAnnounceCommand } from './jellyfin-cli-remote-announce';
type HandleJellyfinAuthCommandsMainDeps = Parameters<typeof createHandleJellyfinAuthCommands>[0];
type HandleJellyfinListCommandsMainDeps = Parameters<typeof createHandleJellyfinListCommands>[0];
type HandleJellyfinPlayCommandMainDeps = Parameters<typeof createHandleJellyfinPlayCommand>[0];
type HandleJellyfinRemoteAnnounceCommandMainDeps = Parameters<
typeof createHandleJellyfinRemoteAnnounceCommand
>[0];
export function createBuildHandleJellyfinAuthCommandsMainDepsHandler(
deps: HandleJellyfinAuthCommandsMainDeps,
) {
return (): HandleJellyfinAuthCommandsMainDeps => ({
patchRawConfig: (patch) => deps.patchRawConfig(patch),
authenticateWithPassword: (serverUrl, username, password, clientInfo) =>
deps.authenticateWithPassword(serverUrl, username, password, clientInfo),
saveStoredSession: (session) => deps.saveStoredSession(session),
clearStoredSession: () => deps.clearStoredSession(),
logInfo: (message: string) => deps.logInfo(message),
});
}
export function createBuildHandleJellyfinListCommandsMainDepsHandler(
deps: HandleJellyfinListCommandsMainDeps,
) {
return (): HandleJellyfinListCommandsMainDeps => ({
listJellyfinLibraries: (session, clientInfo) => deps.listJellyfinLibraries(session, clientInfo),
listJellyfinItems: (session, clientInfo, params) =>
deps.listJellyfinItems(session, clientInfo, params),
listJellyfinSubtitleTracks: (session, clientInfo, itemId) =>
deps.listJellyfinSubtitleTracks(session, clientInfo, itemId),
writeJellyfinResponse: (responsePath, response) =>
deps.writeJellyfinResponse(responsePath, response),
logInfo: (message: string) => deps.logInfo(message),
});
}
export function createBuildHandleJellyfinPlayCommandMainDepsHandler(
deps: HandleJellyfinPlayCommandMainDeps,
) {
return (): HandleJellyfinPlayCommandMainDeps => ({
playJellyfinItemInMpv: (params) => deps.playJellyfinItemInMpv(params),
logWarn: (message: string) => deps.logWarn(message),
});
}
export function createBuildHandleJellyfinRemoteAnnounceCommandMainDepsHandler(
deps: HandleJellyfinRemoteAnnounceCommandMainDeps,
) {
return (): HandleJellyfinRemoteAnnounceCommandMainDeps => ({
startJellyfinRemoteSession: (options) => deps.startJellyfinRemoteSession(options),
getRemoteSession: () => deps.getRemoteSession(),
logInfo: (message: string) => deps.logInfo(message),
logWarn: (message: string) => deps.logWarn(message),
});
}
@@ -1,59 +0,0 @@
import type { CliArgs } from '../../cli/args';
import type { JellyfinCliResponse } from '../../shared/jellyfin-cli-response';
type JellyfinConfigBase = {
serverUrl?: string;
accessToken?: string;
userId?: string;
username?: string;
};
type JellyfinSession = {
serverUrl: string;
accessToken: string;
userId: string;
username: string;
};
export type RunJellyfinCommandMainDeps<TClientInfo, TConfig extends JellyfinConfigBase> = {
getJellyfinConfig: () => TConfig;
defaultServerUrl: string;
getJellyfinClientInfo: (config: TConfig) => TClientInfo;
handleAuthCommands: (params: {
args: CliArgs;
jellyfinConfig: TConfig;
serverUrl: string;
clientInfo: TClientInfo;
}) => Promise<boolean>;
handleRemoteAnnounceCommand: (args: CliArgs) => Promise<boolean>;
handleListCommands: (params: {
args: CliArgs;
session: JellyfinSession;
clientInfo: TClientInfo;
jellyfinConfig: TConfig;
}) => Promise<boolean>;
handlePlayCommand: (params: {
args: CliArgs;
session: JellyfinSession;
clientInfo: TClientInfo;
jellyfinConfig: TConfig;
}) => Promise<boolean>;
writeJellyfinResponse: (responsePath: string, response: JellyfinCliResponse) => void;
};
export function createBuildRunJellyfinCommandMainDepsHandler<
TClientInfo,
TConfig extends JellyfinConfigBase,
>(deps: RunJellyfinCommandMainDeps<TClientInfo, TConfig>) {
return (): RunJellyfinCommandMainDeps<TClientInfo, TConfig> => ({
getJellyfinConfig: () => deps.getJellyfinConfig(),
defaultServerUrl: deps.defaultServerUrl,
getJellyfinClientInfo: (config: TConfig) => deps.getJellyfinClientInfo(config),
handleAuthCommands: (params) => deps.handleAuthCommands(params),
handleRemoteAnnounceCommand: (args: CliArgs) => deps.handleRemoteAnnounceCommand(args),
handleListCommands: (params) => deps.handleListCommands(params),
handlePlayCommand: (params) => deps.handlePlayCommand(params),
writeJellyfinResponse: (responsePath, response) =>
deps.writeJellyfinResponse(responsePath, response),
});
}
@@ -1,30 +0,0 @@
import type { createPlayJellyfinItemInMpvHandler } from './jellyfin-playback-launch';
type PlayJellyfinItemInMpvMainDeps = Parameters<typeof createPlayJellyfinItemInMpvHandler>[0];
export function createBuildPlayJellyfinItemInMpvMainDepsHandler(
deps: PlayJellyfinItemInMpvMainDeps,
) {
return (): PlayJellyfinItemInMpvMainDeps => ({
ensureMpvConnectedForPlayback: () => deps.ensureMpvConnectedForPlayback(),
getMpvClient: () => deps.getMpvClient(),
resolvePlaybackPlan: (params) => deps.resolvePlaybackPlan(params),
applyJellyfinMpvDefaults: (mpvClient) => deps.applyJellyfinMpvDefaults(mpvClient),
showVisibleOverlay: () => deps.showVisibleOverlay(),
sendMpvCommand: (command: Array<string | number>) => deps.sendMpvCommand(command),
armQuitOnDisconnect: () => deps.armQuitOnDisconnect(),
schedule: (callback: () => void, delayMs: number) => deps.schedule(callback, delayMs),
convertTicksToSeconds: (ticks: number) => deps.convertTicksToSeconds(ticks),
preloadExternalSubtitles: (params) => deps.preloadExternalSubtitles(params),
setActivePlayback: (state) => deps.setActivePlayback(state),
setLastProgressAtMs: (value: number) => deps.setLastProgressAtMs(value),
createPlaybackReporter: (params) => deps.createPlaybackReporter(params),
showMpvOsd: (text: string) => deps.showMpvOsd(text),
recordJellyfinPlaybackMetadata: deps.recordJellyfinPlaybackMetadata
? (metadata) => deps.recordJellyfinPlaybackMetadata!(metadata)
: undefined,
updateCurrentMediaTitle: deps.updateCurrentMediaTitle
? (title) => deps.updateCurrentMediaTitle!(title)
: undefined,
});
}
@@ -1,50 +0,0 @@
import type {
EnsureMpvConnectedDeps,
LaunchMpvForJellyfinDeps,
WaitForMpvConnectedDeps,
} from './jellyfin-remote-connection';
export function createBuildWaitForMpvConnectedMainDepsHandler(deps: WaitForMpvConnectedDeps) {
return (): WaitForMpvConnectedDeps => ({
getMpvClient: () => deps.getMpvClient(),
now: () => deps.now(),
sleep: (delayMs: number) => deps.sleep(delayMs),
});
}
export function createBuildLaunchMpvIdleForJellyfinPlaybackMainDepsHandler(
deps: LaunchMpvForJellyfinDeps,
) {
return (): LaunchMpvForJellyfinDeps => ({
getMpvExecutablePath: () => deps.getMpvExecutablePath(),
getSocketPath: () => deps.getSocketPath(),
getLaunchMode: () => deps.getLaunchMode(),
platform: deps.platform,
execPath: deps.execPath,
getRuntimePluginEntrypoint: deps.getRuntimePluginEntrypoint,
getInstalledPluginDetection: deps.getInstalledPluginDetection,
getPluginRuntimeConfig: deps.getPluginRuntimeConfig,
getDefaultMpvLogPath: () => deps.getDefaultMpvLogPath(),
defaultMpvArgs: deps.defaultMpvArgs,
removeSocketPath: (socketPath: string) => deps.removeSocketPath(socketPath),
spawnMpv: (executablePath, args) => deps.spawnMpv(executablePath, args),
logWarn: (message: string, error: unknown) => deps.logWarn(message, error),
logInfo: (message: string) => deps.logInfo(message),
});
}
export function createBuildEnsureMpvConnectedForJellyfinPlaybackMainDepsHandler(
deps: EnsureMpvConnectedDeps,
) {
return (): EnsureMpvConnectedDeps => ({
getMpvClient: () => deps.getMpvClient(),
setMpvClient: (client) => deps.setMpvClient(client),
createMpvClient: () => deps.createMpvClient(),
waitForMpvConnected: (timeoutMs: number) => deps.waitForMpvConnected(timeoutMs),
launchMpvIdleForJellyfinPlayback: () => deps.launchMpvIdleForJellyfinPlayback(),
getAutoLaunchInFlight: () => deps.getAutoLaunchInFlight(),
setAutoLaunchInFlight: (promise) => deps.setAutoLaunchInFlight(promise),
connectTimeoutMs: deps.connectTimeoutMs,
autoLaunchTimeoutMs: deps.autoLaunchTimeoutMs,
});
}
@@ -1,44 +0,0 @@
import type {
createStartJellyfinRemoteSessionHandler,
createStopJellyfinRemoteSessionHandler,
} from './jellyfin-remote-session-lifecycle';
type StartJellyfinRemoteSessionMainDeps = Parameters<
typeof createStartJellyfinRemoteSessionHandler
>[0];
type StopJellyfinRemoteSessionMainDeps = Parameters<
typeof createStopJellyfinRemoteSessionHandler
>[0];
export function createBuildStartJellyfinRemoteSessionMainDepsHandler(
deps: StartJellyfinRemoteSessionMainDeps,
) {
return (): StartJellyfinRemoteSessionMainDeps => ({
getJellyfinConfig: () => deps.getJellyfinConfig(),
getCurrentSession: () => deps.getCurrentSession(),
setCurrentSession: (session) => deps.setCurrentSession(session),
createRemoteSessionService: (options) => deps.createRemoteSessionService(options),
getClientInfo: () => deps.getClientInfo(),
getHostName: () => deps.getHostName(),
defaultDeviceId: deps.defaultDeviceId,
defaultClientName: deps.defaultClientName,
defaultClientVersion: deps.defaultClientVersion,
handlePlay: (payload) => deps.handlePlay(payload),
handlePlaystate: (payload) => deps.handlePlaystate(payload),
handleGeneralCommand: (payload) => deps.handleGeneralCommand(payload),
logInfo: (message: string) => deps.logInfo(message),
logWarn: (message: string, details?: unknown) => deps.logWarn(message, details),
onSessionStateChanged: deps.onSessionStateChanged,
});
}
export function createBuildStopJellyfinRemoteSessionMainDepsHandler(
deps: StopJellyfinRemoteSessionMainDeps,
) {
return (): StopJellyfinRemoteSessionMainDeps => ({
getCurrentSession: () => deps.getCurrentSession(),
setCurrentSession: (session) => deps.setCurrentSession(session),
clearActivePlayback: () => deps.clearActivePlayback(),
onSessionStateChanged: deps.onSessionStateChanged,
});
}
@@ -1,23 +0,0 @@
import type { createPreloadJellyfinExternalSubtitlesHandler } from './jellyfin-subtitle-preload';
type PreloadJellyfinExternalSubtitlesMainDeps = Parameters<
typeof createPreloadJellyfinExternalSubtitlesHandler
>[0];
export function createBuildPreloadJellyfinExternalSubtitlesMainDepsHandler(
deps: PreloadJellyfinExternalSubtitlesMainDeps,
) {
return (): PreloadJellyfinExternalSubtitlesMainDeps => ({
listJellyfinSubtitleTracks: (session, clientInfo, itemId) =>
deps.listJellyfinSubtitleTracks(session, clientInfo, itemId),
getMpvClient: () => deps.getMpvClient(),
sendMpvCommand: (command) => deps.sendMpvCommand(command),
wait: (ms: number) => deps.wait(ms),
cacheSubtitleTrack: (track) => deps.cacheSubtitleTrack(track),
cleanupCachedSubtitles: (dirs) => deps.cleanupCachedSubtitles(dirs),
initSubtitlePrefetch: deps.initSubtitlePrefetch
? (sourcePath) => deps.initSubtitlePrefetch!(sourcePath)
: undefined,
logDebug: (message: string, error: unknown) => deps.logDebug(message, error),
});
}
@@ -1,30 +0,0 @@
import type { SubtitlePosition } from '../../types';
export function createBuildMediaRuntimeMainDepsHandler(deps: {
isRemoteMediaPath: (mediaPath: string) => boolean;
loadSubtitlePosition: () => SubtitlePosition | null;
getCurrentMediaPath: () => string | null;
getPendingSubtitlePosition: () => SubtitlePosition | null;
getSubtitlePositionsDir: () => string;
setCurrentMediaPath: (mediaPath: string | null) => void;
clearPendingSubtitlePosition: () => void;
setSubtitlePosition: (position: SubtitlePosition | null) => void;
broadcastToOverlayWindows: (channel: string, payload: unknown) => void;
getCurrentMediaTitle: () => string | null;
setCurrentMediaTitle: (title: string | null) => void;
}) {
return () => ({
isRemoteMediaPath: (mediaPath: string) => deps.isRemoteMediaPath(mediaPath),
loadSubtitlePosition: () => deps.loadSubtitlePosition(),
getCurrentMediaPath: () => deps.getCurrentMediaPath(),
getPendingSubtitlePosition: () => deps.getPendingSubtitlePosition(),
getSubtitlePositionsDir: () => deps.getSubtitlePositionsDir(),
setCurrentMediaPath: (nextPath: string | null) => deps.setCurrentMediaPath(nextPath),
clearPendingSubtitlePosition: () => deps.clearPendingSubtitlePosition(),
setSubtitlePosition: (position: SubtitlePosition | null) => deps.setSubtitlePosition(position),
broadcastSubtitlePosition: (position: SubtitlePosition | null) =>
deps.broadcastToOverlayWindows('subtitle-position:set', position),
getCurrentMediaTitle: () => deps.getCurrentMediaTitle(),
setCurrentMediaTitle: (title: string | null) => deps.setCurrentMediaTitle(title),
});
}
@@ -1,88 +0,0 @@
export function createBuildHandleMultiCopyDigitMainDepsHandler<TSubtitleTimingTracker>(deps: {
getSubtitleTimingTracker: () => TSubtitleTimingTracker;
writeClipboardText: (text: string) => void;
showMpvOsd: (text: string) => void;
handleMultiCopyDigitCore: (
count: number,
options: {
subtitleTimingTracker: TSubtitleTimingTracker;
writeClipboardText: (text: string) => void;
showMpvOsd: (text: string) => void;
},
) => void;
}) {
return () => ({
getSubtitleTimingTracker: () => deps.getSubtitleTimingTracker(),
writeClipboardText: (text: string) => deps.writeClipboardText(text),
showMpvOsd: (text: string) => deps.showMpvOsd(text),
handleMultiCopyDigitCore: (
count: number,
options: {
subtitleTimingTracker: TSubtitleTimingTracker;
writeClipboardText: (text: string) => void;
showMpvOsd: (text: string) => void;
},
) => deps.handleMultiCopyDigitCore(count, options),
});
}
export function createBuildCopyCurrentSubtitleMainDepsHandler<TSubtitleTimingTracker>(deps: {
getSubtitleTimingTracker: () => TSubtitleTimingTracker;
writeClipboardText: (text: string) => void;
showMpvOsd: (text: string) => void;
copyCurrentSubtitleCore: (options: {
subtitleTimingTracker: TSubtitleTimingTracker;
writeClipboardText: (text: string) => void;
showMpvOsd: (text: string) => void;
}) => void;
}) {
return () => ({
getSubtitleTimingTracker: () => deps.getSubtitleTimingTracker(),
writeClipboardText: (text: string) => deps.writeClipboardText(text),
showMpvOsd: (text: string) => deps.showMpvOsd(text),
copyCurrentSubtitleCore: (options: {
subtitleTimingTracker: TSubtitleTimingTracker;
writeClipboardText: (text: string) => void;
showMpvOsd: (text: string) => void;
}) => deps.copyCurrentSubtitleCore(options),
});
}
export function createBuildHandleMineSentenceDigitMainDepsHandler<
TSubtitleTimingTracker,
TAnkiIntegration,
>(deps: {
getSubtitleTimingTracker: () => TSubtitleTimingTracker;
getAnkiIntegration: () => TAnkiIntegration;
getCurrentSecondarySubText: () => string | undefined;
showMpvOsd: (text: string) => void;
logError: (message: string, err: unknown) => void;
handleMineSentenceDigitCore: (
count: number,
options: {
subtitleTimingTracker: TSubtitleTimingTracker;
ankiIntegration: TAnkiIntegration;
getCurrentSecondarySubText: () => string | undefined;
showMpvOsd: (text: string) => void;
logError: (message: string, err: unknown) => void;
},
) => void;
}) {
return () => ({
getSubtitleTimingTracker: () => deps.getSubtitleTimingTracker(),
getAnkiIntegration: () => deps.getAnkiIntegration(),
getCurrentSecondarySubText: () => deps.getCurrentSecondarySubText(),
showMpvOsd: (text: string) => deps.showMpvOsd(text),
logError: (message: string, err: unknown) => deps.logError(message, err),
handleMineSentenceDigitCore: (
count: number,
options: {
subtitleTimingTracker: TSubtitleTimingTracker;
ankiIntegration: TAnkiIntegration;
getCurrentSecondarySubText: () => string | undefined;
showMpvOsd: (text: string) => void;
logError: (message: string, err: unknown) => void;
},
) => deps.handleMineSentenceDigitCore(count, options),
});
}
@@ -1,22 +0,0 @@
import type {
createApplyJellyfinMpvDefaultsHandler,
createGetDefaultSocketPathHandler,
} from './mpv-jellyfin-defaults';
type ApplyJellyfinMpvDefaultsMainDeps = Parameters<typeof createApplyJellyfinMpvDefaultsHandler>[0];
type GetDefaultSocketPathMainDeps = Parameters<typeof createGetDefaultSocketPathHandler>[0];
export function createBuildApplyJellyfinMpvDefaultsMainDepsHandler(
deps: ApplyJellyfinMpvDefaultsMainDeps,
) {
return (): ApplyJellyfinMpvDefaultsMainDeps => ({
sendMpvCommandRuntime: (client, command) => deps.sendMpvCommandRuntime(client, command),
jellyfinLangPref: deps.jellyfinLangPref,
});
}
export function createBuildGetDefaultSocketPathMainDepsHandler(deps: GetDefaultSocketPathMainDeps) {
return (): GetDefaultSocketPathMainDeps => ({
platform: deps.platform,
});
}
-25
View File
@@ -1,25 +0,0 @@
import type { createAppendToMpvLogHandler, createShowMpvOsdHandler } from './mpv-osd-log';
type AppendToMpvLogMainDeps = Parameters<typeof createAppendToMpvLogHandler>[0];
type ShowMpvOsdMainDeps = Parameters<typeof createShowMpvOsdHandler>[0];
export function createBuildAppendToMpvLogMainDepsHandler(deps: AppendToMpvLogMainDeps) {
return (): AppendToMpvLogMainDeps => ({
getLogPath: () => deps.getLogPath(),
dirname: (targetPath: string) => deps.dirname(targetPath),
mkdir: (targetPath: string, options: { recursive: boolean }) => deps.mkdir(targetPath, options),
appendFile: (targetPath: string, data: string, options: { encoding: 'utf8' }) =>
deps.appendFile(targetPath, data, options),
now: () => deps.now(),
});
}
export function createBuildShowMpvOsdMainDepsHandler(deps: ShowMpvOsdMainDeps) {
return (): ShowMpvOsdMainDeps => ({
appendToMpvLog: (message: string) => deps.appendToMpvLog(message),
showMpvOsdRuntime: (mpvClient, text, fallbackLog) =>
deps.showMpvOsdRuntime(mpvClient, text, fallbackLog),
getMpvClient: () => deps.getMpvClient(),
logInfo: (line: string) => deps.logInfo(line),
});
}
+4 -14
View File
@@ -1,27 +1,17 @@
import { createAppendToMpvLogHandler, createShowMpvOsdHandler } from './mpv-osd-log';
import {
createBuildAppendToMpvLogMainDepsHandler,
createBuildShowMpvOsdMainDepsHandler,
} from './mpv-osd-log-main-deps';
type AppendToMpvLogMainDeps = Parameters<typeof createBuildAppendToMpvLogMainDepsHandler>[0];
type ShowMpvOsdMainDeps = Parameters<typeof createBuildShowMpvOsdMainDepsHandler>[0];
type AppendToMpvLogMainDeps = Parameters<typeof createAppendToMpvLogHandler>[0];
type ShowMpvOsdMainDeps = Parameters<typeof createShowMpvOsdHandler>[0];
export function createMpvOsdRuntimeHandlers(deps: {
appendToMpvLogMainDeps: AppendToMpvLogMainDeps;
buildShowMpvOsdMainDeps: (appendToMpvLog: (message: string) => void) => ShowMpvOsdMainDeps;
}) {
const appendToMpvLogMainDeps = createBuildAppendToMpvLogMainDepsHandler(
deps.appendToMpvLogMainDeps,
)();
const appendToMpvLogRuntime = createAppendToMpvLogHandler(appendToMpvLogMainDeps);
const appendToMpvLogRuntime = createAppendToMpvLogHandler(deps.appendToMpvLogMainDeps);
const appendToMpvLog = (message: string) => appendToMpvLogRuntime.appendToMpvLog(message);
const flushMpvLog = async () => appendToMpvLogRuntime.flushMpvLog();
const showMpvOsdMainDeps = createBuildShowMpvOsdMainDepsHandler(
deps.buildShowMpvOsdMainDeps(appendToMpvLog),
)();
const showMpvOsdHandler = createShowMpvOsdHandler(showMpvOsdMainDeps);
const showMpvOsdHandler = createShowMpvOsdHandler(deps.buildShowMpvOsdMainDeps(appendToMpvLog));
const showMpvOsd = (text: string) => showMpvOsdHandler(text);
return {
@@ -1,16 +0,0 @@
import type { createUpdateMpvSubtitleRenderMetricsHandler } from './mpv-subtitle-render-metrics';
type UpdateMpvSubtitleRenderMetricsMainDeps = Parameters<
typeof createUpdateMpvSubtitleRenderMetricsHandler
>[0];
export function createBuildUpdateMpvSubtitleRenderMetricsMainDepsHandler(
deps: UpdateMpvSubtitleRenderMetricsMainDeps,
) {
return (): UpdateMpvSubtitleRenderMetricsMainDeps => ({
getCurrentMetrics: () => deps.getCurrentMetrics(),
setCurrentMetrics: (metrics) => deps.setCurrentMetrics(metrics),
applyPatch: (current, patch) => deps.applyPatch(current, patch),
broadcastMetrics: (metrics) => deps.broadcastMetrics(metrics),
});
}
@@ -1,12 +0,0 @@
import type { NumericShortcutRuntimeOptions } from '../../core/services/numeric-shortcut';
export function createBuildNumericShortcutRuntimeMainDepsHandler(
deps: NumericShortcutRuntimeOptions,
) {
return (): NumericShortcutRuntimeOptions => ({
globalShortcut: deps.globalShortcut,
showMpvOsd: (text: string) => deps.showMpvOsd(text),
setTimer: (handler: () => void, timeoutMs: number) => deps.setTimer(handler, timeoutMs),
clearTimer: (timer: ReturnType<typeof setTimeout>) => deps.clearTimer(timer),
});
}
@@ -1,29 +0,0 @@
import type {
createCancelNumericShortcutSessionHandler,
createStartNumericShortcutSessionHandler,
} from './numeric-shortcut-session-handlers';
type CancelNumericShortcutSessionMainDeps = Parameters<
typeof createCancelNumericShortcutSessionHandler
>[0];
type StartNumericShortcutSessionMainDeps = Parameters<
typeof createStartNumericShortcutSessionHandler
>[0];
export function createBuildCancelNumericShortcutSessionMainDepsHandler(
deps: CancelNumericShortcutSessionMainDeps,
) {
return (): CancelNumericShortcutSessionMainDeps => ({
session: deps.session,
});
}
export function createBuildStartNumericShortcutSessionMainDepsHandler(
deps: StartNumericShortcutSessionMainDeps,
) {
return (): StartNumericShortcutSessionMainDeps => ({
session: deps.session,
onDigit: (digit: number) => deps.onDigit(digit),
messages: deps.messages,
});
}
@@ -2,13 +2,9 @@ import {
createCancelNumericShortcutSessionHandler,
createStartNumericShortcutSessionHandler,
} from './numeric-shortcut-session-handlers';
import {
createBuildCancelNumericShortcutSessionMainDepsHandler,
createBuildStartNumericShortcutSessionMainDepsHandler,
} from './numeric-shortcut-session-main-deps';
type CancelNumericShortcutSessionMainDeps = Parameters<
typeof createBuildCancelNumericShortcutSessionMainDepsHandler
typeof createCancelNumericShortcutSessionHandler
>[0];
export function createNumericShortcutSessionRuntimeHandlers(deps: {
@@ -19,14 +15,11 @@ export function createNumericShortcutSessionRuntimeHandlers(deps: {
tryBeginMultiCopyOverlaySelection?: (timeoutMs: number) => boolean;
tryBeginMineSentenceOverlaySelection?: (timeoutMs: number) => boolean;
}) {
const cancelPendingMultiCopyMainDeps = createBuildCancelNumericShortcutSessionMainDepsHandler({
const cancelPendingMultiCopyHandler = createCancelNumericShortcutSessionHandler({
session: deps.multiCopySession,
})();
const cancelPendingMultiCopyHandler = createCancelNumericShortcutSessionHandler(
cancelPendingMultiCopyMainDeps,
);
});
const startPendingMultiCopyMainDeps = createBuildStartNumericShortcutSessionMainDepsHandler({
const startPendingMultiCopyHandler = createStartNumericShortcutSessionHandler({
session: deps.multiCopySession,
onDigit: deps.onMultiCopyDigit,
messages: {
@@ -34,32 +27,21 @@ export function createNumericShortcutSessionRuntimeHandlers(deps: {
timeout: 'Copy timeout',
cancelled: 'Cancelled',
},
})();
const startPendingMultiCopyHandler = createStartNumericShortcutSessionHandler(
startPendingMultiCopyMainDeps,
);
});
const cancelPendingMineSentenceMultipleMainDeps =
createBuildCancelNumericShortcutSessionMainDepsHandler({
session: deps.mineSentenceSession,
})();
const cancelPendingMineSentenceMultipleHandler = createCancelNumericShortcutSessionHandler(
cancelPendingMineSentenceMultipleMainDeps,
);
const cancelPendingMineSentenceMultipleHandler = createCancelNumericShortcutSessionHandler({
session: deps.mineSentenceSession,
});
const startPendingMineSentenceMultipleMainDeps =
createBuildStartNumericShortcutSessionMainDepsHandler({
session: deps.mineSentenceSession,
onDigit: deps.onMineSentenceDigit,
messages: {
prompt: 'Mine how many lines? Press 1-9 (Esc to cancel)',
timeout: 'Mine sentence timeout',
cancelled: 'Cancelled',
},
})();
const startPendingMineSentenceMultipleHandler = createStartNumericShortcutSessionHandler(
startPendingMineSentenceMultipleMainDeps,
);
const startPendingMineSentenceMultipleHandler = createStartNumericShortcutSessionHandler({
session: deps.mineSentenceSession,
onDigit: deps.onMineSentenceDigit,
messages: {
prompt: 'Mine how many lines? Press 1-9 (Esc to cancel)',
timeout: 'Mine sentence timeout',
cancelled: 'Cancelled',
},
});
return {
cancelPendingMultiCopy: () => cancelPendingMultiCopyHandler(),
@@ -1,25 +0,0 @@
import type { OverlayWindowResolver } from '../overlay-runtime';
type OverlayContentMeasurementStoreMainDeps = {
now: () => number;
warn: (message: string) => void;
};
export function createBuildOverlayContentMeasurementStoreMainDepsHandler(
deps: OverlayContentMeasurementStoreMainDeps,
) {
return (): OverlayContentMeasurementStoreMainDeps => ({
now: () => deps.now(),
warn: (message: string) => deps.warn(message),
});
}
export function createBuildOverlayModalRuntimeMainDepsHandler(deps: OverlayWindowResolver) {
return (): OverlayWindowResolver => ({
getMainWindow: () => deps.getMainWindow(),
getModalWindow: () => deps.getModalWindow(),
createModalWindow: () => deps.createModalWindow(),
getModalGeometry: () => deps.getModalGeometry(),
setModalWindowBounds: (geometry) => deps.setModalWindowBounds(geometry),
});
}
@@ -1,46 +0,0 @@
import type {
createAppendClipboardVideoToQueueHandler,
createHandleOverlayModalClosedHandler,
createSetOverlayVisibleHandler,
createToggleOverlayHandler,
} from './overlay-main-actions';
type SetOverlayVisibleMainDeps = Parameters<typeof createSetOverlayVisibleHandler>[0];
type ToggleOverlayMainDeps = Parameters<typeof createToggleOverlayHandler>[0];
type HandleOverlayModalClosedMainDeps = Parameters<typeof createHandleOverlayModalClosedHandler>[0];
type AppendClipboardVideoToQueueMainDeps = Parameters<
typeof createAppendClipboardVideoToQueueHandler
>[0];
export function createBuildSetOverlayVisibleMainDepsHandler(deps: SetOverlayVisibleMainDeps) {
return (): SetOverlayVisibleMainDeps => ({
setVisibleOverlayVisible: (visible: boolean) => deps.setVisibleOverlayVisible(visible),
});
}
export function createBuildToggleOverlayMainDepsHandler(deps: ToggleOverlayMainDeps) {
return (): ToggleOverlayMainDeps => ({
toggleVisibleOverlay: () => deps.toggleVisibleOverlay(),
});
}
export function createBuildHandleOverlayModalClosedMainDepsHandler(
deps: HandleOverlayModalClosedMainDeps,
) {
return (): HandleOverlayModalClosedMainDeps => ({
handleOverlayModalClosedRuntime: (modal) => deps.handleOverlayModalClosedRuntime(modal),
});
}
export function createBuildAppendClipboardVideoToQueueMainDepsHandler(
deps: AppendClipboardVideoToQueueMainDeps,
) {
return (): AppendClipboardVideoToQueueMainDeps => ({
appendClipboardVideoToQueueRuntime: (options) =>
deps.appendClipboardVideoToQueueRuntime(options),
getMpvClient: () => deps.getMpvClient(),
readClipboardText: () => deps.readClipboardText(),
showMpvOsd: (text: string) => deps.showMpvOsd(text),
sendMpvCommand: (command: (string | number)[]) => deps.sendMpvCommand(command),
});
}
@@ -1,85 +0,0 @@
import {
createBroadcastRuntimeOptionsChangedHandler,
createGetRuntimeOptionsStateHandler,
createOpenRuntimeOptionsPaletteHandler,
createRestorePreviousSecondarySubVisibilityHandler,
createSendToActiveOverlayWindowHandler,
createSetOverlayDebugVisualizationEnabledHandler,
} from './overlay-runtime-main-actions';
type GetRuntimeOptionsStateMainDeps = Parameters<typeof createGetRuntimeOptionsStateHandler>[0];
type RestorePreviousSecondarySubVisibilityMainDeps = Parameters<
typeof createRestorePreviousSecondarySubVisibilityHandler
>[0];
type BroadcastRuntimeOptionsChangedMainDeps = Parameters<
typeof createBroadcastRuntimeOptionsChangedHandler
>[0];
type SendToActiveOverlayWindowMainDeps = Parameters<
typeof createSendToActiveOverlayWindowHandler
>[0];
type SetOverlayDebugVisualizationEnabledMainDeps = Parameters<
typeof createSetOverlayDebugVisualizationEnabledHandler
>[0];
type OpenRuntimeOptionsPaletteMainDeps = Parameters<
typeof createOpenRuntimeOptionsPaletteHandler
>[0];
export function createBuildGetRuntimeOptionsStateMainDepsHandler(
deps: GetRuntimeOptionsStateMainDeps,
) {
return (): GetRuntimeOptionsStateMainDeps => ({
getRuntimeOptionsManager: () => deps.getRuntimeOptionsManager(),
});
}
export function createBuildRestorePreviousSecondarySubVisibilityMainDepsHandler(
deps: RestorePreviousSecondarySubVisibilityMainDeps,
) {
return (): RestorePreviousSecondarySubVisibilityMainDeps => ({
getMpvClient: () => deps.getMpvClient(),
});
}
export function createBuildBroadcastRuntimeOptionsChangedMainDepsHandler(
deps: BroadcastRuntimeOptionsChangedMainDeps,
) {
return (): BroadcastRuntimeOptionsChangedMainDeps => ({
broadcastRuntimeOptionsChangedRuntime: (getRuntimeOptionsState, broadcastToOverlayWindows) =>
deps.broadcastRuntimeOptionsChangedRuntime(getRuntimeOptionsState, broadcastToOverlayWindows),
getRuntimeOptionsState: () => deps.getRuntimeOptionsState(),
broadcastToOverlayWindows: (channel: string, ...args: unknown[]) =>
deps.broadcastToOverlayWindows(channel, ...args),
});
}
export function createBuildSendToActiveOverlayWindowMainDepsHandler(
deps: SendToActiveOverlayWindowMainDeps,
) {
return (): SendToActiveOverlayWindowMainDeps => ({
sendToActiveOverlayWindowRuntime: (channel, payload, runtimeOptions) =>
deps.sendToActiveOverlayWindowRuntime(channel, payload, runtimeOptions),
});
}
export function createBuildSetOverlayDebugVisualizationEnabledMainDepsHandler(
deps: SetOverlayDebugVisualizationEnabledMainDeps,
) {
return (): SetOverlayDebugVisualizationEnabledMainDeps => ({
setOverlayDebugVisualizationEnabledRuntime: (currentEnabled, nextEnabled, setCurrentEnabled) =>
deps.setOverlayDebugVisualizationEnabledRuntime(
currentEnabled,
nextEnabled,
setCurrentEnabled,
),
getCurrentEnabled: () => deps.getCurrentEnabled(),
setCurrentEnabled: (enabled: boolean) => deps.setCurrentEnabled(enabled),
});
}
export function createBuildOpenRuntimeOptionsPaletteMainDepsHandler(
deps: OpenRuntimeOptionsPaletteMainDeps,
) {
return (): OpenRuntimeOptionsPaletteMainDeps => ({
openRuntimeOptionsPaletteRuntime: () => deps.openRuntimeOptionsPaletteRuntime(),
});
}
@@ -1,43 +0,0 @@
import type {
createRefreshOverlayShortcutsHandler,
createRegisterOverlayShortcutsHandler,
createSyncOverlayShortcutsHandler,
createUnregisterOverlayShortcutsHandler,
} from './overlay-shortcuts-lifecycle';
type RegisterOverlayShortcutsMainDeps = Parameters<typeof createRegisterOverlayShortcutsHandler>[0];
type UnregisterOverlayShortcutsMainDeps = Parameters<
typeof createUnregisterOverlayShortcutsHandler
>[0];
type SyncOverlayShortcutsMainDeps = Parameters<typeof createSyncOverlayShortcutsHandler>[0];
type RefreshOverlayShortcutsMainDeps = Parameters<typeof createRefreshOverlayShortcutsHandler>[0];
export function createBuildRegisterOverlayShortcutsMainDepsHandler(
deps: RegisterOverlayShortcutsMainDeps,
) {
return (): RegisterOverlayShortcutsMainDeps => ({
overlayShortcutsRuntime: deps.overlayShortcutsRuntime,
});
}
export function createBuildUnregisterOverlayShortcutsMainDepsHandler(
deps: UnregisterOverlayShortcutsMainDeps,
) {
return (): UnregisterOverlayShortcutsMainDeps => ({
overlayShortcutsRuntime: deps.overlayShortcutsRuntime,
});
}
export function createBuildSyncOverlayShortcutsMainDepsHandler(deps: SyncOverlayShortcutsMainDeps) {
return (): SyncOverlayShortcutsMainDeps => ({
overlayShortcutsRuntime: deps.overlayShortcutsRuntime,
});
}
export function createBuildRefreshOverlayShortcutsMainDepsHandler(
deps: RefreshOverlayShortcutsMainDeps,
) {
return (): RefreshOverlayShortcutsMainDeps => ({
overlayShortcutsRuntime: deps.overlayShortcutsRuntime,
});
}
@@ -4,46 +4,26 @@ import {
createSyncOverlayShortcutsHandler,
createUnregisterOverlayShortcutsHandler,
} from './overlay-shortcuts-lifecycle';
import {
createBuildRefreshOverlayShortcutsMainDepsHandler,
createBuildRegisterOverlayShortcutsMainDepsHandler,
createBuildSyncOverlayShortcutsMainDepsHandler,
createBuildUnregisterOverlayShortcutsMainDepsHandler,
} from './overlay-shortcuts-lifecycle-main-deps';
type RegisterOverlayShortcutsMainDeps = Parameters<
typeof createBuildRegisterOverlayShortcutsMainDepsHandler
>[0];
type RegisterOverlayShortcutsMainDeps = Parameters<typeof createRegisterOverlayShortcutsHandler>[0];
export function createOverlayShortcutsRuntimeHandlers(deps: {
overlayShortcutsRuntimeMainDeps: RegisterOverlayShortcutsMainDeps;
}) {
const registerOverlayShortcutsMainDeps = createBuildRegisterOverlayShortcutsMainDepsHandler(
deps.overlayShortcutsRuntimeMainDeps,
)();
const registerOverlayShortcutsHandler = createRegisterOverlayShortcutsHandler(
registerOverlayShortcutsMainDeps,
deps.overlayShortcutsRuntimeMainDeps,
);
const unregisterOverlayShortcutsMainDeps = createBuildUnregisterOverlayShortcutsMainDepsHandler(
deps.overlayShortcutsRuntimeMainDeps,
)();
const unregisterOverlayShortcutsHandler = createUnregisterOverlayShortcutsHandler(
unregisterOverlayShortcutsMainDeps,
deps.overlayShortcutsRuntimeMainDeps,
);
const syncOverlayShortcutsMainDeps = createBuildSyncOverlayShortcutsMainDepsHandler(
deps.overlayShortcutsRuntimeMainDeps,
)();
const syncOverlayShortcutsHandler = createSyncOverlayShortcutsHandler(
syncOverlayShortcutsMainDeps,
deps.overlayShortcutsRuntimeMainDeps,
);
const refreshOverlayShortcutsMainDeps = createBuildRefreshOverlayShortcutsMainDepsHandler(
deps.overlayShortcutsRuntimeMainDeps,
)();
const refreshOverlayShortcutsHandler = createRefreshOverlayShortcutsHandler(
refreshOverlayShortcutsMainDeps,
deps.overlayShortcutsRuntimeMainDeps,
);
return {
+4 -10
View File
@@ -7,10 +7,6 @@ import {
createBuildToggleVisibleOverlayMainDepsHandler,
} from './overlay-visibility-actions-main-deps';
import { createSetOverlayVisibleHandler, createToggleOverlayHandler } from './overlay-main-actions';
import {
createBuildSetOverlayVisibleMainDepsHandler,
createBuildToggleOverlayMainDepsHandler,
} from './overlay-main-actions-main-deps';
type SetVisibleOverlayVisibleMainDeps = Parameters<
typeof createBuildSetVisibleOverlayVisibleMainDepsHandler
@@ -36,15 +32,13 @@ export function createOverlayVisibilityRuntime(deps: OverlayVisibilityRuntimeDep
})();
const toggleVisibleOverlay = createToggleVisibleOverlayHandler(toggleVisibleOverlayMainDeps);
const setOverlayVisibleMainDeps = createBuildSetOverlayVisibleMainDepsHandler({
const setOverlayVisible = createSetOverlayVisibleHandler({
setVisibleOverlayVisible: (visible) => setVisibleOverlayVisible(visible),
})();
const setOverlayVisible = createSetOverlayVisibleHandler(setOverlayVisibleMainDeps);
});
const toggleOverlayMainDeps = createBuildToggleOverlayMainDepsHandler({
const toggleOverlay = createToggleOverlayHandler({
toggleVisibleOverlay: () => toggleVisibleOverlay(),
})();
const toggleOverlay = createToggleOverlayHandler(toggleOverlayMainDeps);
});
return {
setVisibleOverlayVisible,
@@ -1,53 +0,0 @@
import type { AnilistStateRuntimeDeps } from './anilist-state';
import type { ConfigDerivedRuntimeDeps } from './config-derived';
import type { ImmersionMediaRuntimeDeps } from './immersion-media';
import type { MainSubsyncRuntimeDeps } from './subsync-runtime';
export function createBuildImmersionMediaRuntimeMainDepsHandler(deps: ImmersionMediaRuntimeDeps) {
return (): ImmersionMediaRuntimeDeps => ({
getResolvedConfig: () => deps.getResolvedConfig(),
defaultImmersionDbPath: deps.defaultImmersionDbPath,
getTracker: () => deps.getTracker(),
getMpvClient: () => deps.getMpvClient(),
getCurrentMediaPath: () => deps.getCurrentMediaPath(),
getCurrentMediaTitle: () => deps.getCurrentMediaTitle(),
sleep: deps.sleep,
seedWaitMs: deps.seedWaitMs,
seedAttempts: deps.seedAttempts,
logDebug: (message: string) => deps.logDebug(message),
logInfo: (message: string) => deps.logInfo(message),
});
}
export function createBuildAnilistStateRuntimeMainDepsHandler(deps: AnilistStateRuntimeDeps) {
return (): AnilistStateRuntimeDeps => ({
getClientSecretState: () => deps.getClientSecretState(),
setClientSecretState: (next) => deps.setClientSecretState(next),
getRetryQueueState: () => deps.getRetryQueueState(),
setRetryQueueState: (next) => deps.setRetryQueueState(next),
getUpdateQueueSnapshot: () => deps.getUpdateQueueSnapshot(),
clearStoredToken: () => deps.clearStoredToken(),
clearCachedAccessToken: () => deps.clearCachedAccessToken(),
});
}
export function createBuildConfigDerivedRuntimeMainDepsHandler(deps: ConfigDerivedRuntimeDeps) {
return (): ConfigDerivedRuntimeDeps => ({
getResolvedConfig: () => deps.getResolvedConfig(),
getRuntimeOptionsManager: () => deps.getRuntimeOptionsManager(),
defaultJimakuLanguagePreference: deps.defaultJimakuLanguagePreference,
defaultJimakuMaxEntryResults: deps.defaultJimakuMaxEntryResults,
defaultJimakuApiBaseUrl: deps.defaultJimakuApiBaseUrl,
});
}
export function createBuildMainSubsyncRuntimeMainDepsHandler(deps: MainSubsyncRuntimeDeps) {
return (): MainSubsyncRuntimeDeps => ({
getMpvClient: () => deps.getMpvClient(),
getResolvedConfig: () => deps.getResolvedConfig(),
getSubsyncInProgress: () => deps.getSubsyncInProgress(),
setSubsyncInProgress: (inProgress: boolean) => deps.setSubsyncInProgress(inProgress),
showMpvOsd: (text: string) => deps.showMpvOsd(text),
openManualPicker: (payload) => deps.openManualPicker(payload),
});
}
@@ -1,21 +0,0 @@
import type { SecondarySubMode } from '../../types';
export function createBuildCycleSecondarySubModeMainDepsHandler(deps: {
getSecondarySubMode: () => SecondarySubMode;
setSecondarySubMode: (mode: SecondarySubMode) => void;
getLastSecondarySubToggleAtMs: () => number;
setLastSecondarySubToggleAtMs: (timestampMs: number) => void;
broadcastToOverlayWindows: (channel: string, mode: SecondarySubMode) => void;
showMpvOsd: (text: string) => void;
}) {
return () => ({
getSecondarySubMode: () => deps.getSecondarySubMode(),
setSecondarySubMode: (mode: SecondarySubMode) => deps.setSecondarySubMode(mode),
getLastSecondarySubToggleAtMs: () => deps.getLastSecondarySubToggleAtMs(),
setLastSecondarySubToggleAtMs: (timestampMs: number) =>
deps.setLastSecondarySubToggleAtMs(timestampMs),
broadcastSecondarySubMode: (mode: SecondarySubMode) =>
deps.broadcastToOverlayWindows('secondary-subtitle:mode', mode),
showMpvOsd: (text: string) => deps.showMpvOsd(text),
});
}
@@ -1,18 +1,8 @@
import { createBuildCycleSecondarySubModeMainDepsHandler } from './secondary-sub-mode-main-deps';
type CycleSecondarySubModeMainDeps = Parameters<
typeof createBuildCycleSecondarySubModeMainDepsHandler
>[0];
type CycleSecondarySubModeDeps = ReturnType<
ReturnType<typeof createBuildCycleSecondarySubModeMainDepsHandler>
>;
import type { CycleSecondarySubModeDeps } from '../../core/services/subtitle-position';
export function createCycleSecondarySubModeRuntimeHandler(deps: {
cycleSecondarySubModeMainDeps: CycleSecondarySubModeMainDeps;
cycleSecondarySubModeMainDeps: CycleSecondarySubModeDeps;
cycleSecondarySubMode: (deps: CycleSecondarySubModeDeps) => void;
}) {
const buildCycleSecondarySubModeMainDepsHandler = createBuildCycleSecondarySubModeMainDepsHandler(
deps.cycleSecondarySubModeMainDeps,
);
return () => deps.cycleSecondarySubMode(buildCycleSecondarySubModeMainDepsHandler());
return () => deps.cycleSecondarySubMode(deps.cycleSecondarySubModeMainDeps);
}
@@ -1,45 +0,0 @@
import type { createCriticalConfigErrorHandler, createReloadConfigHandler } from './startup-config';
type ReloadConfigMainDeps = Parameters<typeof createReloadConfigHandler>[0];
type CriticalConfigErrorMainDeps = Parameters<typeof createCriticalConfigErrorHandler>[0];
export function createBuildReloadConfigMainDepsHandler(deps: ReloadConfigMainDeps) {
return (): ReloadConfigMainDeps => ({
reloadConfigStrict: () => deps.reloadConfigStrict(),
logInfo: (message: string) => deps.logInfo(message),
logDebug: (message: string) => deps.logDebug(message),
logWarning: (message: string) => deps.logWarning(message),
showDesktopNotification: (title: string, options: { body: string }) =>
deps.showDesktopNotification(title, options),
startConfigHotReload: () => deps.startConfigHotReload(),
shouldRefreshAnilistClientSecretState: deps.shouldRefreshAnilistClientSecretState
? () => deps.shouldRefreshAnilistClientSecretState?.() !== false
: undefined,
refreshAnilistClientSecretState: (options: { force: boolean }) =>
deps.refreshAnilistClientSecretState(options),
failHandlers: {
logError: (details: string) => deps.failHandlers.logError(details),
showErrorBox: (title: string, details: string) =>
deps.failHandlers.showErrorBox(title, details),
setExitCode: deps.failHandlers.setExitCode
? (code: number) => deps.failHandlers.setExitCode?.(code)
: undefined,
quit: () => deps.failHandlers.quit(),
},
});
}
export function createBuildCriticalConfigErrorMainDepsHandler(deps: CriticalConfigErrorMainDeps) {
return (): CriticalConfigErrorMainDeps => ({
getConfigPath: () => deps.getConfigPath(),
failHandlers: {
logError: (details: string) => deps.failHandlers.logError(details),
showErrorBox: (title: string, details: string) =>
deps.failHandlers.showErrorBox(title, details),
setExitCode: deps.failHandlers.setExitCode
? (code: number) => deps.failHandlers.setExitCode?.(code)
: undefined,
quit: () => deps.failHandlers.quit(),
},
});
}
@@ -1,21 +0,0 @@
import type { AppLifecycleRuntimeRunnerParams } from '../startup-lifecycle';
export function createBuildAppLifecycleRuntimeRunnerMainDepsHandler(
deps: AppLifecycleRuntimeRunnerParams,
) {
return (): AppLifecycleRuntimeRunnerParams => ({
app: deps.app,
platform: deps.platform,
shouldStartApp: deps.shouldStartApp,
parseArgs: deps.parseArgs,
handleCliCommand: deps.handleCliCommand,
printHelp: deps.printHelp,
logNoRunningInstance: deps.logNoRunningInstance,
startControlServer: deps.startControlServer,
onReady: deps.onReady,
onWillQuitCleanup: deps.onWillQuitCleanup,
shouldRestoreWindowsOnActivate: deps.shouldRestoreWindowsOnActivate,
restoreWindowsOnActivate: deps.restoreWindowsOnActivate,
shouldQuitOnWindowAllClosed: deps.shouldQuitOnWindowAllClosed,
});
}
+4 -10
View File
@@ -1,10 +1,7 @@
import { createBuildStartupBootstrapRuntimeFactoryDepsHandler } from './startup-bootstrap-deps-builder';
import { createBuildAppLifecycleRuntimeRunnerMainDepsHandler } from './startup-lifecycle-main-deps';
import type { AppLifecycleRuntimeRunnerParams } from '../startup-lifecycle';
import { createBuildStartupBootstrapMainDepsHandler } from './startup-bootstrap-main-deps';
type AppLifecycleRuntimeRunnerMainDeps = Parameters<
typeof createBuildAppLifecycleRuntimeRunnerMainDepsHandler
>[0];
type StartupBootstrapMainDeps = Parameters<typeof createBuildStartupBootstrapMainDepsHandler>[0];
type StartupBootstrapRuntimeFactoryDeps = Parameters<
typeof createBuildStartupBootstrapRuntimeFactoryDepsHandler
@@ -15,9 +12,9 @@ export function createStartupRuntimeHandlers<
TStartupState,
TStartupBootstrapRuntimeDeps = unknown,
>(deps: {
appLifecycleRuntimeRunnerMainDeps: AppLifecycleRuntimeRunnerMainDeps;
appLifecycleRuntimeRunnerMainDeps: AppLifecycleRuntimeRunnerParams;
createAppLifecycleRuntimeRunner: (
params: AppLifecycleRuntimeRunnerMainDeps,
params: AppLifecycleRuntimeRunnerParams,
) => (args: TCliArgs) => void;
buildStartupBootstrapMainDeps: (
startAppLifecycle: (args: TCliArgs) => void,
@@ -28,11 +25,8 @@ export function createStartupRuntimeHandlers<
runStartupBootstrapRuntime: (deps: TStartupBootstrapRuntimeDeps) => TStartupState;
applyStartupState: (startupState: TStartupState) => void;
}) {
const appLifecycleRuntimeRunnerMainDeps = createBuildAppLifecycleRuntimeRunnerMainDepsHandler(
deps.appLifecycleRuntimeRunnerMainDeps,
)();
const appLifecycleRuntimeRunner = deps.createAppLifecycleRuntimeRunner(
appLifecycleRuntimeRunnerMainDeps,
deps.appLifecycleRuntimeRunnerMainDeps,
);
const startupBootstrapMainDeps = createBuildStartupBootstrapMainDepsHandler(
@@ -1,52 +0,0 @@
import type {
createLaunchBackgroundWarmupTaskHandler,
createStartBackgroundWarmupsHandler,
} from './startup-warmups';
type LaunchBackgroundWarmupTaskMainDeps = Parameters<
typeof createLaunchBackgroundWarmupTaskHandler
>[0];
type StartBackgroundWarmupsMainDeps = Parameters<typeof createStartBackgroundWarmupsHandler>[0];
export function createBuildLaunchBackgroundWarmupTaskMainDepsHandler(
deps: LaunchBackgroundWarmupTaskMainDeps,
) {
return (): LaunchBackgroundWarmupTaskMainDeps => ({
now: () => deps.now(),
logDebug: (message: string) => deps.logDebug(message),
logWarn: (message: string) => deps.logWarn(message),
});
}
export function createBuildStartBackgroundWarmupsMainDepsHandler(
deps: StartBackgroundWarmupsMainDeps,
) {
return (): StartBackgroundWarmupsMainDeps => ({
getStarted: () => deps.getStarted(),
setStarted: (started: boolean) => deps.setStarted(started),
isTexthookerOnlyMode: () => deps.isTexthookerOnlyMode(),
launchTask: (label: string, task: () => Promise<void>) => deps.launchTask(label, task),
createMecabTokenizerAndCheck: () => deps.createMecabTokenizerAndCheck(),
ensureYomitanExtensionLoaded: () => deps.ensureYomitanExtensionLoaded(),
prewarmSubtitleDictionaries: () => deps.prewarmSubtitleDictionaries(),
shouldWarmupMecab: () => deps.shouldWarmupMecab(),
shouldWarmupYomitanExtension: () => deps.shouldWarmupYomitanExtension(),
shouldWarmupSubtitleDictionaries: () => deps.shouldWarmupSubtitleDictionaries(),
shouldWarmupJellyfinRemoteSession: () => deps.shouldWarmupJellyfinRemoteSession(),
shouldAutoConnectJellyfinRemote: () => deps.shouldAutoConnectJellyfinRemote(),
startJellyfinRemoteSession: () => deps.startJellyfinRemoteSession(),
...(deps.onYomitanExtensionWarmupScheduled
? {
onYomitanExtensionWarmupScheduled: (promise: Promise<void>) =>
deps.onYomitanExtensionWarmupScheduled!(promise),
}
: {}),
...(deps.onTokenizationWarmupScheduled
? {
onTokenizationWarmupScheduled: (promise: Promise<void>) =>
deps.onTokenizationWarmupScheduled!(promise),
}
: {}),
logDebug: deps.logDebug,
});
}
@@ -1,21 +0,0 @@
import type {
createLoadSubtitlePositionHandler,
createSaveSubtitlePositionHandler,
} from './subtitle-position';
type LoadSubtitlePositionMainDeps = Parameters<typeof createLoadSubtitlePositionHandler>[0];
type SaveSubtitlePositionMainDeps = Parameters<typeof createSaveSubtitlePositionHandler>[0];
export function createBuildLoadSubtitlePositionMainDepsHandler(deps: LoadSubtitlePositionMainDeps) {
return (): LoadSubtitlePositionMainDeps => ({
loadSubtitlePositionCore: () => deps.loadSubtitlePositionCore(),
setSubtitlePosition: (position) => deps.setSubtitlePosition(position),
});
}
export function createBuildSaveSubtitlePositionMainDepsHandler(deps: SaveSubtitlePositionMainDeps) {
return (): SaveSubtitlePositionMainDeps => ({
saveSubtitlePositionCore: (position) => deps.saveSubtitlePositionCore(position),
setSubtitlePosition: (position) => deps.setSubtitlePosition(position),
});
}
@@ -1,13 +0,0 @@
import type { SubtitleProcessingControllerDeps } from '../../core/services/subtitle-processing-controller';
export function createBuildSubtitleProcessingControllerMainDepsHandler(
deps: SubtitleProcessingControllerDeps,
) {
return (): SubtitleProcessingControllerDeps => ({
tokenizeSubtitle: (text: string) => deps.tokenizeSubtitle(text),
emitSubtitle: (payload) => deps.emitSubtitle(payload),
onProcessingSettled: () => deps.onProcessingSettled?.(),
logDebug: deps.logDebug,
now: deps.now,
});
}
@@ -0,0 +1,62 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { createPrefetchAwareSubtitleProcessingController } from './subtitle-processing-runtime';
import type { SubtitleData } from '../../types';
function flushMicrotasks(): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, 0));
}
function createHarness() {
const events: string[] = [];
let resolveTokenization: ((value: SubtitleData | null) => void) | undefined;
const controller = createPrefetchAwareSubtitleProcessingController({
tokenizeSubtitle: () =>
new Promise<SubtitleData | null>((resolve) => {
resolveTokenization = resolve;
}),
emitSubtitlePayload: (payload, options) =>
events.push(
`emit:${payload.text}:tokens=${payload.tokens ? 'yes' : 'no'}:resume=${options.resumePrefetch}`,
),
resumePrefetch: () => events.push('resume'),
logDebug: () => {},
now: () => 0,
});
return {
events,
controller,
resolveTokenization: (value: SubtitleData | null) => resolveTokenization?.(value),
};
}
test('subtitle processing resumes prefetch after the tokenized emit settles', async () => {
const { events, controller, resolveTokenization } = createHarness();
controller.onSubtitleChange('一行目');
await flushMicrotasks();
// The provisional plain payload is out, but tokenization is still pending.
assert.deepEqual(events, ['emit:一行目:tokens=no:resume=false']);
resolveTokenization({ text: '一行目', tokens: [] });
await flushMicrotasks();
await flushMicrotasks();
assert.deepEqual(events, [
'emit:一行目:tokens=no:resume=false',
'emit:一行目:tokens=yes:resume=false',
'resume',
]);
});
test('subtitle processing resumes prefetch when tokenization fails without another emit', async () => {
const { events, controller, resolveTokenization } = createHarness();
controller.onSubtitleChange('一行目');
await flushMicrotasks();
assert.deepEqual(events, ['emit:一行目:tokens=no:resume=false']);
resolveTokenization(null);
await flushMicrotasks();
await flushMicrotasks();
assert.deepEqual(events, ['emit:一行目:tokens=no:resume=false', 'resume']);
});
@@ -0,0 +1,33 @@
import {
createSubtitleProcessingController,
type SubtitleProcessingControllerDeps,
} from '../../core/services/subtitle-processing-controller';
import type { SubtitleData } from '../../types';
/**
* Creates the main-process subtitle processing controller and ties it to the
* subtitle prefetch pause.
*
* Controller emits never release the prefetch pause: the first emit for an
* uncached line is the provisional plain payload, sent before tokenization
* starts, so resuming on it would put prefetch back in contention with the
* on-screen line for the single parser window. The pause is released once the
* controller has no work left, which covers the runs that end without an emit
* (suppressed duplicate, failed tokenization) as well as the ones that deliver
* a payload.
*/
export function createPrefetchAwareSubtitleProcessingController(deps: {
tokenizeSubtitle: SubtitleProcessingControllerDeps['tokenizeSubtitle'];
emitSubtitlePayload: (payload: SubtitleData, options: { resumePrefetch: boolean }) => void;
resumePrefetch: () => void;
logDebug: (message: string) => void;
now: () => number;
}) {
return createSubtitleProcessingController({
tokenizeSubtitle: deps.tokenizeSubtitle,
emitSubtitle: (payload) => deps.emitSubtitlePayload(payload, { resumePrefetch: false }),
onProcessingSettled: () => deps.resumePrefetch(),
logDebug: deps.logDebug,
now: deps.now,
});
}
-108
View File
@@ -1,108 +0,0 @@
import type { DictionaryBackend } from '../../types/config';
export function createBuildResolveTrayIconPathMainDepsHandler(deps: {
resolveTrayIconPathRuntime: (options: {
platform: string;
resourcesPath: string;
appPath: string;
dirname: string;
joinPath: (...parts: string[]) => string;
fileExists: (path: string) => boolean;
}) => string | null;
platform: string;
resourcesPath: string;
appPath: string;
dirname: string;
joinPath: (...parts: string[]) => string;
fileExists: (path: string) => boolean;
}) {
return () => ({
resolveTrayIconPathRuntime: deps.resolveTrayIconPathRuntime,
platform: deps.platform,
resourcesPath: deps.resourcesPath,
appPath: deps.appPath,
dirname: deps.dirname,
joinPath: deps.joinPath,
fileExists: deps.fileExists,
});
}
export function createBuildTrayMenuTemplateMainDepsHandler<TMenuItem>(deps: {
buildTrayMenuTemplateRuntime: (handlers: {
platform?: string;
openSessionHelp: () => void;
openChangelog: () => void;
openTexthookerInBrowser: () => void;
showTexthookerPage: boolean;
openFirstRunSetup: () => void;
showFirstRunSetup: boolean;
openWindowsMpvLauncherSetup: () => void;
showWindowsMpvLauncherSetup: boolean;
dictionaryBackend: DictionaryBackend;
openHachidoriSettings: () => void;
openYomitanSettings: () => void;
openConfigSettings: () => void;
openSyncUi: () => void;
openYoutubeBrowser: () => void;
exportLogs: () => void;
openJellyfinSetup: () => void;
showJellyfinDiscovery: boolean;
jellyfinDiscoveryActive: boolean;
toggleJellyfinDiscovery: (checked: boolean) => void;
openAnilistSetup: () => void;
checkForUpdates: () => void;
quitApp: () => void;
}) => TMenuItem[];
initializeOverlayRuntime: () => void;
isOverlayRuntimeInitialized: () => boolean;
openSessionHelpModal: () => void;
openChangelogModal: () => void;
openTexthookerInBrowser: () => void;
showTexthookerPage: () => boolean;
showFirstRunSetup: () => boolean;
openFirstRunSetupWindow: (force?: boolean) => void;
showWindowsMpvLauncherSetup: () => boolean;
getDictionaryBackend: () => DictionaryBackend;
openHachidoriSettings: () => void;
openYomitanSettings: () => void;
openConfigSettingsWindow: () => void;
openSyncUiWindow: () => void;
openYoutubeBrowserWindow: () => void;
exportLogs: () => void;
openJellyfinSetupWindow: () => void;
isJellyfinConfigured: () => boolean;
isJellyfinDiscoveryActive: () => boolean;
toggleJellyfinDiscovery: (checked: boolean) => void | Promise<void>;
platform?: string;
openAnilistSetupWindow: () => void;
checkForUpdates: () => void;
quitApp: () => void;
}) {
return () => ({
buildTrayMenuTemplateRuntime: deps.buildTrayMenuTemplateRuntime,
platform: deps.platform,
initializeOverlayRuntime: deps.initializeOverlayRuntime,
isOverlayRuntimeInitialized: deps.isOverlayRuntimeInitialized,
openSessionHelpModal: deps.openSessionHelpModal,
openChangelogModal: deps.openChangelogModal,
openTexthookerInBrowser: deps.openTexthookerInBrowser,
showTexthookerPage: deps.showTexthookerPage,
showFirstRunSetup: deps.showFirstRunSetup,
openFirstRunSetupWindow: deps.openFirstRunSetupWindow,
showWindowsMpvLauncherSetup: deps.showWindowsMpvLauncherSetup,
getDictionaryBackend: deps.getDictionaryBackend,
openHachidoriSettings: deps.openHachidoriSettings,
openYomitanSettings: deps.openYomitanSettings,
openConfigSettingsWindow: deps.openConfigSettingsWindow,
openSyncUiWindow: deps.openSyncUiWindow,
openYoutubeBrowserWindow: deps.openYoutubeBrowserWindow,
exportLogs: deps.exportLogs,
openJellyfinSetupWindow: deps.openJellyfinSetupWindow,
isJellyfinConfigured: deps.isJellyfinConfigured,
isJellyfinDiscoveryActive: deps.isJellyfinDiscoveryActive,
toggleJellyfinDiscovery: deps.toggleJellyfinDiscovery,
openAnilistSetupWindow: deps.openAnilistSetupWindow,
checkForUpdates: deps.checkForUpdates,
quitApp: deps.quitApp,
});
}
+4 -14
View File
@@ -7,16 +7,10 @@ import {
createBuildTrayMenuTemplateHandler,
createResolveTrayIconPathHandler,
} from './tray-main-actions';
import {
createBuildResolveTrayIconPathMainDepsHandler,
createBuildTrayMenuTemplateMainDepsHandler,
} from './tray-main-deps';
type ResolveTrayIconPathMainDeps = Parameters<
typeof createBuildResolveTrayIconPathMainDepsHandler
>[0];
type ResolveTrayIconPathMainDeps = Parameters<typeof createResolveTrayIconPathHandler>[0];
type BuildTrayMenuTemplateMainDeps<TMenuItem> = Parameters<
typeof createBuildTrayMenuTemplateMainDepsHandler<TMenuItem>
typeof createBuildTrayMenuTemplateHandler<TMenuItem>
>[0];
type EnsureTrayMainDeps<TTrayMenu> = Parameters<
typeof createBuildEnsureTrayMainDepsHandler<TrayLike, TTrayMenu, TrayIconLike>
@@ -32,12 +26,8 @@ export function createTrayRuntimeHandlers<TMenuItem, TMenu>(deps: {
destroyTrayDeps: DestroyTrayMainDeps;
buildMenuFromTemplate: (template: TMenuItem[]) => TMenu;
}) {
const resolveTrayIconPath = createResolveTrayIconPathHandler(
createBuildResolveTrayIconPathMainDepsHandler(deps.resolveTrayIconPathDeps)(),
);
const buildTrayMenuTemplate = createBuildTrayMenuTemplateHandler(
createBuildTrayMenuTemplateMainDepsHandler(deps.buildTrayMenuTemplateDeps)(),
);
const resolveTrayIconPath = createResolveTrayIconPathHandler(deps.resolveTrayIconPathDeps);
const buildTrayMenuTemplate = createBuildTrayMenuTemplateHandler(deps.buildTrayMenuTemplateDeps);
const buildTrayMenu = () => deps.buildMenuFromTemplate(buildTrayMenuTemplate());
const ensureTray = createEnsureTrayHandler(
@@ -1,34 +0,0 @@
import type {
createEnsureYomitanExtensionLoadedHandler,
createLoadYomitanExtensionHandler,
} from './yomitan-extension-loader';
type LoadYomitanExtensionMainDeps = Parameters<typeof createLoadYomitanExtensionHandler>[0];
type EnsureYomitanExtensionLoadedMainDeps = Parameters<
typeof createEnsureYomitanExtensionLoadedHandler
>[0];
export function createBuildLoadYomitanExtensionMainDepsHandler(deps: LoadYomitanExtensionMainDeps) {
return (): LoadYomitanExtensionMainDeps => ({
loadYomitanExtensionCore: (options) => deps.loadYomitanExtensionCore(options),
userDataPath: deps.userDataPath,
externalProfilePath: deps.externalProfilePath,
getYomitanParserWindow: () => deps.getYomitanParserWindow(),
setYomitanParserWindow: (window) => deps.setYomitanParserWindow(window),
setYomitanParserReadyPromise: (promise) => deps.setYomitanParserReadyPromise(promise),
setYomitanParserInitPromise: (promise) => deps.setYomitanParserInitPromise(promise),
setYomitanExtension: (extension) => deps.setYomitanExtension(extension),
setYomitanSession: (session) => deps.setYomitanSession(session),
});
}
export function createBuildEnsureYomitanExtensionLoadedMainDepsHandler(
deps: EnsureYomitanExtensionLoadedMainDeps,
) {
return (): EnsureYomitanExtensionLoadedMainDeps => ({
getYomitanExtension: () => deps.getYomitanExtension(),
getLoadInFlight: () => deps.getLoadInFlight(),
setLoadInFlight: (promise) => deps.setLoadInFlight(promise),
loadYomitanExtension: () => deps.loadYomitanExtension(),
});
}
+9 -22
View File
@@ -3,17 +3,11 @@ import {
createLoadYomitanExtensionHandler,
} from './yomitan-extension-loader';
import type { Extension } from 'electron';
import {
createBuildEnsureYomitanExtensionLoadedMainDepsHandler,
createBuildLoadYomitanExtensionMainDepsHandler,
} from './yomitan-extension-loader-main-deps';
type LoadYomitanExtensionMainDeps = Parameters<
typeof createBuildLoadYomitanExtensionMainDepsHandler
>[0];
type LoadYomitanExtensionMainDeps = Parameters<typeof createLoadYomitanExtensionHandler>[0];
type EnsureYomitanExtensionLoadedMainDeps = Omit<
Parameters<typeof createBuildEnsureYomitanExtensionLoadedMainDepsHandler>[0],
Parameters<typeof createEnsureYomitanExtensionLoadedHandler>[0],
'loadYomitanExtension'
>;
@@ -23,7 +17,7 @@ export type YomitanExtensionRuntimeDeps = LoadYomitanExtensionMainDeps &
};
export function createYomitanExtensionRuntime(deps: YomitanExtensionRuntimeDeps) {
const buildLoadYomitanExtensionMainDepsHandler = createBuildLoadYomitanExtensionMainDepsHandler({
const loadYomitanExtensionHandler = createLoadYomitanExtensionHandler({
loadYomitanExtensionCore: deps.loadYomitanExtensionCore,
userDataPath: deps.userDataPath,
externalProfilePath: deps.externalProfilePath,
@@ -34,20 +28,13 @@ export function createYomitanExtensionRuntime(deps: YomitanExtensionRuntimeDeps)
setYomitanExtension: deps.setYomitanExtension,
setYomitanSession: deps.setYomitanSession,
});
const loadYomitanExtensionHandler = createLoadYomitanExtensionHandler(
buildLoadYomitanExtensionMainDepsHandler(),
);
const buildEnsureYomitanExtensionLoadedMainDepsHandler =
createBuildEnsureYomitanExtensionLoadedMainDepsHandler({
getYomitanExtension: deps.getYomitanExtension,
getLoadInFlight: deps.getLoadInFlight,
setLoadInFlight: deps.setLoadInFlight,
loadYomitanExtension: () => loadYomitanExtensionHandler(),
});
const ensureYomitanExtensionLoadedHandler = createEnsureYomitanExtensionLoadedHandler(
buildEnsureYomitanExtensionLoadedMainDepsHandler(),
);
const ensureYomitanExtensionLoadedHandler = createEnsureYomitanExtensionLoadedHandler({
getYomitanExtension: deps.getYomitanExtension,
getLoadInFlight: deps.getLoadInFlight,
setLoadInFlight: deps.setLoadInFlight,
loadYomitanExtension: () => loadYomitanExtensionHandler(),
});
let lastNotifiedExtension: Extension | null = null;
let notifyingExtension: Extension | null = null;