fix: enforce frame-based positioning across all video playback

Core Principle: Frame is the only standard

Changes:
- VideoPlayer: Add startFrame/endFrame props
- VideoPlayer: Use frame parameters in simple playback path
- SearchView: Remove frame/time conversion (stop using fps = 24)
- PeopleView: Pass frames directly instead of time
- PersonDetailView: Pass frames directly instead of time

All video streams now use startFrame/endFrame API parameters.
Time conversion only happens in VideoPlayer internals for UI display.

Ref: frame-based positioning standards research
This commit is contained in:
2026-07-24 21:17:11 +08:00
parent 2af8ffaf93
commit 1fd6312f7e
4 changed files with 15 additions and 11 deletions

View File

@@ -161,6 +161,8 @@ const props = withDefaults(defineProps<{
fileUuid: string fileUuid: string
startTime?: number startTime?: number
endTime?: number endTime?: number
startFrame?: number
endFrame?: number
allTraces?: any[] allTraces?: any[]
mergedSegments?: any[] mergedSegments?: any[]
initialTraceIdx?: number initialTraceIdx?: number
@@ -886,10 +888,15 @@ onMounted(async () => {
emit('trace-change', currentTraceIdx.value) emit('trace-change', currentTraceIdx.value)
} else { } else {
try { try {
const st = props.startTime ?? 0 const stFrame = props.startFrame ?? 0
pendingSeekTime.value = st const enFrame = props.endFrame
const data = await apiCall('get_video_stream', { const data = await apiCall('get_video_stream', {
uuid: props.fileUuid, uuid: props.fileUuid,
startTime: null,
endTime: null,
startFrame: stFrame,
endFrame: enFrame,
original: props.useOriginal, original: props.useOriginal,
}) })
if (typeof data === 'string') { if (typeof data === 'string') {

View File

@@ -1060,8 +1060,8 @@ async function playClusterVideo() {
currentVideo.value = { currentVideo.value = {
fileUuid, fileUuid,
startTime: firstTrace ? (firstTrace.start_frame / fps) : 0, startFrame: firstTrace ? firstTrace.start_frame : 0,
endTime: lastTrace ? (lastTrace.end_frame / fps) : 30, endFrame: lastTrace ? lastTrace.end_frame : 30,
title: `${getFileName(fileUuid)} - ${c.name}`, title: `${getFileName(fileUuid)} - ${c.name}`,
} }
clusterVideoTraces.value = allClusterTraces clusterVideoTraces.value = allClusterTraces
@@ -1083,8 +1083,8 @@ async function playClusterTraceVideo(trace: any) {
currentVideo.value = { currentVideo.value = {
fileUuid, fileUuid,
startTime: startFrame / fps, startFrame: startFrame,
endTime: endFrame / fps, endFrame: endFrame,
title: `${getFileName(fileUuid)} - F${trace.trace_id}`, title: `${getFileName(fileUuid)} - F${trace.trace_id}`,
} }
clusterVideoTraces.value = [{ clusterVideoTraces.value = [{

View File

@@ -443,8 +443,8 @@ function playTrace(t: any) {
const endFrame = (t.last_frame || 0) + 10 const endFrame = (t.last_frame || 0) + 10
currentVideo.value = { currentVideo.value = {
fileUuid: t.file_uuid || '', fileUuid: t.file_uuid || '',
startTime: startFrame / fps, startFrame: startFrame,
endTime: endFrame / fps, endFrame: endFrame,
traceIdx: idx, traceIdx: idx,
title: `${person.value?.name} · T${t.trace_id}`, title: `${person.value?.name} · T${t.trace_id}`,
} }

View File

@@ -805,11 +805,8 @@ function formatTime(sec: number): string {
function playVideo(r: any) { function playVideo(r: any) {
const sf = r.start_frame || 0 const sf = r.start_frame || 0
const ef = r.end_frame || sf + 1 const ef = r.end_frame || sf + 1
const fps = 24
currentVideo.value = { currentVideo.value = {
fileUuid: r.file_uuid, fileUuid: r.file_uuid,
startTime: sf / fps,
endTime: ef / fps,
startFrame: sf, startFrame: sf,
endFrame: ef, endFrame: ef,
title: r.summary?.slice(0, 60) || r.file_name || 'Video' title: r.summary?.slice(0, 60) || r.file_name || 'Video'