mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-10 05:05:26 +03:00
add birdseye
This commit is contained in:
parent
921cb23f82
commit
aa4811f948
@ -298,24 +298,26 @@ function NewGroupDialog({ open, setOpen, currentGroups }: NewGroupDialogProps) {
|
|||||||
</div>
|
</div>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent>
|
<DropdownMenuContent>
|
||||||
{Object.keys(config?.cameras ?? {}).map((camera) => (
|
{[...Object.keys(config?.cameras ?? {}), "birdseye"].map(
|
||||||
<FilterCheckBox
|
(camera) => (
|
||||||
key={camera}
|
<FilterCheckBox
|
||||||
isChecked={cameras.includes(camera)}
|
key={camera}
|
||||||
label={camera.replaceAll("_", " ")}
|
isChecked={cameras.includes(camera)}
|
||||||
onCheckedChange={(checked) => {
|
label={camera.replaceAll("_", " ")}
|
||||||
if (checked) {
|
onCheckedChange={(checked) => {
|
||||||
setCameras([...cameras, camera]);
|
if (checked) {
|
||||||
} else {
|
setCameras([...cameras, camera]);
|
||||||
const index = cameras.indexOf(camera);
|
} else {
|
||||||
setCameras([
|
const index = cameras.indexOf(camera);
|
||||||
...cameras.slice(0, index),
|
setCameras([
|
||||||
...cameras.slice(index + 1),
|
...cameras.slice(0, index),
|
||||||
]);
|
...cameras.slice(index + 1),
|
||||||
}
|
]);
|
||||||
}}
|
}
|
||||||
/>
|
}}
|
||||||
))}
|
/>
|
||||||
|
),
|
||||||
|
)}
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
{error && <div className="text-danger">{error}</div>}
|
{error && <div className="text-danger">{error}</div>}
|
||||||
|
|||||||
@ -13,21 +13,18 @@ export default function BirdseyeLivePlayer({
|
|||||||
birdseyeConfig,
|
birdseyeConfig,
|
||||||
liveMode,
|
liveMode,
|
||||||
}: LivePlayerProps) {
|
}: LivePlayerProps) {
|
||||||
|
let player;
|
||||||
if (liveMode == "webrtc") {
|
if (liveMode == "webrtc") {
|
||||||
return (
|
player = (
|
||||||
<div className="max-w-5xl">
|
<WebRtcPlayer className={`rounded-2xl size-full`} camera="birdseye" />
|
||||||
<WebRtcPlayer camera="birdseye" />
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
} else if (liveMode == "mse") {
|
} else if (liveMode == "mse") {
|
||||||
if ("MediaSource" in window || "ManagedMediaSource" in window) {
|
if ("MediaSource" in window || "ManagedMediaSource" in window) {
|
||||||
return (
|
player = (
|
||||||
<div className="max-w-5xl">
|
<MSEPlayer className={`rounded-2xl size-full`} camera="birdseye" />
|
||||||
<MSEPlayer camera="birdseye" />
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
player = (
|
||||||
<div className="w-5xl text-center text-sm">
|
<div className="w-5xl text-center text-sm">
|
||||||
MSE is only supported on iOS 17.1+. You'll need to update if available
|
MSE is only supported on iOS 17.1+. You'll need to update if available
|
||||||
or use jsmpeg / webRTC streams. See the docs for more info.
|
or use jsmpeg / webRTC streams. See the docs for more info.
|
||||||
@ -35,17 +32,23 @@ export default function BirdseyeLivePlayer({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (liveMode == "jsmpeg") {
|
} else if (liveMode == "jsmpeg") {
|
||||||
return (
|
player = (
|
||||||
<div className={`max-w-[${birdseyeConfig.width}px]`}>
|
<JSMpegPlayer
|
||||||
<JSMpegPlayer
|
className={`w-full max-w-[${birdseyeConfig.width}px] flex justify-center rounded-2xl overflow-hidden`}
|
||||||
camera="birdseye"
|
camera="birdseye"
|
||||||
className="w-full flex justify-center rounded-2xl overflow-hidden"
|
width={birdseyeConfig.width}
|
||||||
width={birdseyeConfig.width}
|
height={birdseyeConfig.height}
|
||||||
height={birdseyeConfig.height}
|
/>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
<ActivityIndicator />;
|
player = <ActivityIndicator />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`relative flex justify-center w-full cursor-pointer`}>
|
||||||
|
<div className="absolute top-0 inset-x-0 rounded-2xl z-10 w-full h-[30%] bg-gradient-to-b from-black/20 to-transparent pointer-events-none"></div>
|
||||||
|
<div className="absolute bottom-0 inset-x-0 rounded-2xl z-10 w-full h-[10%] bg-gradient-to-t from-black/20 to-transparent pointer-events-none"></div>
|
||||||
|
<div className="">{player}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,14 @@ function Live() {
|
|||||||
const [selectedCameraName, setSelectedCameraName] = useOverlayState("camera");
|
const [selectedCameraName, setSelectedCameraName] = useOverlayState("camera");
|
||||||
const [cameraGroup] = useOverlayState("cameraGroup");
|
const [cameraGroup] = useOverlayState("cameraGroup");
|
||||||
|
|
||||||
|
const includesBirdseye = useMemo(() => {
|
||||||
|
if (config && cameraGroup) {
|
||||||
|
return config.camera_groups[cameraGroup].cameras.includes("birdseye");
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}, [config, cameraGroup]);
|
||||||
|
|
||||||
const cameras = useMemo(() => {
|
const cameras = useMemo(() => {
|
||||||
if (!config) {
|
if (!config) {
|
||||||
return [];
|
return [];
|
||||||
@ -18,15 +26,9 @@ function Live() {
|
|||||||
|
|
||||||
if (cameraGroup) {
|
if (cameraGroup) {
|
||||||
const group = config.camera_groups[cameraGroup];
|
const group = config.camera_groups[cameraGroup];
|
||||||
console.log(config.cameras);
|
|
||||||
const groupCameras = Object.values(config.cameras)
|
const groupCameras = Object.values(config.cameras)
|
||||||
.filter((conf) => conf.enabled && group.cameras.includes(conf.name))
|
.filter((conf) => conf.enabled && group.cameras.includes(conf.name))
|
||||||
.sort((aConf, bConf) => aConf.ui.order - bConf.ui.order);
|
.sort((aConf, bConf) => aConf.ui.order - bConf.ui.order);
|
||||||
if (group.cameras.includes("birdseye")) {
|
|
||||||
groupCameras.push({
|
|
||||||
name: "birdseye",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return groupCameras;
|
return groupCameras;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,6 +49,7 @@ function Live() {
|
|||||||
return (
|
return (
|
||||||
<LiveDashboardView
|
<LiveDashboardView
|
||||||
cameras={cameras}
|
cameras={cameras}
|
||||||
|
includeBirdseye={includesBirdseye}
|
||||||
onSelectCamera={setSelectedCameraName}
|
onSelectCamera={setSelectedCameraName}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -186,7 +186,6 @@ function UIPlayground() {
|
|||||||
|
|
||||||
const [isEventsReviewTimeline, setIsEventsReviewTimeline] = useState(true);
|
const [isEventsReviewTimeline, setIsEventsReviewTimeline] = useState(true);
|
||||||
const birdseyeConfig = config?.birdseye;
|
const birdseyeConfig = config?.birdseye;
|
||||||
console.log(birdseyeConfig);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@ -2,12 +2,13 @@ import { useFrigateReviews } from "@/api/ws";
|
|||||||
import Logo from "@/components/Logo";
|
import Logo from "@/components/Logo";
|
||||||
import { CameraGroupSelector } from "@/components/filter/CameraGroupSelector";
|
import { CameraGroupSelector } from "@/components/filter/CameraGroupSelector";
|
||||||
import { AnimatedEventThumbnail } from "@/components/image/AnimatedEventThumbnail";
|
import { AnimatedEventThumbnail } from "@/components/image/AnimatedEventThumbnail";
|
||||||
|
import BirdseyeLivePlayer from "@/components/player/BirdseyeLivePlayer";
|
||||||
import LivePlayer from "@/components/player/LivePlayer";
|
import LivePlayer from "@/components/player/LivePlayer";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
|
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
|
||||||
import { TooltipProvider } from "@/components/ui/tooltip";
|
import { TooltipProvider } from "@/components/ui/tooltip";
|
||||||
import { usePersistence } from "@/hooks/use-persistence";
|
import { usePersistence } from "@/hooks/use-persistence";
|
||||||
import { CameraConfig } from "@/types/frigateConfig";
|
import { CameraConfig, FrigateConfig } from "@/types/frigateConfig";
|
||||||
import { ReviewSegment } from "@/types/review";
|
import { ReviewSegment } from "@/types/review";
|
||||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import { isDesktop, isMobile, isSafari } from "react-device-detect";
|
import { isDesktop, isMobile, isSafari } from "react-device-detect";
|
||||||
@ -16,12 +17,16 @@ import useSWR from "swr";
|
|||||||
|
|
||||||
type LiveDashboardViewProps = {
|
type LiveDashboardViewProps = {
|
||||||
cameras: CameraConfig[];
|
cameras: CameraConfig[];
|
||||||
|
includeBirdseye: boolean;
|
||||||
onSelectCamera: (camera: string) => void;
|
onSelectCamera: (camera: string) => void;
|
||||||
};
|
};
|
||||||
export default function LiveDashboardView({
|
export default function LiveDashboardView({
|
||||||
cameras,
|
cameras,
|
||||||
|
includeBirdseye,
|
||||||
onSelectCamera,
|
onSelectCamera,
|
||||||
}: LiveDashboardViewProps) {
|
}: LiveDashboardViewProps) {
|
||||||
|
const { data: config } = useSWR<FrigateConfig>("config");
|
||||||
|
|
||||||
// layout
|
// layout
|
||||||
|
|
||||||
const [layout, setLayout] = usePersistence<"grid" | "list">(
|
const [layout, setLayout] = usePersistence<"grid" | "list">(
|
||||||
@ -74,6 +79,8 @@ export default function LiveDashboardView({
|
|||||||
};
|
};
|
||||||
}, [visibilityListener]);
|
}, [visibilityListener]);
|
||||||
|
|
||||||
|
const birdseyeConfig = config?.birdseye;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="size-full overflow-y-auto px-2">
|
<div className="size-full overflow-y-auto px-2">
|
||||||
{isMobile && (
|
{isMobile && (
|
||||||
@ -123,6 +130,12 @@ export default function LiveDashboardView({
|
|||||||
<div
|
<div
|
||||||
className={`my-4 grid ${layout == "grid" ? "grid-cols-2 xl:grid-cols-3 3xl:grid-cols-4" : ""} gap-2 md:gap-4 *:rounded-2xl *:bg-black`}
|
className={`my-4 grid ${layout == "grid" ? "grid-cols-2 xl:grid-cols-3 3xl:grid-cols-4" : ""} gap-2 md:gap-4 *:rounded-2xl *:bg-black`}
|
||||||
>
|
>
|
||||||
|
{includeBirdseye && birdseyeConfig && (
|
||||||
|
<BirdseyeLivePlayer
|
||||||
|
birdseyeConfig={birdseyeConfig}
|
||||||
|
liveMode={birdseyeConfig.restream ? "mse" : "jsmpeg"}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{cameras.map((camera) => {
|
{cameras.map((camera) => {
|
||||||
let grow;
|
let grow;
|
||||||
const aspectRatio = camera.detect.width / camera.detect.height;
|
const aspectRatio = camera.detect.width / camera.detect.height;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user