mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-02 09:15:22 +03:00
refactor: used hls system instead
This commit is contained in:
parent
46020693cd
commit
7401789b3b
@ -14,13 +14,14 @@ const getLast24Hours = () => {
|
||||
};
|
||||
|
||||
export default function HistoryViewer({ camera }) {
|
||||
console.log('history', { camera });
|
||||
const apiHost = useApiHost();
|
||||
const videoRef = useRef();
|
||||
const { searchString } = useSearchString(200, `camera=${camera}&after=${getLast24Hours()}`);
|
||||
const { searchString } = useSearchString(500, `camera=${camera}&after=${getLast24Hours()}`);
|
||||
const { data: events } = useEvents(searchString);
|
||||
const [timelineEvents, setTimelineEvents] = useState();
|
||||
|
||||
const [hasPlayed, setHasPlayed] = useState();
|
||||
const [isPlaying, setIsPlaying] = useState(false);
|
||||
const [currentEvent, setCurrentEvent] = useState();
|
||||
const [currentEventIndex, setCurrentEventIndex] = useState();
|
||||
const [timelineOffset, setTimelineOffset] = useState(0);
|
||||
@ -28,11 +29,18 @@ export default function HistoryViewer({ camera }) {
|
||||
|
||||
useEffect(() => {
|
||||
if (events) {
|
||||
setTimelineEvents([...events].reverse().filter((e) => e.end_time !== undefined));
|
||||
const filteredEvents = [...events].reverse().filter((e) => e.end_time !== undefined);
|
||||
setTimelineEvents(filteredEvents);
|
||||
setCurrentEventIndex(filteredEvents.length - 1);
|
||||
}
|
||||
}, [events]);
|
||||
|
||||
const handleTimeUpdate = () => {
|
||||
const videoContainer = videoRef.current;
|
||||
if (videoContainer.paused) {
|
||||
return;
|
||||
}
|
||||
|
||||
const timestamp = Math.round(videoRef.current.currentTime);
|
||||
const offset = Math.round(timestamp);
|
||||
const triggerStateChange = offset !== timelineOffset;
|
||||
@ -41,18 +49,50 @@ export default function HistoryViewer({ camera }) {
|
||||
}
|
||||
};
|
||||
|
||||
const handleTimelineChange = (event) => {
|
||||
if (event !== undefined) {
|
||||
setCurrentEvent(event);
|
||||
const handleTimelineChange = (timelineChangedEvent) => {
|
||||
if (timelineChangedEvent.seekComplete) {
|
||||
const currentEventExists = currentEvent !== undefined;
|
||||
if (!currentEventExists || currentEvent.id !== timelineChangedEvent.event.id) {
|
||||
setCurrentEvent(timelineChangedEvent.event);
|
||||
}
|
||||
}
|
||||
|
||||
const videoContainer = videoRef.current;
|
||||
if (videoContainer) {
|
||||
if (!videoContainer.paused) {
|
||||
videoContainer.pause();
|
||||
setHasPlayed(true);
|
||||
}
|
||||
|
||||
const videoHasPermissionToPlay = hasPlayed !== undefined;
|
||||
if (videoHasPermissionToPlay && timelineChangedEvent.seekComplete) {
|
||||
const markerTime = Math.abs(timelineChangedEvent.time - timelineChangedEvent.event.startTime) / 1000;
|
||||
videoContainer.currentTime = markerTime;
|
||||
if (hasPlayed) {
|
||||
videoContainer.play();
|
||||
setHasPlayed(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handlePlay = function () {
|
||||
videoRef.current.play();
|
||||
const videoContainer = videoRef.current;
|
||||
if (videoContainer) {
|
||||
if (videoContainer.paused) {
|
||||
videoContainer.play();
|
||||
} else {
|
||||
videoContainer.pause();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handlePlayed = () => {
|
||||
setIsPlaying(true);
|
||||
};
|
||||
|
||||
const handlePaused = () => {
|
||||
setTimelineOffset(undefined);
|
||||
setIsPlaying(false);
|
||||
};
|
||||
|
||||
const handlePrevious = function () {
|
||||
@ -64,17 +104,20 @@ export default function HistoryViewer({ camera }) {
|
||||
};
|
||||
|
||||
const handleMetadataLoad = () => {
|
||||
if (videoRef.current) {
|
||||
setMinHeight(videoRef.current.clientHeight);
|
||||
const videoContainer = videoRef.current;
|
||||
if (videoContainer) {
|
||||
setMinHeight(videoContainer.clientHeight);
|
||||
}
|
||||
};
|
||||
|
||||
const RenderVideo = useCallback(() => {
|
||||
if (currentEvent) {
|
||||
return (
|
||||
<video
|
||||
ref={videoRef}
|
||||
onTimeUpdate={handleTimeUpdate}
|
||||
onPause={handlePaused}
|
||||
onPlay={handlePlayed}
|
||||
poster={`${apiHost}/api/events/${currentEvent.id}/snapshot.jpg`}
|
||||
onLoadedMetadata={handleMetadataLoad}
|
||||
preload='metadata'
|
||||
@ -86,13 +129,11 @@ export default function HistoryViewer({ camera }) {
|
||||
: {}
|
||||
}
|
||||
playsInline
|
||||
controls
|
||||
>
|
||||
<source
|
||||
src={`${apiHost}/api/${camera}/start/${currentEvent.start_time}/end/${currentEvent.end_time}/clip.mp4`}
|
||||
/>
|
||||
<source type='application/vnd.apple.mpegurl' src={`${apiHost}/vod/event/${currentEvent.id}/index.m3u8`} />
|
||||
</video>
|
||||
);
|
||||
}
|
||||
}, [currentEvent, apiHost, camera, videoRef]);
|
||||
|
||||
return (
|
||||
@ -115,6 +156,7 @@ export default function HistoryViewer({ camera }) {
|
||||
events={timelineEvents}
|
||||
offset={timelineOffset}
|
||||
currentIndex={currentEventIndex}
|
||||
disabled={isPlaying}
|
||||
onChange={handleTimelineChange}
|
||||
/>
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ import { h } from 'preact';
|
||||
import { useCallback, useEffect, useRef, useState } from 'preact/hooks';
|
||||
import { longToDate } from '../utils/dateUtil';
|
||||
|
||||
export default function Timeline({ events, offset, currentIndex, onChange }) {
|
||||
export default function Timeline({ events, offset, currentIndex, disabled, onChange }) {
|
||||
const timelineContainerRef = useRef(undefined);
|
||||
|
||||
const [timeline, setTimeline] = useState([]);
|
||||
@ -11,6 +11,7 @@ export default function Timeline({ events, offset, currentIndex, onChange }) {
|
||||
const [currentEvent, setCurrentEvent] = useState();
|
||||
const [scrollTimeout, setScrollTimeout] = useState();
|
||||
const [scrollActive, setScrollActive] = useState(true);
|
||||
const [eventsEnabled, setEventsEnable] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
if (events && events.length > 0 && timelineOffset) {
|
||||
@ -40,8 +41,8 @@ export default function Timeline({ events, offset, currentIndex, onChange }) {
|
||||
...firstTimelineEvent,
|
||||
id: firstTimelineEvent.id,
|
||||
index: 0,
|
||||
startTime: firstTimelineEvent.start_time,
|
||||
endTime: firstTimelineEvent.end_time,
|
||||
startTime: longToDate(firstTimelineEvent.start_time),
|
||||
endTime: longToDate(firstTimelineEvent.end_time),
|
||||
});
|
||||
setTimeline(timelineEvents);
|
||||
}
|
||||
@ -54,25 +55,22 @@ export default function Timeline({ events, offset, currentIndex, onChange }) {
|
||||
useEffect(() => {
|
||||
const cEvent = getCurrentEvent();
|
||||
if (cEvent && offset >= 0) {
|
||||
setScrollActive(false);
|
||||
timelineContainerRef.current.scroll({
|
||||
left: cEvent.positionX + offset - timelineOffset,
|
||||
behavior: 'smooth',
|
||||
});
|
||||
} else {
|
||||
setScrollActive(true);
|
||||
}
|
||||
}, [offset, timelineContainerRef]);
|
||||
|
||||
useEffect(() => {
|
||||
if (currentIndex !== undefined) {
|
||||
if (timeline.length > 0 && currentIndex !== undefined) {
|
||||
const event = timeline[currentIndex];
|
||||
setCurrentEvent({
|
||||
...event,
|
||||
id: event.id,
|
||||
index: currentIndex,
|
||||
startTime: event.start_time,
|
||||
endTime: event.end_time,
|
||||
startTime: longToDate(event.start_time),
|
||||
endTime: longToDate(event.end_time),
|
||||
});
|
||||
timelineContainerRef.current.scroll({ left: event.positionX - timelineOffset, behavior: 'smooth' });
|
||||
}
|
||||
@ -88,32 +86,53 @@ export default function Timeline({ events, offset, currentIndex, onChange }) {
|
||||
const foundIndex = timeline.findIndex((event) => event.startTime <= markerTime && markerTime <= event.endTime);
|
||||
if (foundIndex > -1) {
|
||||
const found = timeline[foundIndex];
|
||||
if (found !== currentEvent && found.id !== currentEvent.id) {
|
||||
setCurrentEvent({
|
||||
...found,
|
||||
id: found.id,
|
||||
index: foundIndex,
|
||||
startTime: found.start_time,
|
||||
endTime: found.end_time,
|
||||
startTime: longToDate(found.start_time),
|
||||
endTime: longToDate(found.end_time),
|
||||
});
|
||||
return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleScroll = (event) => {
|
||||
const scrollLogic = () => {
|
||||
clearTimeout(scrollTimeout);
|
||||
|
||||
const scrollPosition = event.target.scrollLeft;
|
||||
const scrollPosition = timelineContainerRef.current.scrollLeft;
|
||||
const startTime = longToDate(timeline[0].start_time);
|
||||
const markerTime = new Date(startTime.getTime() + scrollPosition * 1000);
|
||||
setMarkerTime(markerTime);
|
||||
|
||||
handleChange(currentEvent, markerTime, false);
|
||||
|
||||
setScrollTimeout(
|
||||
setTimeout(() => {
|
||||
checkMarkerForEvent(markerTime);
|
||||
const foundEvent = checkMarkerForEvent(markerTime);
|
||||
handleChange(foundEvent ? foundEvent : currentEvent, markerTime, true);
|
||||
}, 250)
|
||||
);
|
||||
};
|
||||
|
||||
const handleWheel = (event) => {
|
||||
if (!disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
scrollLogic(event);
|
||||
};
|
||||
|
||||
const handleScroll = (event) => {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
scrollLogic(event);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (timelineContainerRef) {
|
||||
const timelineContainerWidth = timelineContainerRef.current.offsetWidth;
|
||||
@ -122,9 +141,18 @@ export default function Timeline({ events, offset, currentIndex, onChange }) {
|
||||
}
|
||||
}, [timelineContainerRef]);
|
||||
|
||||
useEffect(() => {
|
||||
onChange && onChange(currentEvent);
|
||||
}, [onChange, currentEvent]);
|
||||
const handleChange = useCallback(
|
||||
(event, time, seekComplete) => {
|
||||
if (onChange !== undefined) {
|
||||
onChange({
|
||||
event,
|
||||
time,
|
||||
seekComplete,
|
||||
});
|
||||
}
|
||||
},
|
||||
[onChange]
|
||||
);
|
||||
|
||||
const RenderTimeline = useCallback(() => {
|
||||
if (timeline && timeline.length > 0) {
|
||||
@ -175,7 +203,13 @@ export default function Timeline({ events, offset, currentIndex, onChange }) {
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<div ref={timelineContainerRef} className='overflow-x-auto hide-scroll' onScroll={handleScroll}>
|
||||
<div
|
||||
ref={timelineContainerRef}
|
||||
onWheel={handleWheel}
|
||||
onTouchMove={handleWheel}
|
||||
onScroll={handleScroll}
|
||||
className='overflow-x-auto hide-scroll'
|
||||
>
|
||||
<RenderTimeline />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user