// assets/js/video_segment.js const DEFAULT_FPS = 30; /** * Playback rules by label (case-insensitive): * * time : video start -> endFrame * success : entire video * failure : entire video * safety conflict : startFrame -> endFrame * safety avoidance : startFrame -> end of video * passive wait : startFrame -> endFrame * redundant retrieval : startFrame -> end of video * task model uncertainty : startFrame -> end of video * capability miscalibration : startFrame -> endFrame * missed grab : (startFrame-10) -> (endFrame+50 or end of video) * slippage : (startFrame-10) -> (endFrame+50 or end of video) * * Manual control requirements: * 1) If user pauses, DO NOT auto-resume. * 2) If user seeks/scrubs anywhere, allow it. When playback reaches the window end, * it loops back to the window start (and continues only if user is playing). */ function normalizeLabel(label) { return String(label || "").trim().toLowerCase(); } function computeWindowFrames(label, startFrame, endFrame) { const lab = normalizeLabel(label); let s = Number(startFrame); let e = Number(endFrame); if (!Number.isFinite(s)) s = 0; if (!Number.isFinite(e)) e = 0; if (s > e) [s, e] = [e, s]; let start = s; let end = e; let endIsVideo = false; const isTime = lab === "time"; const isSuccess = lab === "success"; const isFailure = lab === "failure"; const isSafetyConflict = lab === "safety conflict"; const isSafetyAvoidance = lab === "safety avoidance"; const isPassiveWait = lab === "passive wait"; const isRedundantRetrieval = lab === "redundant retrieval"; const isTMU = lab === "task model uncertainty"; const isCapMis = lab === "capability miscalibration"; const isMissedGrab = lab === "missed grab"; const isSlippage = lab === "slippage"; if (isTime) { start = 0; end = e; endIsVideo = false; } else if (isSuccess || isFailure) { start = 0; endIsVideo = true; } else if (isSafetyConflict || isPassiveWait || isCapMis) { start = s; end = e; endIsVideo = false; } else if (isSafetyAvoidance || isRedundantRetrieval || isTMU) { start = s; endIsVideo = true; } else if (isMissedGrab || isSlippage) { start = Math.max(0, s - 10); end = e + 50; // will clamp to duration endIsVideo = false; } else { // Unknown label: just use annotated segment start = s; end = e; endIsVideo = false; } start = Math.max(0, Math.floor(start)); if (!endIsVideo) end = Math.max(start, Math.floor(end)); return { startFrameAdj: start, endFrameAdj: end, endIsVideo }; } /** * Attaches labeled looping to a