Files
mediaproc/ui/detection-app/src/composables/useCheckpointLoader.ts
2026-03-30 09:53:10 -03:00

100 lines
2.8 KiB
TypeScript

import { ref, computed, watch } from 'vue'
import type { Ref } from 'vue'
import type { DataSource } from 'mpr-ui-framework'
import { usePipelineStore } from '../stores/pipeline'
import { useStageRegistry } from './useStageRegistry'
interface CheckpointFrame {
seq: number
timestamp: number
jpeg_b64: string
}
export function useCheckpointLoader(
jobId: Ref<string>,
source: DataSource,
) {
const pipeline = usePipelineStore()
const currentFrameImage = ref<string | null>(null)
const currentFrameRef = ref<number | null>(null)
const checkpointFrames = ref<CheckpointFrame[]>([])
const checkpointFrameIndex = ref(0)
const checkpointStage = ref<string | null>(null)
const stripSelStart = ref(0)
const stripSelEndOverride = ref<number | null>(null)
const stripSelEnd = computed(() =>
stripSelEndOverride.value ?? Math.max(0, checkpointFrames.value.length - 1),
)
// Track current frame from SSE
source.on<{ frame_ref: number; jpeg_b64: string }>('frame_update', (e) => {
currentFrameImage.value = e.jpeg_b64
currentFrameRef.value = e.frame_ref
})
async function loadCheckpoint(job: string, stage: string) {
try {
const lookupId = pipeline.timelineId || job
const resp = await fetch(`/api/detect/checkpoints/${lookupId}/${stage}`)
if (!resp.ok) return
const data = await resp.json()
checkpointFrames.value = data.frames ?? []
checkpointStage.value = stage
if (checkpointFrames.value.length > 0) {
checkpointFrameIndex.value = 0
const first = checkpointFrames.value[0]
currentFrameImage.value = first.jpeg_b64
currentFrameRef.value = first.seq
}
stripSelStart.value = 0
stripSelEndOverride.value = null
} catch (e) {
console.error('Failed to load checkpoint:', e)
}
}
function setCheckpointFrame(index: number) {
if (index < 0 || index >= checkpointFrames.value.length) return
checkpointFrameIndex.value = index
const frame = checkpointFrames.value[index]
currentFrameImage.value = frame.jpeg_b64
currentFrameRef.value = frame.seq
}
const { stages, checkpointStageFor } = useStageRegistry()
// Auto-load checkpoint when entering editor mode.
watch(
() => [pipeline.layoutMode, pipeline.editorStage, jobId.value, stages.value.length] as const,
([mode, stage, job]) => {
if (mode === 'bbox_editor' && stage && job) {
const cpStage = checkpointStageFor(stage)
if (cpStage) {
loadCheckpoint(job, cpStage)
}
}
},
{ immediate: true },
)
return {
currentFrameImage,
currentFrameRef,
checkpointFrames,
checkpointFrameIndex,
checkpointStage,
stripSelStart,
stripSelEnd,
stripSelEndOverride,
loadCheckpoint,
setCheckpointFrame,
}
}