feat: Add camera nickname (#19567)

* refactor: Refactor camera nickname

* fix: fix cameraNameLabel visually

* chore: The Explore search function also displays the Camera's nickname in English

* chore: add mobile page camera nickname

* feat: webpush support camera nickname

* fix: fix storage camera name is null

* chore: fix review detail and context menu camera nickname

* chore: fix use-stats and notification setting camera nickname

* fix: fix stats camera if not nickname need capitalize

* fix: fix debug page open camera web ui i18n and camera nickname support

* fix: fix camera metrics not use nickname

* refactor: refactor use-camera-nickname hook.
This commit is contained in:
GuoQing Liu
2025-08-26 11:15:01 -06:00
committed by GitHub
parent 195f705616
commit d3af748366
31 changed files with 276 additions and 99 deletions
+4 -2
View File
@@ -24,6 +24,7 @@ import { baseUrl } from "@/api/baseUrl";
import { PlayerStats } from "./PlayerStats";
import { LuVideoOff } from "react-icons/lu";
import { Trans, useTranslation } from "react-i18next";
import { useCameraNickname } from "@/hooks/use-camera-nickname";
type LivePlayerProps = {
cameraRef?: (ref: HTMLDivElement | null) => void;
@@ -76,6 +77,7 @@ export default function LivePlayer({
const internalContainerRef = useRef<HTMLDivElement | null>(null);
const cameraName = useCameraNickname(cameraConfig);
// stats
const [stats, setStats] = useState<PlayerStatsType>({
@@ -412,7 +414,7 @@ export default function LivePlayer({
<Trans
ns="components/player"
values={{
cameraName: capitalizeFirstLetter(cameraConfig.name),
cameraName: cameraName,
}}
>
streamOffline.desc
@@ -444,7 +446,7 @@ export default function LivePlayer({
<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`}
>
{cameraConfig.name.replaceAll("_", " ")}
{cameraName}
</Chip>
)}
</div>
+5 -2
View File
@@ -21,6 +21,7 @@ import {
usePreviewForTimeRange,
} from "@/hooks/use-camera-previews";
import { useTranslation } from "react-i18next";
import { useCameraNickname } from "@/hooks/use-camera-nickname";
type PreviewPlayerProps = {
previewRef?: (ref: HTMLDivElement | null) => void;
@@ -148,6 +149,7 @@ function PreviewVideoPlayer({
const { t } = useTranslation(["components/player"]);
const { data: config } = useSWR<FrigateConfig>("config");
const cameraName = useCameraNickname(camera);
// controlling playback
const previewRef = useRef<HTMLVideoElement | null>(null);
@@ -342,7 +344,7 @@ function PreviewVideoPlayer({
)}
{cameraPreviews && !currentPreview && (
<div className="absolute inset-0 flex items-center justify-center rounded-lg bg-background_alt text-primary dark:bg-black md:rounded-2xl">
{t("noPreviewFoundFor", { camera: camera.replaceAll("_", " ") })}
{t("noPreviewFoundFor", { camera: cameraName })}
</div>
)}
{firstLoad && <Skeleton className="absolute aspect-video size-full" />}
@@ -464,6 +466,7 @@ function PreviewFramesPlayer({
}: PreviewFramesPlayerProps) {
const { t } = useTranslation(["components/player"]);
const cameraName = useCameraNickname(camera);
// frames data
const { data: previewFrames } = useSWR<string[]>(
@@ -564,7 +567,7 @@ function PreviewFramesPlayer({
/>
{previewFrames?.length === 0 && (
<div className="-y-translate-1/2 align-center absolute inset-x-0 top-1/2 rounded-lg bg-background_alt text-center text-primary dark:bg-black md:rounded-2xl">
{t("noPreviewFoundFor", { cameraName: camera.replaceAll("_", " ") })}
{t("noPreviewFoundFor", { cameraName: cameraName })}
</div>
)}
{firstLoad && <Skeleton className="absolute aspect-video size-full" />}