mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-08-05 19:21:35 -07:00
fix(subtitles): release prefetch pause across all priming paths
The repeated-subtitle pause leak was only fixed for ordinary subtitle changes. Startup autoplay priming and visible-overlay priming pause the same way and also ignored whether any tokenization was scheduled, so a cache miss on text the controller already holds (mining a card while the line is on screen) left prefetching idle for the rest of the cue. Pause and release are now one operation via pausePrefetchUntilEmit, and both controller entry points report whether an emit is expected. A repeat arriving while a run is already in flight keeps the pause, since that run still emits. Also invalidate the character dictionary lookups centrally from the sync completion handler instead of at three manager call sites. Ordinary selection sync never invalidated them, so a stale non-null name candidate list could skip a newly added name for up to five seconds; a missing list falls back to the exhaustive scan, but a stale one does not. Ordering matters: the invalidation runs before the subtitle refreshes so they re-tokenize against the new dictionary content. Docs: subtitle-overlay-priming no longer claims every subtitle change calls onSeek().
This commit is contained in:
@@ -42,8 +42,8 @@ test('scheduleSubtitlePrefetchRefresh logs refresh failures from timer callback'
|
||||
setActiveParsedSubtitleMediaPath: () => {},
|
||||
subtitleProcessingController: {
|
||||
consumeCachedSubtitle: () => null,
|
||||
onSubtitleChange: () => {},
|
||||
refreshCurrentSubtitle: () => {},
|
||||
onSubtitleChange: () => true,
|
||||
refreshCurrentSubtitle: () => true,
|
||||
},
|
||||
emitSubtitlePayload: () => {},
|
||||
getSubtitlePrefetchService: () => null,
|
||||
@@ -93,13 +93,24 @@ test('primeCurrentSubtitleForAutoplay refreshes active subtitle cues when mpv su
|
||||
setActiveParsedSubtitleMediaPath: () => {},
|
||||
subtitleProcessingController: {
|
||||
consumeCachedSubtitle: () => null,
|
||||
onSubtitleChange: (text) => calls.push(`change:${text}`),
|
||||
refreshCurrentSubtitle: (text) => calls.push(`refresh:${text ?? ''}`),
|
||||
onSubtitleChange: (text) => {
|
||||
calls.push(`change:${text}`);
|
||||
return true;
|
||||
},
|
||||
refreshCurrentSubtitle: (text) => {
|
||||
calls.push(`refresh:${text ?? ''}`);
|
||||
return true;
|
||||
},
|
||||
},
|
||||
emitSubtitlePayload: (payload, options) =>
|
||||
calls.push(`emit:${payload.text}:resume=${options?.resumePrefetch !== false}`),
|
||||
getSubtitlePrefetchService: () => ({
|
||||
pause: () => calls.push('prefetch:pause'),
|
||||
pause: () => {
|
||||
calls.push('prefetch:pause');
|
||||
},
|
||||
resume: () => {
|
||||
calls.push('prefetch:resume');
|
||||
},
|
||||
}),
|
||||
getLastObservedTimePos: () => 12,
|
||||
getVisibleOverlayVisible: () => true,
|
||||
@@ -151,13 +162,24 @@ test('primeCurrentSubtitleForAutoplay emits raw first paint on cache miss before
|
||||
setActiveParsedSubtitleMediaPath: () => {},
|
||||
subtitleProcessingController: {
|
||||
consumeCachedSubtitle: () => null,
|
||||
onSubtitleChange: (text) => calls.push(`change:${text}`),
|
||||
refreshCurrentSubtitle: (text) => calls.push(`refresh:${text ?? ''}`),
|
||||
onSubtitleChange: (text) => {
|
||||
calls.push(`change:${text}`);
|
||||
return true;
|
||||
},
|
||||
refreshCurrentSubtitle: (text) => {
|
||||
calls.push(`refresh:${text ?? ''}`);
|
||||
return true;
|
||||
},
|
||||
},
|
||||
emitSubtitlePayload: (payload, options) =>
|
||||
calls.push(`emit:${payload.text}:resume=${options?.resumePrefetch !== false}`),
|
||||
getSubtitlePrefetchService: () => ({
|
||||
pause: () => calls.push('prefetch:pause'),
|
||||
pause: () => {
|
||||
calls.push('prefetch:pause');
|
||||
},
|
||||
resume: () => {
|
||||
calls.push('prefetch:resume');
|
||||
},
|
||||
}),
|
||||
getLastObservedTimePos: () => 12,
|
||||
getVisibleOverlayVisible: () => true,
|
||||
@@ -179,3 +201,65 @@ test('primeCurrentSubtitleForAutoplay emits raw first paint on cache miss before
|
||||
'change:起動字幕',
|
||||
]);
|
||||
});
|
||||
|
||||
test('primeCurrentSubtitleForAutoplay releases the prefetch pause when no tokenization is scheduled', async () => {
|
||||
const calls: string[] = [];
|
||||
let currentSubText = '';
|
||||
const mediaPath = '/media/video.mkv';
|
||||
|
||||
const runtime = createAutoplaySubtitlePrimingRuntime({
|
||||
getCurrentMediaPath: () => mediaPath,
|
||||
getMpvClient: () => ({
|
||||
connected: true,
|
||||
currentVideoPath: mediaPath,
|
||||
requestProperty: async (name) => {
|
||||
if (name === 'sub-text') return '起動字幕';
|
||||
return null;
|
||||
},
|
||||
}),
|
||||
setCurrentSubText: (text) => {
|
||||
currentSubText = text;
|
||||
},
|
||||
getCurrentSubText: () => currentSubText,
|
||||
getCurrentSubtitleData: () => null,
|
||||
getActiveParsedSubtitleCues: () => [],
|
||||
setActiveParsedSubtitleMediaPath: () => {},
|
||||
subtitleProcessingController: {
|
||||
// The tokenization cache was invalidated (for example by mining a card),
|
||||
// so the cached payload is gone...
|
||||
consumeCachedSubtitle: () => null,
|
||||
// ...but the controller still holds this text, so it schedules nothing
|
||||
// and no emit will arrive to release the pause.
|
||||
onSubtitleChange: (text) => {
|
||||
calls.push(`change:${text}`);
|
||||
return false;
|
||||
},
|
||||
refreshCurrentSubtitle: () => true,
|
||||
},
|
||||
emitSubtitlePayload: (payload, options) =>
|
||||
calls.push(`emit:${payload.text}:resume=${options?.resumePrefetch !== false}`),
|
||||
getSubtitlePrefetchService: () => ({
|
||||
pause: () => {
|
||||
calls.push('prefetch:pause');
|
||||
},
|
||||
resume: () => {
|
||||
calls.push('prefetch:resume');
|
||||
},
|
||||
}),
|
||||
getLastObservedTimePos: () => 12,
|
||||
getVisibleOverlayVisible: () => true,
|
||||
emitSecondarySubtitle: () => {},
|
||||
initSubtitlePrefetch: async () => {},
|
||||
refreshSubtitlePrefetchFromActiveTrack: async () => {},
|
||||
logDebug: () => {},
|
||||
});
|
||||
|
||||
await runtime.primeCurrentSubtitleForAutoplay(mediaPath);
|
||||
|
||||
assert.deepEqual(calls, [
|
||||
'prefetch:pause',
|
||||
'emit:起動字幕:resume=false',
|
||||
'change:起動字幕',
|
||||
'prefetch:resume',
|
||||
]);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user