fix(lab): make recorded replay seek-safe

This commit is contained in:
DCCONSTRUCTIONS
2026-08-24 02:15:30 +03:00
parent 7400fe2fd7
commit 992c5a8b74
12 changed files with 1096 additions and 287 deletions
@@ -37,7 +37,7 @@ import type {
} from "../../core/laboratory/m4ReplayThreat";
import { buildM4LocalSurface } from "../../core/laboratory/m4LocalSurface";
import { recordedObservationSources } from "../../core/observation/recordedObservationSources";
import { replayObservationSession } from "../../core/observation/sessionArchive";
import { resolveObservationSessionReplay } from "../../core/observation/useObservationSessions";
import type { ObservationSourceDescriptor } from "../../core/runtime/contracts";
import {
useM4ThreatTimelineFrame,
@@ -148,14 +148,11 @@ export function M4ReplayThreatVisual({
const controller = new AbortController();
setVideoLoading(true);
setVideoError(null);
void replayObservationSession(timeline.recordedSourceSessionId, {
void resolveObservationSessionReplay(timeline.recordedSourceSessionId, {
signal: controller.signal,
})
.then((replay) => {
if (replay.kind !== "ready") {
throw new Error("RIGHT-видео RAVNOVES00 ещё готовится к воспроизведению.");
}
const source = recordedObservationSources(replay.launch).find(
.then((launch) => {
const source = recordedObservationSources(launch).find(
(candidate) => candidate.modality === "video"
&& candidate.semanticChannelId === "camera.video.recorded",
);
@@ -532,9 +529,14 @@ export function M4ReplayThreatVisual({
semanticOverlay={mediaMode === "video" ? semanticOverlay : undefined}
ariaLabel={`M4.6 recorded-realtime frame ${frame?.sequence ?? 0}: ${activeBoxes.length} proposals`}
interactive={false}
segmentSequence={frame ? frame.sequence + 1 : undefined}
segmentSequence={
timelineFrame.activeSequence === null
? undefined
: timelineFrame.activeSequence + 1
}
segmentCount={timeline.frameCount}
onPlaybackChange={playbackController.synchronize}
onPlayingRejected={() => playbackController.setPlaying(false)}
/>
) : videoError ? (
<SpatialState message={videoError} />
@@ -29,6 +29,18 @@ export function m4ThreatChunkWindowStarts(
).filter((start) => start >= 0 && start < frameCount);
}
export function cancelM4ThreatChunkRequestsOutsideWindow<T extends { abort(): void }>(
inFlight: Map<number, T>,
desiredStarts: readonly number[],
): void {
const desired = new Set(desiredStarts);
for (const [start, controller] of inFlight) {
if (desired.has(start)) continue;
controller.abort();
inFlight.delete(start);
}
}
export function useM4ThreatTimelineMetadata(resultId: string) {
const [timeline, setTimeline] = useState<M4ThreatTimeline | null>(null);
const [error, setError] = useState<string | null>(null);
@@ -105,6 +117,7 @@ export function useM4ThreatTimelineFrame({
chunkSize,
timeline.frameCount,
);
cancelM4ThreatChunkRequestsOutsideWindow(inFlight.current, starts);
for (const start of starts) {
if (chunksRef.current.has(start) || inFlight.current.has(start)) continue;
const controller = new AbortController();