mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-07 11:45:24 +03:00
Optimizations and improvements
This commit is contained in:
parent
d1b1c67062
commit
88820613ce
@ -76,7 +76,7 @@ const domEvents: TimelineEventsWithMissing[] = [
|
|||||||
type ActivityScrubberProps = {
|
type ActivityScrubberProps = {
|
||||||
className?: string;
|
className?: string;
|
||||||
items?: TimelineItem[];
|
items?: TimelineItem[];
|
||||||
timeBars?: { time: DateType; id?: IdType | undefined }[];
|
timeBars?: { time: DateType; id: IdType }[];
|
||||||
groups?: TimelineGroup[];
|
groups?: TimelineGroup[];
|
||||||
options?: TimelineOptions;
|
options?: TimelineOptions;
|
||||||
} & TimelineEventsHandlers;
|
} & TimelineEventsHandlers;
|
||||||
@ -94,6 +94,9 @@ function ActivityScrubber({
|
|||||||
timeline: null,
|
timeline: null,
|
||||||
});
|
});
|
||||||
const [currentTime, setCurrentTime] = useState(Date.now());
|
const [currentTime, setCurrentTime] = useState(Date.now());
|
||||||
|
const [customTimes, setCustomTimes] = useState<
|
||||||
|
{ id: IdType; time: DateType }[]
|
||||||
|
>([]);
|
||||||
|
|
||||||
const defaultOptions: TimelineOptions = {
|
const defaultOptions: TimelineOptions = {
|
||||||
width: "100%",
|
width: "100%",
|
||||||
@ -161,6 +164,41 @@ function ActivityScrubber({
|
|||||||
};
|
};
|
||||||
}, [containerRef]);
|
}, [containerRef]);
|
||||||
|
|
||||||
|
// need to keep custom times in sync
|
||||||
|
useEffect(() => {
|
||||||
|
if (!timelineRef.current.timeline || timeBars == undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCustomTimes((prevTimes) => {
|
||||||
|
if (prevTimes.length == 0 && timeBars.length == 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
prevTimes
|
||||||
|
.filter((x) => timeBars.find((y) => x.id == y.id) == undefined)
|
||||||
|
.forEach((time) => {
|
||||||
|
try {
|
||||||
|
timelineRef.current.timeline?.removeCustomTime(time.id);
|
||||||
|
} catch {}
|
||||||
|
});
|
||||||
|
|
||||||
|
timeBars.forEach((time) => {
|
||||||
|
try {
|
||||||
|
const existing = timelineRef.current.timeline?.getCustomTime(time.id);
|
||||||
|
|
||||||
|
if (existing != time.time) {
|
||||||
|
timelineRef.current.timeline?.setCustomTime(time.time, time.id);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
timelineRef.current.timeline?.addCustomTime(time.time, time.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return timeBars;
|
||||||
|
});
|
||||||
|
}, [timeBars, timelineRef]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={className || ""}>
|
<div className={className || ""}>
|
||||||
<div ref={containerRef} />
|
<div ref={containerRef} />
|
||||||
|
|||||||
@ -111,10 +111,20 @@ export function getTimelineHoursForDay(
|
|||||||
const now = new Date();
|
const now = new Date();
|
||||||
const data: TimelinePlayback[] = [];
|
const data: TimelinePlayback[] = [];
|
||||||
const startDay = new Date(timestamp * 1000);
|
const startDay = new Date(timestamp * 1000);
|
||||||
|
startDay.setHours(23, 59, 59, 999);
|
||||||
|
const dayEnd = startDay.getTime() / 1000;
|
||||||
startDay.setHours(0, 0, 0, 0);
|
startDay.setHours(0, 0, 0, 0);
|
||||||
let start = startDay.getTime() / 1000;
|
let start = startDay.getTime() / 1000;
|
||||||
let end = 0;
|
let end = 0;
|
||||||
|
|
||||||
|
const relevantPreviews = allPreviews.filter((preview) => {
|
||||||
|
return (
|
||||||
|
preview.camera == camera &&
|
||||||
|
preview.start >= start &&
|
||||||
|
Math.floor(preview.end - 1) <= dayEnd
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
const dayIdx = Object.keys(cards).find((day) => {
|
const dayIdx = Object.keys(cards).find((day) => {
|
||||||
if (parseInt(day) > start) {
|
if (parseInt(day) > start) {
|
||||||
return false;
|
return false;
|
||||||
@ -156,12 +166,9 @@ export function getTimelineHoursForDay(
|
|||||||
return [];
|
return [];
|
||||||
})
|
})
|
||||||
: [];
|
: [];
|
||||||
const previewCheck = start + 30; // preview can start after the hour
|
const relevantPreview = relevantPreviews.find(
|
||||||
const relevantPreview = Object.values(allPreviews || []).find(
|
|
||||||
(preview) =>
|
(preview) =>
|
||||||
preview.camera == camera &&
|
Math.round(preview.start) >= start && Math.floor(preview.end) <= end
|
||||||
preview.start < previewCheck &&
|
|
||||||
preview.end > previewCheck
|
|
||||||
);
|
);
|
||||||
data.push({
|
data.push({
|
||||||
camera,
|
camera,
|
||||||
|
|||||||
@ -42,6 +42,11 @@ export default function DesktopTimelineView({
|
|||||||
|
|
||||||
const [seeking, setSeeking] = useState(false);
|
const [seeking, setSeeking] = useState(false);
|
||||||
const [timeToSeek, setTimeToSeek] = useState<number | undefined>(undefined);
|
const [timeToSeek, setTimeToSeek] = useState<number | undefined>(undefined);
|
||||||
|
const [timelineTime, setTimelineTime] = useState(
|
||||||
|
initialPlayback.timelineItems.length > 0
|
||||||
|
? initialPlayback.timelineItems[0].timestamp
|
||||||
|
: initialPlayback.range.start
|
||||||
|
);
|
||||||
|
|
||||||
const annotationOffset = useMemo(() => {
|
const annotationOffset = useMemo(() => {
|
||||||
if (!config) {
|
if (!config) {
|
||||||
@ -54,14 +59,6 @@ export default function DesktopTimelineView({
|
|||||||
);
|
);
|
||||||
}, [config]);
|
}, [config]);
|
||||||
|
|
||||||
const timelineTime = useMemo(() => {
|
|
||||||
if (!selectedPlayback || selectedPlayback.timelineItems.length == 0) {
|
|
||||||
return selectedPlayback.range.start;
|
|
||||||
}
|
|
||||||
|
|
||||||
return selectedPlayback.timelineItems.at(0)!!.timestamp;
|
|
||||||
}, [selectedPlayback]);
|
|
||||||
|
|
||||||
const recordingParams = useMemo(() => {
|
const recordingParams = useMemo(() => {
|
||||||
return {
|
return {
|
||||||
before: selectedPlayback.range.end,
|
before: selectedPlayback.range.end,
|
||||||
@ -121,36 +118,6 @@ export default function DesktopTimelineView({
|
|||||||
[annotationOffset, recordings, playerRef]
|
[annotationOffset, recordings, playerRef]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onScrubTime = useCallback(
|
|
||||||
(data: { time: Date }) => {
|
|
||||||
if (!selectedPlayback.relevantPreview) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (playerRef.current?.paused() == false) {
|
|
||||||
setScrubbing(true);
|
|
||||||
playerRef.current?.pause();
|
|
||||||
}
|
|
||||||
|
|
||||||
const seekTimestamp = data.time.getTime() / 1000;
|
|
||||||
const seekTime = seekTimestamp - selectedPlayback.relevantPreview.start;
|
|
||||||
setTimeToSeek(Math.round(seekTime));
|
|
||||||
},
|
|
||||||
[scrubbing, playerRef, selectedPlayback]
|
|
||||||
);
|
|
||||||
|
|
||||||
const onStopScrubbing = useCallback(
|
|
||||||
(data: { time: Date }) => {
|
|
||||||
const playbackTime = data.time.getTime() / 1000;
|
|
||||||
playerRef.current?.currentTime(
|
|
||||||
playbackTime - selectedPlayback.range.start
|
|
||||||
);
|
|
||||||
setScrubbing(false);
|
|
||||||
playerRef.current?.play();
|
|
||||||
},
|
|
||||||
[selectedPlayback, playerRef]
|
|
||||||
);
|
|
||||||
|
|
||||||
// handle seeking to next frame when seek is finished
|
// handle seeking to next frame when seek is finished
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (seeking) {
|
if (seeking) {
|
||||||
@ -165,15 +132,33 @@ export default function DesktopTimelineView({
|
|||||||
|
|
||||||
// handle loading main playback when selected hour changes
|
// handle loading main playback when selected hour changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!playerRef.current) {
|
if (!playerRef.current || !previewRef.current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setTimelineTime(
|
||||||
|
selectedPlayback.timelineItems.length > 0
|
||||||
|
? selectedPlayback.timelineItems[0].timestamp
|
||||||
|
: selectedPlayback.range.start
|
||||||
|
);
|
||||||
|
|
||||||
playerRef.current.src({
|
playerRef.current.src({
|
||||||
src: playbackUri,
|
src: playbackUri,
|
||||||
type: "application/vnd.apple.mpegurl",
|
type: "application/vnd.apple.mpegurl",
|
||||||
});
|
});
|
||||||
}, [playerRef, selectedPlayback]);
|
|
||||||
|
if (selectedPlayback.relevantPreview) {
|
||||||
|
console.log(
|
||||||
|
`found relevant preview with start ${new Date(
|
||||||
|
selectedPlayback.relevantPreview.start * 1000
|
||||||
|
)} for ${new Date(selectedPlayback.range.start * 1000)}`
|
||||||
|
);
|
||||||
|
previewRef.current.src({
|
||||||
|
src: selectedPlayback.relevantPreview.src,
|
||||||
|
type: selectedPlayback.relevantPreview.type,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [playerRef, previewRef, selectedPlayback]);
|
||||||
|
|
||||||
const timelineStack = useMemo(
|
const timelineStack = useMemo(
|
||||||
() =>
|
() =>
|
||||||
@ -181,7 +166,7 @@ export default function DesktopTimelineView({
|
|||||||
selectedPlayback.camera,
|
selectedPlayback.camera,
|
||||||
timelineData,
|
timelineData,
|
||||||
allPreviews,
|
allPreviews,
|
||||||
timelineTime
|
selectedPlayback.range.start + 60
|
||||||
),
|
),
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
@ -216,9 +201,15 @@ export default function DesktopTimelineView({
|
|||||||
seekOptions={{ forward: 10, backward: 5 }}
|
seekOptions={{ forward: 10, backward: 5 }}
|
||||||
onReady={(player) => {
|
onReady={(player) => {
|
||||||
playerRef.current = player;
|
playerRef.current = player;
|
||||||
player.currentTime(
|
|
||||||
timelineTime - selectedPlayback.range.start
|
if (selectedPlayback.timelineItems.length > 0) {
|
||||||
);
|
player.currentTime(
|
||||||
|
selectedPlayback.timelineItems[0].timestamp -
|
||||||
|
selectedPlayback.range.start
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
player.currentTime(0);
|
||||||
|
}
|
||||||
player.on("playing", () => {
|
player.on("playing", () => {
|
||||||
onSelectItem(undefined);
|
onSelectItem(undefined);
|
||||||
});
|
});
|
||||||
@ -285,13 +276,18 @@ export default function DesktopTimelineView({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={timeline.range.start}
|
key={timeline.range.start}
|
||||||
className={`${isSelected ? "border border-primary" : ""}`}
|
className={`p-1 ${isSelected ? "border border-primary" : ""}`}
|
||||||
>
|
>
|
||||||
<ActivityScrubber
|
<ActivityScrubber
|
||||||
items={[]}
|
items={[]}
|
||||||
timeBars={
|
timeBars={
|
||||||
isSelected && selectedPlayback.relevantPreview
|
isSelected && selectedPlayback.relevantPreview
|
||||||
? [{ time: new Date(timelineTime * 1000), id: "playback" }]
|
? [
|
||||||
|
{
|
||||||
|
time: new Date(timelineTime * 1000),
|
||||||
|
id: "playback",
|
||||||
|
},
|
||||||
|
]
|
||||||
: []
|
: []
|
||||||
}
|
}
|
||||||
options={{
|
options={{
|
||||||
@ -300,8 +296,30 @@ export default function DesktopTimelineView({
|
|||||||
max: new Date(timeline.range.end * 1000),
|
max: new Date(timeline.range.end * 1000),
|
||||||
zoomable: false,
|
zoomable: false,
|
||||||
}}
|
}}
|
||||||
timechangeHandler={onScrubTime}
|
timechangeHandler={(data) => {
|
||||||
timechangedHandler={onStopScrubbing}
|
if (!timeline.relevantPreview) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (playerRef.current?.paused() == false) {
|
||||||
|
setScrubbing(true);
|
||||||
|
playerRef.current?.pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
const seekTimestamp = data.time.getTime() / 1000;
|
||||||
|
const seekTime =
|
||||||
|
seekTimestamp - timeline.relevantPreview.start;
|
||||||
|
setTimelineTime(seekTimestamp);
|
||||||
|
setTimeToSeek(Math.round(seekTime));
|
||||||
|
}}
|
||||||
|
timechangedHandler={(data) => {
|
||||||
|
const playbackTime = data.time.getTime() / 1000;
|
||||||
|
playerRef.current?.currentTime(
|
||||||
|
playbackTime - timeline.range.start
|
||||||
|
);
|
||||||
|
setScrubbing(false);
|
||||||
|
playerRef.current?.play();
|
||||||
|
}}
|
||||||
doubleClickHandler={() => {
|
doubleClickHandler={() => {
|
||||||
setSelectedPlayback(timeline);
|
setSelectedPlayback(timeline);
|
||||||
}}
|
}}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user