diff --git a/migrations/036_add_perf_indexes.py b/migrations/039_add_perf_indexes.py similarity index 65% rename from migrations/036_add_perf_indexes.py rename to migrations/039_add_perf_indexes.py index 9be193d6b4..ffabf25e6e 100644 --- a/migrations/036_add_perf_indexes.py +++ b/migrations/039_add_perf_indexes.py @@ -1,6 +1,6 @@ -"""Peewee migrations -- 036_add_perf_indexes.py. +"""Peewee migrations -- 039_add_perf_indexes.py. -Adds composite/single-column indexes to speed up single-camera queries +Adds a composite (camera, start_time) index to speed up single-camera queries issued by the web UI. """ @@ -13,7 +13,7 @@ SQL = pw.SQL def migrate(migrator, database, fake=False, **kwargs): migrator.sql( 'CREATE INDEX IF NOT EXISTS "event_camera_start_time" ' - 'ON "event" ("camera", "start_time" DESC)' + 'ON "event" ("camera", "start_time")' ) diff --git a/web/src/components/overlay/ExportDialog.tsx b/web/src/components/overlay/ExportDialog.tsx index a067537a37..7767d63a47 100644 --- a/web/src/components/overlay/ExportDialog.tsx +++ b/web/src/components/overlay/ExportDialog.tsx @@ -80,7 +80,7 @@ type ExportOption = (typeof EXPORT_OPTIONS)[number]; export type ExportTab = "export" | "multi"; // length of a range seeded around the current playback time -const MULTI_CAMERA_RANGE_SECONDS = 3600; +const MULTI_CAMERA_RANGE_SECONDS = 1800; const TIMELINE_SELECTION_SECONDS = 60; type ExportDialogProps = { diff --git a/web/src/views/recording/RecordingView.tsx b/web/src/views/recording/RecordingView.tsx index b575a655d8..30d7cfb16d 100644 --- a/web/src/views/recording/RecordingView.tsx +++ b/web/src/views/recording/RecordingView.tsx @@ -231,6 +231,14 @@ export function RecordingView({ const [debugReplayMode, setDebugReplayMode] = useState("none"); const [debugReplayRange, setDebugReplayRange] = useState(); + + // while the range handles are up the player has to stay put: the + // timeline auto-scrolls to follow the playhead once the interaction + // timeout lapses, yanking the view out from under the drag + const selectingRange = + exportMode == "timeline" || + exportMode == "timeline_multi" || + debugReplayMode == "timeline"; const [shareTimestampOpen, setShareTimestampOpen] = useState(false); const [shareTimestampAtOpen, setShareTimestampAtOpen] = useState( Math.floor(startTime), @@ -338,6 +346,19 @@ export function RecordingView({ updateSelectedSegment, ]); + const wasPlayingBeforeSelectRef = useRef(false); + + useEffect(() => { + if (selectingRange) { + wasPlayingBeforeSelectRef.current = + mainControllerRef.current?.isPlaying() ?? false; + mainControllerRef.current?.pause(); + } else if (wasPlayingBeforeSelectRef.current) { + wasPlayingBeforeSelectRef.current = false; + mainControllerRef.current?.play(); + } + }, [selectingRange]); + const manuallySetCurrentTime = useCallback( (time: number, play: boolean = false) => { if (!currentTimeRange) { @@ -396,7 +417,7 @@ export function RecordingView({ }, [navigate, recording?.navigationSource]); useEffect(() => { - if (!scrubbing) { + if (!scrubbing && !selectingRange) { if (Math.abs(currentTime - playerTime) > 10) { if ( currentTimeRange.after <= currentTime && @@ -423,9 +444,10 @@ export function RecordingView({ mainControllerRef.current?.play(); } } - // we only want to seek when current time doesn't match the player update time + // we only want to seek when current time doesn't match the player update + // time, and once more on the way out of a range selection // eslint-disable-next-line react-hooks/exhaustive-deps - }, [currentTime, scrubbing]); + }, [currentTime, scrubbing, selectingRange]); const [fullResolution, setFullResolution] = useState({ width: 0, @@ -737,13 +759,7 @@ export function RecordingView({ latestTime={timeRange.before} mode={debugReplayMode} range={debugReplayRange} - setRange={(range: TimeRange | undefined) => { - setDebugReplayRange(range); - - if (range != undefined) { - mainControllerRef.current?.pause(); - } - }} + setRange={setDebugReplayRange} setMode={setDebugReplayMode} /> )} @@ -756,13 +772,7 @@ export function RecordingView({ mode={exportMode} range={exportRange} showPreview={showExportPreview} - setRange={(range) => { - setExportRange(range); - - if (range != undefined) { - mainControllerRef.current?.pause(); - } - }} + setRange={setExportRange} setMode={setExportMode} setShowPreview={setShowExportPreview} /> @@ -925,13 +935,7 @@ export function RecordingView({ debugReplayMode={debugReplayMode} debugReplayRange={debugReplayRange} setDebugReplayMode={setDebugReplayMode} - setDebugReplayRange={(range: TimeRange | undefined) => { - setDebugReplayRange(range); - - if (range != undefined) { - mainControllerRef.current?.pause(); - } - }} + setDebugReplayRange={setDebugReplayRange} onShareTimestamp={onShareReviewLink} onMotionSearch={ onMotionSearch ? () => onMotionSearch(mainCamera) : undefined