feat(mining): cache remote media windows during card creation

- Reuse one temporary download for timing review, audio, and screenshots
- Expire cached windows after inactivity and clean them up on exit
This commit is contained in:
2026-09-02 01:21:26 -07:00
parent 760b3e1d3d
commit f90ee78204
19 changed files with 1424 additions and 79 deletions
+18
View File
@@ -464,6 +464,8 @@ import { handleCliCommandRuntimeServiceWithContext } from './main/cli-runtime';
import { createOverlayModalRuntimeService } from './main/overlay-runtime';
import { createOverlayModalInputState } from './main/runtime/overlay-modal-input-state';
import { MediaTimingPreviewSession } from './core/services/media-timing-preview';
import { getSharedRemoteMediaWindowCache } from './core/services/remote-media-window-cache';
import { resolveMediaGenerationInput } from './anki-integration/media-source';
import { generateSpeechWaveform } from './core/services/media-timing-waveform';
import {
collectMediaTimingContextLines,
@@ -2896,6 +2898,21 @@ const mediaTimingReviewRuntime = createMediaTimingReviewRuntime({
configService.getConfig().mpv.executablePath || process.env.SUBMINER_MPV_PATH?.trim() || '',
createPreviewSession: () => new MediaTimingPreviewSession(),
generateWaveform: (options) => generateSpeechWaveform(options),
resolveMediaSource: async () => {
const resolved = await resolveMediaGenerationInput(appState.mpvClient, 'audio', {
getCachedMediaPath: (currentVideoPath, kind) =>
getCachedYoutubeMediaPathForCurrentPlayback(currentVideoPath, kind),
remoteCacheMode: shouldRequireYoutubeMediaCacheForCurrentPlayback() ? 'required' : 'optional',
});
return resolved
? {
path: resolved.path,
...(resolved.inputOptions ? { inputOptions: resolved.inputOptions } : {}),
singleResolvedStream: resolved.singleResolvedStream,
}
: null;
},
acquireMediaWindow: (source, range) => getSharedRemoteMediaWindowCache().acquire(source, range),
getSubtitleContextLines: (range) =>
collectMediaTimingContextLines({
cues: appState.activeParsedSubtitleCues,
@@ -3973,6 +3990,7 @@ const {
cleanupInternalSubtitleTrackCache: () => cachedInternalSubtitleTrackExtractor.clear(),
cleanupYoutubeSubtitleTempDirs: () => youtubeFlowRuntime.cleanupSubtitleTempDirs(),
cleanupYoutubeMediaCache: () => youtubeMediaCache.cleanup(),
cleanupRemoteMediaWindows: () => getSharedRemoteMediaWindowCache().cleanup(),
cleanupJellyfinSubtitleCache: () => cleanupJellyfinSubtitleCache(),
stopDiscordPresenceService: () => {
void appState.discordPresenceService?.stop();