feat(player): always show camera names + add UI config toggle (#20705)

* feat(player): always show camera names + add UI config toggle

* feat(settings): add toggle for displaying camera names in multi-camera views

* update label and description for camera name setting
This commit is contained in:
Thibault Junin 2025-10-29 14:20:11 +00:00 committed by GitHub
parent 576f692dae
commit 9917fc3169
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 40 additions and 8 deletions

View File

@ -47,6 +47,10 @@
"playAlertVideos": { "playAlertVideos": {
"label": "Play Alert Videos", "label": "Play Alert Videos",
"desc": "By default, recent alerts on the Live dashboard play as small looping videos. Disable this option to only show a static image of recent alerts on this device/browser." "desc": "By default, recent alerts on the Live dashboard play as small looping videos. Disable this option to only show a static image of recent alerts on this device/browser."
},
"displayCameraNames": {
"label": "Always Show Camera Names",
"desc": "Always show the camera names in a chip in the multi-camera live view dashboard."
} }
}, },
"storedLayouts": { "storedLayouts": {

View File

@ -35,6 +35,7 @@ type LivePlayerProps = {
streamName: string; streamName: string;
preferredLiveMode: LivePlayerMode; preferredLiveMode: LivePlayerMode;
showStillWithoutActivity?: boolean; showStillWithoutActivity?: boolean;
alwaysShowCameraName?: boolean;
useWebGL: boolean; useWebGL: boolean;
windowVisible?: boolean; windowVisible?: boolean;
playAudio?: boolean; playAudio?: boolean;
@ -59,6 +60,7 @@ export default function LivePlayer({
streamName, streamName,
preferredLiveMode, preferredLiveMode,
showStillWithoutActivity = true, showStillWithoutActivity = true,
alwaysShowCameraName = false,
useWebGL = false, useWebGL = false,
windowVisible = true, windowVisible = true,
playAudio = false, playAudio = false,
@ -433,20 +435,22 @@ export default function LivePlayer({
</div> </div>
)} )}
<div className="absolute right-2 top-2"> <div className="absolute right-2 top-2 flex items-center gap-3">
{autoLive && {(alwaysShowCameraName ||
!offline && (offline && showStillWithoutActivity) ||
activeMotion && !cameraEnabled) && (
((showStillWithoutActivity && !liveReady) || liveReady) && (
<MdCircle className="mr-2 size-2 animate-pulse text-danger shadow-danger drop-shadow-md" />
)}
{((offline && showStillWithoutActivity) || !cameraEnabled) && (
<Chip <Chip
className={`z-0 flex items-start justify-between space-x-1 bg-gray-500 bg-gradient-to-br from-gray-400 to-gray-500 text-xs capitalize`} className={`z-0 flex items-start justify-between space-x-1 bg-gray-500 bg-gradient-to-br from-gray-400 to-gray-500 text-xs capitalize`}
> >
{cameraName} {cameraName}
</Chip> </Chip>
)} )}
{autoLive &&
!offline &&
activeMotion &&
((showStillWithoutActivity && !liveReady) || liveReady) && (
<MdCircle className="mr-2 size-2 animate-pulse text-danger shadow-danger drop-shadow-md" />
)}
</div> </div>
{showStats && ( {showStats && (
<PlayerStats stats={stats} minimal={cameraRef !== undefined} /> <PlayerStats stats={stats} minimal={cameraRef !== undefined} />

View File

@ -95,6 +95,7 @@ export default function DraggableGridLayout({
} = useCameraLiveMode(cameras, windowVisible); } = useCameraLiveMode(cameras, windowVisible);
const [globalAutoLive] = usePersistence("autoLiveView", true); const [globalAutoLive] = usePersistence("autoLiveView", true);
const [displayCameraNames] = usePersistence("displayCameraNames", false);
const { allGroupsStreamingSettings, setAllGroupsStreamingSettings } = const { allGroupsStreamingSettings, setAllGroupsStreamingSettings } =
useStreamingSettings(); useStreamingSettings();
@ -610,6 +611,7 @@ export default function DraggableGridLayout({
streamName={streamName} streamName={streamName}
autoLive={autoLive ?? globalAutoLive} autoLive={autoLive ?? globalAutoLive}
showStillWithoutActivity={showStillWithoutActivity ?? true} showStillWithoutActivity={showStillWithoutActivity ?? true}
alwaysShowCameraName={displayCameraNames}
useWebGL={useWebGL} useWebGL={useWebGL}
cameraRef={cameraRef} cameraRef={cameraRef}
className={cn( className={cn(

View File

@ -654,6 +654,7 @@ export default function LiveCameraView({
className={`${fullscreen ? "*:rounded-none" : ""}`} className={`${fullscreen ? "*:rounded-none" : ""}`}
windowVisible windowVisible
showStillWithoutActivity={false} showStillWithoutActivity={false}
alwaysShowCameraName={false}
cameraConfig={camera} cameraConfig={camera}
playAudio={audio} playAudio={audio}
playInBackground={playInBackground ?? false} playInBackground={playInBackground ?? false}

View File

@ -211,6 +211,7 @@ export default function LiveDashboardView({
} = useCameraLiveMode(cameras, windowVisible); } = useCameraLiveMode(cameras, windowVisible);
const [globalAutoLive] = usePersistence("autoLiveView", true); const [globalAutoLive] = usePersistence("autoLiveView", true);
const [displayCameraNames] = usePersistence("displayCameraNames", false);
const { allGroupsStreamingSettings, setAllGroupsStreamingSettings } = const { allGroupsStreamingSettings, setAllGroupsStreamingSettings } =
useStreamingSettings(); useStreamingSettings();
@ -549,6 +550,7 @@ export default function LiveDashboardView({
preferredLiveMode={preferredLiveModes[camera.name] ?? "mse"} preferredLiveMode={preferredLiveModes[camera.name] ?? "mse"}
autoLive={autoLive ?? globalAutoLive} autoLive={autoLive ?? globalAutoLive}
showStillWithoutActivity={showStillWithoutActivity ?? true} showStillWithoutActivity={showStillWithoutActivity ?? true}
alwaysShowCameraName={displayCameraNames}
useWebGL={useWebGL} useWebGL={useWebGL}
playInBackground={false} playInBackground={false}
showStats={statsStates[camera.name]} showStats={statsStates[camera.name]}

View File

@ -92,6 +92,10 @@ export default function UiSettingsView() {
// settings // settings
const [autoLive, setAutoLive] = usePersistence("autoLiveView", true); const [autoLive, setAutoLive] = usePersistence("autoLiveView", true);
const [cameraNames, setCameraName] = usePersistence(
"displayCameraNames",
false,
);
const [playbackRate, setPlaybackRate] = usePersistence("playbackRate", 1); const [playbackRate, setPlaybackRate] = usePersistence("playbackRate", 1);
const [weekStartsOn, setWeekStartsOn] = usePersistence("weekStartsOn", 0); const [weekStartsOn, setWeekStartsOn] = usePersistence("weekStartsOn", 0);
const [alertVideos, setAlertVideos] = usePersistence("alertVideos", true); const [alertVideos, setAlertVideos] = usePersistence("alertVideos", true);
@ -142,6 +146,21 @@ export default function UiSettingsView() {
<p>{t("general.liveDashboard.playAlertVideos.desc")}</p> <p>{t("general.liveDashboard.playAlertVideos.desc")}</p>
</div> </div>
</div> </div>
<div className="space-y-3">
<div className="flex flex-row items-center justify-start gap-2">
<Switch
id="camera-names"
checked={cameraNames}
onCheckedChange={setCameraName}
/>
<Label className="cursor-pointer" htmlFor="auto-live">
{t("general.liveDashboard.displayCameraNames.label")}
</Label>
</div>
<div className="my-2 max-w-5xl text-sm text-muted-foreground">
<p>{t("general.liveDashboard.displayCameraNames.desc")}</p>
</div>
</div>
</div> </div>
<div className="my-3 flex w-full flex-col space-y-6"> <div className="my-3 flex w-full flex-col space-y-6">