mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-10-22 05:01:24 -07:00
fix(subtitle-prefetch): reuse startup runs and prefetch overlap lines
- Warm combined text for overlapping subtitle cues. - Preserve valid in-flight tokenization across startup restarts while rejecting invalidated cache results.
This commit is contained in:
9 files changed
+274
-6
No files matched your search
@@ -300,3 +300,65 @@ test('prefetch service deduplicates repeated cue text within a run', async () =>
|
||||
);
|
||||
assert.ok(tokenizedTexts.includes('other'));
|
||||
});
|
||||
|
||||
test('prefetch service keeps an in-flight result from a superseded run', async () => {
|
||||
const cues = makeCues(5);
|
||||
const cachedTexts: string[] = [];
|
||||
let releaseFirst!: () => void;
|
||||
const firstGate = new Promise<void>((resolve) => {
|
||||
releaseFirst = resolve;
|
||||
});
|
||||
|
||||
const service = createSubtitlePrefetchService({
|
||||
cues,
|
||||
tokenizeSubtitle: async (text) => {
|
||||
if (text === 'line-0') await firstGate;
|
||||
return { text, tokens: [] };
|
||||
},
|
||||
preCacheTokenization: (text) => {
|
||||
cachedTexts.push(text);
|
||||
},
|
||||
getCacheGeneration: () => 0,
|
||||
priorityWindowSize: 3,
|
||||
});
|
||||
|
||||
service.start(0);
|
||||
await flushMicrotasks();
|
||||
service.stop();
|
||||
releaseFirst();
|
||||
await flushMicrotasks();
|
||||
|
||||
assert.deepEqual(cachedTexts, ['line-0']);
|
||||
});
|
||||
|
||||
test('prefetch service drops an in-flight result after cache invalidation', async () => {
|
||||
const cues = makeCues(5);
|
||||
const cachedTexts: string[] = [];
|
||||
let generation = 0;
|
||||
let releaseFirst!: () => void;
|
||||
const firstGate = new Promise<void>((resolve) => {
|
||||
releaseFirst = resolve;
|
||||
});
|
||||
|
||||
const service = createSubtitlePrefetchService({
|
||||
cues,
|
||||
tokenizeSubtitle: async (text) => {
|
||||
if (text === 'line-0') await firstGate;
|
||||
return { text, tokens: [] };
|
||||
},
|
||||
preCacheTokenization: (text) => {
|
||||
cachedTexts.push(text);
|
||||
},
|
||||
getCacheGeneration: () => generation,
|
||||
priorityWindowSize: 3,
|
||||
});
|
||||
|
||||
service.start(0);
|
||||
await flushMicrotasks();
|
||||
service.stop();
|
||||
generation += 1;
|
||||
releaseFirst();
|
||||
await flushMicrotasks();
|
||||
|
||||
assert.deepEqual(cachedTexts, []);
|
||||
});
|
||||
@@ -7,6 +7,12 @@ export interface SubtitlePrefetchServiceDeps {
|
||||
tokenizeSubtitle: (text: string) => Promise<SubtitleData | null>;
|
||||
preCacheTokenization: (text: string, data: SubtitleData) => void;
|
||||
hasCachedTokenization?: (text: string) => boolean;
|
||||
/**
|
||||
* Bumped whenever the tokenization cache is invalidated. A result is cached only if
|
||||
* the generation is unchanged since its tokenization began; without this dep every
|
||||
* result counts as fresh.
|
||||
*/
|
||||
getCacheGeneration?: () => number;
|
||||
priorityWindowSize?: number;
|
||||
}
|
||||
|
||||
@@ -88,9 +94,12 @@ export function createSubtitlePrefetchService(
|
||||
}
|
||||
warmedKeys.add(cacheKey);
|
||||
|
||||
// Freshness follows the cache generation, not the run: a seek or restart that
|
||||
// supersedes this run leaves the result valid, and the parser time is already spent.
|
||||
const generation = deps.getCacheGeneration?.();
|
||||
try {
|
||||
const result = await deps.tokenizeSubtitle(cue.text);
|
||||
if (result && !stopped && runId === currentRunId) {
|
||||
if (result && generation === deps.getCacheGeneration?.()) {
|
||||
deps.preCacheTokenization(cue.text, result);
|
||||
}
|
||||
} catch {
|
||||
|
||||
@@ -43,6 +43,8 @@ export interface SubtitleProcessingController {
|
||||
*/
|
||||
notePlainSubtitleEmitted: (text: string) => void;
|
||||
invalidateTokenizationCache: () => void;
|
||||
/** Incremented by invalidateTokenizationCache; lets async writers detect stale results. */
|
||||
getCacheGeneration: () => number;
|
||||
preCacheTokenization: (text: string, data: SubtitleData) => void;
|
||||
consumeCachedSubtitle: (text: string) => SubtitleData | null;
|
||||
hasCachedSubtitle: (text: string) => boolean;
|
||||
@@ -253,6 +255,7 @@ export function createSubtitleProcessingController(
|
||||
tokenizationCache.clear();
|
||||
cacheGeneration += 1;
|
||||
},
|
||||
getCacheGeneration: () => cacheGeneration,
|
||||
preCacheTokenization: (text: string, data: SubtitleData) => {
|
||||
setCachedTokenization(text, data);
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user