forked from Github/frigate
Improve graph using pandas (#9234)
* Ensure viewport is always full screen * Protect against hour with no cards and ensure data is consistent * Reduce grouped up image refreshes * Include current hour and fix scrubbing bugginess * Scroll initially selected timeline in to view * Expand timelne class type * Use poster image for preview on video player instead of using separate image view * Fix available streaming modes * Incrase timing for grouping timline items * Fix audio activity listener * Fix player not switching views correctly * Use player time to convert to timeline time * Update sub labels for previous timeline items * Show mini timeline bar for non selected items * Rewrite desktop timeline to use separate dynamic video player component * Extend improvements to mobile as well * Improve time formatting * Fix scroll * Fix no preview case * Mobile fixes * Audio toggle fixes * More fixes for mobile * Improve scaling of graph motion activity * Add keyboard shortcut hook and support shortcuts for playback page * Fix sizing of dialog * Improve height scaling of dialog * simplify and fix layout system for timeline * Fix timeilne items not working * Implement basic Frigate+ submitting from timeline
This commit is contained in:
committed by
Blake Blackshear
parent
9c4b69191b
commit
af3f6dadcb
@@ -1,6 +1,4 @@
|
||||
import { FrigateConfig } from "@/types/frigateConfig";
|
||||
import VideoPlayer from "./VideoPlayer";
|
||||
import useSWR from "swr";
|
||||
import React, {
|
||||
useCallback,
|
||||
useEffect,
|
||||
@@ -12,6 +10,7 @@ import { useApiHost } from "@/api";
|
||||
import Player from "video.js/dist/types/player";
|
||||
import { AspectRatio } from "../ui/aspect-ratio";
|
||||
import { LuPlayCircle } from "react-icons/lu";
|
||||
import { isCurrentHour } from "@/utils/dateUtil";
|
||||
|
||||
type PreviewPlayerProps = {
|
||||
camera: string;
|
||||
@@ -38,7 +37,6 @@ export default function PreviewThumbnailPlayer({
|
||||
isMobile,
|
||||
onClick,
|
||||
}: PreviewPlayerProps) {
|
||||
const { data: config } = useSWR("config");
|
||||
const playerRef = useRef<Player | null>(null);
|
||||
const isSafari = useMemo(() => {
|
||||
return /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
||||
@@ -54,7 +52,10 @@ export default function PreviewThumbnailPlayer({
|
||||
}
|
||||
|
||||
if (!playerRef.current) {
|
||||
setIsInitiallyVisible(true);
|
||||
if (isHovered) {
|
||||
setIsInitiallyVisible(true);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -105,6 +106,7 @@ export default function PreviewThumbnailPlayer({
|
||||
{
|
||||
threshold: 1.0,
|
||||
root: document.getElementById("pageRoot"),
|
||||
rootMargin: "-10% 0px -25% 0px",
|
||||
}
|
||||
);
|
||||
if (node) autoPlayObserver.current.observe(node);
|
||||
@@ -131,7 +133,6 @@ export default function PreviewThumbnailPlayer({
|
||||
isInitiallyVisible={isInitiallyVisible}
|
||||
startTs={startTs}
|
||||
camera={camera}
|
||||
config={config}
|
||||
eventId={eventId}
|
||||
isMobile={isMobile}
|
||||
isSafari={isSafari}
|
||||
@@ -143,7 +144,6 @@ export default function PreviewThumbnailPlayer({
|
||||
|
||||
type PreviewContentProps = {
|
||||
playerRef: React.MutableRefObject<Player | null>;
|
||||
config: FrigateConfig;
|
||||
camera: string;
|
||||
relevantPreview: Preview | undefined;
|
||||
eventId: string;
|
||||
@@ -156,7 +156,6 @@ type PreviewContentProps = {
|
||||
};
|
||||
function PreviewContent({
|
||||
playerRef,
|
||||
config,
|
||||
camera,
|
||||
relevantPreview,
|
||||
eventId,
|
||||
@@ -195,22 +194,13 @@ function PreviewContent({
|
||||
|
||||
if (relevantPreview && !isVisible) {
|
||||
return <div />;
|
||||
} else if (!relevantPreview) {
|
||||
if (isCurrentHour(startTs)) {
|
||||
return (
|
||||
<img
|
||||
className={`${getPreviewWidth(camera, config)}`}
|
||||
src={`${apiHost}api/preview/${camera}/${startTs}/thumbnail.jpg`}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<img
|
||||
className="w-[160px]"
|
||||
src={`${apiHost}api/events/${eventId}/thumbnail.jpg`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
} else if (!relevantPreview && !isCurrentHour(startTs)) {
|
||||
return (
|
||||
<img
|
||||
className="w-[160px]"
|
||||
src={`${apiHost}api/events/${eventId}/thumbnail.jpg`}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
@@ -223,17 +213,26 @@ function PreviewContent({
|
||||
controls: false,
|
||||
muted: true,
|
||||
loadingSpinner: false,
|
||||
sources: [
|
||||
{
|
||||
src: `${relevantPreview.src}`,
|
||||
type: "video/mp4",
|
||||
},
|
||||
],
|
||||
poster: relevantPreview
|
||||
? ""
|
||||
: `${apiHost}api/preview/${camera}/${startTs}/thumbnail.jpg`,
|
||||
sources: relevantPreview
|
||||
? [
|
||||
{
|
||||
src: `${relevantPreview.src}`,
|
||||
type: "video/mp4",
|
||||
},
|
||||
]
|
||||
: [],
|
||||
}}
|
||||
seekOptions={{}}
|
||||
onReady={(player) => {
|
||||
playerRef.current = player;
|
||||
|
||||
if (!relevantPreview) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isInitiallyVisible) {
|
||||
player.pause(); // autoplay + pause is required for iOS
|
||||
}
|
||||
@@ -249,28 +248,10 @@ function PreviewContent({
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<LuPlayCircle className="absolute z-10 left-1 bottom-1 w-4 h-4 text-white text-opacity-60" />
|
||||
{relevantPreview && (
|
||||
<LuPlayCircle className="absolute z-10 left-1 bottom-1 w-4 h-4 text-white text-opacity-60" />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function isCurrentHour(timestamp: number) {
|
||||
const now = new Date();
|
||||
now.setMinutes(0, 0, 0);
|
||||
return timestamp > now.getTime() / 1000;
|
||||
}
|
||||
|
||||
function getPreviewWidth(camera: string, config: FrigateConfig) {
|
||||
const detect = config.cameras[camera].detect;
|
||||
|
||||
if (detect.width / detect.height < 1) {
|
||||
return "w-1/2";
|
||||
}
|
||||
|
||||
if (detect.width / detect.height < 16 / 9) {
|
||||
return "w-2/3";
|
||||
}
|
||||
|
||||
return "w-full";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user