mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-02 17:25:22 +03:00
Add options for reordering and hiding cameras selectively
This commit is contained in:
parent
b1cc64d4fa
commit
7cf35f171b
@ -431,4 +431,12 @@ cameras:
|
|||||||
quality: 70
|
quality: 70
|
||||||
# Optional: Restrict mqtt messages to objects that entered any of the listed zones (default: no required zones)
|
# Optional: Restrict mqtt messages to objects that entered any of the listed zones (default: no required zones)
|
||||||
required_zones: []
|
required_zones: []
|
||||||
|
|
||||||
|
# Optional: Configuration for how camera is handled in the GUI.
|
||||||
|
ui:
|
||||||
|
# Optional: Adjust sort order of cameras in the GUI. Larger numbers come later (default: shown below)
|
||||||
|
# By default the cameras are sorted alphabetically.
|
||||||
|
order: 0
|
||||||
|
# Optional: Whether or not to show the camera in the GUI (default: shown below)
|
||||||
|
show: True
|
||||||
```
|
```
|
||||||
|
|||||||
@ -499,6 +499,11 @@ class CameraLiveConfig(FrigateBaseModel):
|
|||||||
quality: int = Field(default=8, ge=1, le=31, title="Live camera view quality")
|
quality: int = Field(default=8, ge=1, le=31, title="Live camera view quality")
|
||||||
|
|
||||||
|
|
||||||
|
class CameraUiConfig(FrigateBaseModel):
|
||||||
|
order: int = Field(default=0, title="Order of camera in GUI.")
|
||||||
|
show: bool = Field(default=True, title="Show this camera in Frigate GUI.")
|
||||||
|
|
||||||
|
|
||||||
class CameraConfig(FrigateBaseModel):
|
class CameraConfig(FrigateBaseModel):
|
||||||
name: Optional[str] = Field(title="Camera name.", regex="^[a-zA-Z0-9_-]+$")
|
name: Optional[str] = Field(title="Camera name.", regex="^[a-zA-Z0-9_-]+$")
|
||||||
ffmpeg: CameraFfmpegConfig = Field(title="FFmpeg configuration for the camera.")
|
ffmpeg: CameraFfmpegConfig = Field(title="FFmpeg configuration for the camera.")
|
||||||
@ -531,6 +536,9 @@ class CameraConfig(FrigateBaseModel):
|
|||||||
detect: DetectConfig = Field(
|
detect: DetectConfig = Field(
|
||||||
default_factory=DetectConfig, title="Object detection configuration."
|
default_factory=DetectConfig, title="Object detection configuration."
|
||||||
)
|
)
|
||||||
|
ui: CameraUiConfig = Field(
|
||||||
|
default_factory=CameraUiConfig, title="Camera UI Modifications."
|
||||||
|
)
|
||||||
timestamp_style: TimestampStyleConfig = Field(
|
timestamp_style: TimestampStyleConfig = Field(
|
||||||
default_factory=TimestampStyleConfig, title="Timestamp style configuration."
|
default_factory=TimestampStyleConfig, title="Timestamp style configuration."
|
||||||
)
|
)
|
||||||
|
|||||||
@ -20,6 +20,7 @@ export const handlers = [
|
|||||||
detect: { width: 1280, height: 720 },
|
detect: { width: 1280, height: 720 },
|
||||||
snapshots: {},
|
snapshots: {},
|
||||||
live: { height: 720 },
|
live: { height: 720 },
|
||||||
|
ui: { show: true, order: 0 },
|
||||||
},
|
},
|
||||||
side: {
|
side: {
|
||||||
name: 'side',
|
name: 'side',
|
||||||
@ -28,6 +29,7 @@ export const handlers = [
|
|||||||
detect: { width: 1280, height: 720 },
|
detect: { width: 1280, height: 720 },
|
||||||
snapshots: {},
|
snapshots: {},
|
||||||
live: { height: 720 },
|
live: { height: 720 },
|
||||||
|
ui: { show: true, order: 1 },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import LinkedLogo from './components/LinkedLogo';
|
|||||||
import { Match } from 'preact-router/match';
|
import { Match } from 'preact-router/match';
|
||||||
import { memo } from 'preact/compat';
|
import { memo } from 'preact/compat';
|
||||||
import { ENV } from './env';
|
import { ENV } from './env';
|
||||||
|
import { useMemo } from 'preact/hooks'
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import NavigationDrawer, { Destination, Separator } from './components/NavigationDrawer';
|
import NavigationDrawer, { Destination, Separator } from './components/NavigationDrawer';
|
||||||
|
|
||||||
@ -19,35 +20,14 @@ export default function Sidebar() {
|
|||||||
<Match path="/cameras/:camera/:other?">
|
<Match path="/cameras/:camera/:other?">
|
||||||
{({ matches }) =>
|
{({ matches }) =>
|
||||||
matches ? (
|
matches ? (
|
||||||
<Fragment>
|
<SortedCameras unsortedCameras={cameras} />
|
||||||
<Separator />
|
|
||||||
{Object.entries(cameras).map(([camera]) => (
|
|
||||||
<Destination key={camera} href={`/cameras/${camera}`} text={camera} />
|
|
||||||
))}
|
|
||||||
<Separator />
|
|
||||||
</Fragment>
|
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
</Match>
|
</Match>
|
||||||
<Match path="/recording/:camera/:date?/:hour?/:seconds?">
|
<Match path="/recording/:camera/:date?/:hour?/:seconds?">
|
||||||
{({ matches }) =>
|
{({ matches }) =>
|
||||||
matches ? (
|
matches ? (
|
||||||
<Fragment>
|
<SortedRecordingCameras unsortedCameras={cameras} />
|
||||||
<Separator />
|
|
||||||
{Object.entries(cameras).map(([camera, conf]) => {
|
|
||||||
if (conf.record.enabled) {
|
|
||||||
return (
|
|
||||||
<Destination
|
|
||||||
path={`/recording/${camera}/:date?/:hour?/:seconds?`}
|
|
||||||
href={`/recording/${camera}`}
|
|
||||||
text={camera}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
})}
|
|
||||||
<Separator />
|
|
||||||
</Fragment>
|
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
</Match>
|
</Match>
|
||||||
@ -68,10 +48,58 @@ export default function Sidebar() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function SortedCameras({ unsortedCameras }) {
|
||||||
|
|
||||||
|
const sortedCameras = useMemo(() =>
|
||||||
|
Object.entries(unsortedCameras)
|
||||||
|
.filter(([_, conf]) => conf.ui.show)
|
||||||
|
.sort(([_, aConf], [__, bConf]) => aConf.ui.order === bConf.ui.order ? 0 : (aConf.ui.order > bConf.ui.order ? 1 : -1)),
|
||||||
|
[unsortedCameras]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<Separator />
|
||||||
|
{sortedCameras.map(([camera]) => (
|
||||||
|
<Destination key={camera} href={`/cameras/${camera}`} text={camera} />
|
||||||
|
))}
|
||||||
|
<Separator />
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SortedRecordingCameras({ unsortedCameras }) {
|
||||||
|
|
||||||
|
const sortedCameras = useMemo(() =>
|
||||||
|
Object.entries(unsortedCameras)
|
||||||
|
.filter(([_, conf]) => conf.ui.show)
|
||||||
|
.sort(([_, aConf], [__, bConf]) => aConf.ui.order === bConf.ui.order ? 0 : (aConf.ui.order > bConf.ui.order ? 1 : -1)),
|
||||||
|
[unsortedCameras]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<Separator />
|
||||||
|
{sortedCameras.map(([camera, conf]) => {
|
||||||
|
if (conf.record.enabled) {
|
||||||
|
return (
|
||||||
|
<Destination
|
||||||
|
key={camera}
|
||||||
|
path={`/recording/${camera}/:date?/:hour?/:seconds?`}
|
||||||
|
href={`/recording/${camera}`}
|
||||||
|
text={camera}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})}
|
||||||
|
<Separator />
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const Header = memo(() => {
|
const Header = memo(() => {
|
||||||
return (
|
return (
|
||||||
<div className="text-gray-500">
|
<div className="text-gray-500">
|
||||||
<LinkedLogo />
|
<LinkedLogo />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import { h } from 'preact';
|
import { h, Fragment } from 'preact';
|
||||||
import ActivityIndicator from '../components/ActivityIndicator';
|
import ActivityIndicator from '../components/ActivityIndicator';
|
||||||
import Card from '../components/Card';
|
import Card from '../components/Card';
|
||||||
import CameraImage from '../components/CameraImage';
|
import CameraImage from '../components/CameraImage';
|
||||||
@ -16,10 +16,25 @@ export default function Cameras() {
|
|||||||
<ActivityIndicator />
|
<ActivityIndicator />
|
||||||
) : (
|
) : (
|
||||||
<div className="grid grid-cols-1 3xl:grid-cols-3 md:grid-cols-2 gap-4 p-2 px-4">
|
<div className="grid grid-cols-1 3xl:grid-cols-3 md:grid-cols-2 gap-4 p-2 px-4">
|
||||||
{Object.entries(config.cameras).map(([camera, conf]) => (
|
<SortedCameras unsortedCameras={config.cameras} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SortedCameras({ unsortedCameras }) {
|
||||||
|
|
||||||
|
const sortedCameras = useMemo(() =>
|
||||||
|
Object.entries(unsortedCameras)
|
||||||
|
.filter(([_, conf]) => conf.ui.show)
|
||||||
|
.sort(([_, aConf], [__, bConf]) => aConf.ui.order === bConf.ui.order ? 0 : (aConf.ui.order > bConf.ui.order ? 1 : -1)),
|
||||||
|
[unsortedCameras]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
{sortedCameras.map(([camera, conf]) => (
|
||||||
<Camera key={camera} name={camera} conf={conf} />
|
<Camera key={camera} name={camera} conf={conf} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,4 +82,4 @@ function Camera({ name }) {
|
|||||||
return (
|
return (
|
||||||
<Card buttons={buttons} href={href} header={name} icons={icons} media={<CameraImage camera={name} stretch />} />
|
<Card buttons={buttons} href={href} header={name} icons={icons} media={<CameraImage camera={name} stretch />} />
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user