mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-09-11 17:16:18 -07:00
fix(mining): copy multi-line subtitles backward from current line (#231)
This commit is contained in:
@@ -503,14 +503,21 @@ test('canonical ASS cues replace live glyph spam for display, history, and immer
|
||||
assert.deepEqual(timing.slice(3), [{ text: '今 手にある物差しでは', start: 1.2, end: 3.8 }]);
|
||||
assert.equal(immersion.length, 3);
|
||||
|
||||
// A jump of exactly the seek threshold counts as a seek, matching the time-pos
|
||||
// handler's own `>=` boundary.
|
||||
handlers.onTimePosUpdate?.(4.5);
|
||||
handlers.onTimePosUpdate?.(2);
|
||||
// Jumping back to a brief previous line moves time-pos by less than the general
|
||||
// seek threshold. It is still a backward seek, so the revisited line records
|
||||
// again; otherwise multi-line copy would keep treating the later line as current.
|
||||
handlers.onTimePosUpdate?.(3.9);
|
||||
handlers.onTimePosUpdate?.(2.9);
|
||||
handlers.recordSubtitleTiming('今', 0.8, 1.5);
|
||||
|
||||
assert.deepEqual(timing.slice(4), [{ text: '今 手にある物差しでは', start: 1.2, end: 3.8 }]);
|
||||
|
||||
// Tiny time-pos jitter is not a seek and must not re-record the line.
|
||||
handlers.onTimePosUpdate?.(3.0);
|
||||
handlers.onTimePosUpdate?.(2.9);
|
||||
handlers.recordSubtitleTiming('今', 0.8, 1.5);
|
||||
assert.equal(timing.length, 5);
|
||||
|
||||
handlers.recordImmersionSubtitleLine('Maid\nCafe', 10, 12);
|
||||
handlers.recordSubtitleTiming('Maid\nCafe', 10, 12);
|
||||
assert.equal(immersion.length, 3);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { createSubtitleLineDedupGate } from '../../core/services/subtitle-line-dedup-gate';
|
||||
import type { MergedToken, SubtitleCue, SubtitleData } from '../../types';
|
||||
import { SEEK_LIKE_TIME_DELTA_SECONDS } from './mpv-main-event-actions';
|
||||
import {
|
||||
resolveCanonicalPrimarySubtitle,
|
||||
resolvePrimarySubtitleText,
|
||||
@@ -115,6 +114,8 @@ export function createBuildBindMpvMainEventHandlersMainDepsHandler(deps: {
|
||||
// after the change is dropped instead of landing in the next session.
|
||||
let subtitleSessionEpoch = 0;
|
||||
let lastTimePosForTimingReset: number | null = null;
|
||||
// Small margin so time-pos jitter is not mistaken for a backward seek.
|
||||
const BACKWARD_SEEK_TIMING_RESET_SECONDS = 0.25;
|
||||
const canonicalCueKey = (cue: SubtitleCue): string =>
|
||||
`${cue.startTime}|${cue.endTime}|${cue.text}`;
|
||||
const resetSubtitleDeduplication = (): void => {
|
||||
@@ -344,13 +345,15 @@ export function createBuildBindMpvMainEventHandlersMainDepsHandler(deps: {
|
||||
deps.reportJellyfinRemoteProgress(forceImmediate),
|
||||
consumeExplicitSeek: deps.consumeExplicitSeek,
|
||||
onTimePosUpdate: (time: number) => {
|
||||
// Timing history is a viewing log: after a real backward seek, a rewatched
|
||||
// canonical line should enter it again. Immersion stats keep their
|
||||
// once-per-media deduplication and are not reset here.
|
||||
// Timing history is a viewing log: after any backward seek, a rewatched canonical
|
||||
// line should enter it again so multi-line copy treats it as the current line.
|
||||
// Playback never moves time-pos backward on its own, so even a short jump to a
|
||||
// brief previous line counts. Immersion stats keep their once-per-media
|
||||
// deduplication and are not reset here.
|
||||
if (
|
||||
Number.isFinite(time) &&
|
||||
lastTimePosForTimingReset !== null &&
|
||||
time <= lastTimePosForTimingReset - SEEK_LIKE_TIME_DELTA_SECONDS
|
||||
time <= lastTimePosForTimingReset - BACKWARD_SEEK_TIMING_RESET_SECONDS
|
||||
) {
|
||||
recordedTimingCanonicalKeys.clear();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user