refactor(lab): объединить RAV004 в единый Rerun replay

This commit is contained in:
DCCONSTRUCTIONS
2026-08-30 16:40:12 +03:00
parent 9c5259dbc9
commit 81fdf6904a
18 changed files with 750 additions and 186 deletions
@@ -36,7 +36,6 @@ import {
createRecordedAutoplayGate,
createRecordedInitialSeekGate,
createRecordedOpenWatchdog,
shouldReapplyRecordedBlueprint,
} from "../core/observation/recordedRerunLifecycle";
import type {
RecordedPerceptionLayers,
@@ -72,7 +71,6 @@ export {
recordedPlaybackRangeWhenReady,
recordedPointColorKey,
rerunPresentationStatus,
shouldReapplyRecordedBlueprint,
type RecordedPlaybackBufferState,
} from "../core/observation/recordedRerunLifecycle";
export {
@@ -167,6 +165,7 @@ interface RecordedRerunIdentity {
}
const RECORDED_RRD_PATH = /^\/api\/v1\/observation-sessions\/[A-Za-z0-9][A-Za-z0-9._:-]{0,127}\/recording\.rrd$/;
const LAB_RECORDED_REPLAY_PATH = /^\/api\/v1\/laboratory\/vegetation-shadow\/lab-v1-vegetation-shadow-[a-f0-9]{64}\/canonical-replay\.rrd$/;
const RECORDED_BLUEPRINT_PATH = /^\/api\/v1\/observation-sessions\/[A-Za-z0-9][A-Za-z0-9._:-]{0,127}\/blueprint\.rrd$/;
const RECORDED_PERCEPTION_PATH = /^\/api\/v1\/observation-sessions\/[A-Za-z0-9][A-Za-z0-9._:-]{0,127}\/perception\.rrd$/;
const LAB_RECORDED_PERCEPTION_PATH = /^\/api\/v1\/laboratory\/vegetation-shadow\/lab-v1-vegetation-shadow-[a-f0-9]{64}\/canonical-overlay\.rrd$/;
@@ -231,7 +230,8 @@ export function resolveRecordedViewerSourceUrl(
const expectedViewerSourceUrl = `${descriptor.sourceUrl}?generation=${descriptor.sha256}`;
const endpoint = new URL(descriptor.viewerSourceUrl, `${base.origin}/`);
if (
!RECORDED_RRD_PATH.test(descriptor.sourceUrl) ||
!(RECORDED_RRD_PATH.test(descriptor.sourceUrl)
|| LAB_RECORDED_REPLAY_PATH.test(descriptor.sourceUrl)) ||
descriptor.viewerSourceUrl !== expectedViewerSourceUrl ||
endpoint.origin !== base.origin ||
endpoint.pathname !== descriptor.sourceUrl ||
@@ -252,10 +252,23 @@ export function rerunViewerInitialSource(
return resolvedSourceUrl;
}
export function resolveRecordedBlueprintUrl(sourceUrl: string, origin: string): string | null {
export function resolveRecordedBlueprintUrl(
sourceUrl: string,
origin: string,
explicitSourceUrl?: string,
): string | null {
const normalized = sourceUrl.trim();
if (!RECORDED_RRD_PATH.test(normalized)) return null;
const base = new URL(origin);
if (explicitSourceUrl !== undefined) {
const explicit = explicitSourceUrl.trim();
if (
!LAB_RECORDED_REPLAY_PATH.test(normalized)
|| !RECORDED_BLUEPRINT_PATH.test(explicit)
) return null;
const endpoint = new URL(explicit, `${base.origin}/`);
return endpoint.origin === base.origin ? endpoint.href : null;
}
if (!RECORDED_RRD_PATH.test(normalized)) return null;
const endpoint = new URL(
normalized.replace(/\/recording\.rrd$/, "/blueprint.rrd"),
`${base.origin}/`,
@@ -690,6 +703,7 @@ export function RerunViewport({
segmentation: false,
cuboids3d: false,
};
const recordedBlueprintSourceUrl = recordedProfile?.blueprintSourceUrl;
const recordedPerceptionSourceUrl = recordedProfile?.perceptionSourceUrl;
const recordedSemanticLayer = recordedProfile?.semanticLayer;
const recordedUnifiedPerception = recordedProfile?.unifiedPerception ?? false;
@@ -727,7 +741,11 @@ export function RerunViewport({
const [blueprintChannelRevision, setBlueprintChannelRevision] = useState(0);
const [perceptionChannelRevision, setPerceptionChannelRevision] = useState(0);
const recordedBlueprintUrl = sourceUrl
? resolveRecordedBlueprintUrl(sourceUrl, window.location.origin)
? resolveRecordedBlueprintUrl(
sourceUrl,
window.location.origin,
recordedBlueprintSourceUrl,
)
: null;
const recordedPerceptionUrl = recordedPerceptionSourceUrl
? resolveRerunSourceUrl(recordedPerceptionSourceUrl, window.location.origin)
@@ -773,7 +791,8 @@ export function RerunViewport({
return;
}
const isRecordedSource = RECORDED_RRD_PATH.test(normalizedSource);
const isRecordedSource = RECORDED_RRD_PATH.test(normalizedSource)
|| LAB_RECORDED_REPLAY_PATH.test(normalizedSource);
let resolvedSource: string;
try {
if (isRecordedSource) {
@@ -1151,23 +1170,7 @@ export function RerunViewport({
disposed ||
(isRecordedSource && event.application_id !== "nodedc_mission_core_recorded")
) return;
if (recordingOpened) {
const identity = recordedIdentityRef.current;
if (shouldReapplyRecordedBlueprint(
recordingOpened,
isRecordedSource,
recordedBlueprintUrl !== null,
identity,
event,
)) {
// A LAB sidecar is a second upstream receiver for the same
// recording store. Rerun re-opens that store only after the
// sidecar has been decoded, so reapply the canonical blueprint
// at this exact boundary instead of racing it during download.
setBlueprintChannelRevision((revision) => revision + 1);
}
return;
}
if (recordingOpened) return;
recordingOpened = true;
if (!isRecordedSource) {
// Store discovery only establishes a candidate. Admission is
@@ -242,7 +242,7 @@ export function CanonicalRecordedLabReplay<
minPrimarySize={splitView ? 24 : 0}
minSecondarySize={splitView ? 24 : 0}
resizable={splitView}
separatorLabel="Изменить размер VIDEO/CAMERA и 3D/PLAN"
separatorLabel="Изменить размер видео/камеры и 3D/плана"
/>
{mediaMode === "none" && spatialMode === "none" ? (
<div className="l3-visual-audit__state" role="status">