mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-05-03 06:50:58 +00:00
Compare commits
No commits in common. "a9f139e062a175df312f3ba644bdb2059f8810cf" and "9291543705606a99ffae31cbea048094cf8461ad" have entirely different histories.
a9f139e062
...
9291543705
@ -63,9 +63,9 @@ class PtzAutotrackConfig(FrigateBaseModel):
|
|||||||
else:
|
else:
|
||||||
raise ValueError("Invalid type for movement_weights")
|
raise ValueError("Invalid type for movement_weights")
|
||||||
|
|
||||||
if len(weights) != 6:
|
if len(weights) != 5:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"movement_weights must have exactly 6 floats, remove this line from your config and run autotracking calibration"
|
"movement_weights must have exactly 5 floats, remove this line from your config and run autotracking calibration"
|
||||||
)
|
)
|
||||||
|
|
||||||
return weights
|
return weights
|
||||||
|
|||||||
@ -243,7 +243,7 @@ class EmbeddingsContext:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def rename_face(self, old_name: str, new_name: str) -> None:
|
def rename_face(self, old_name: str, new_name: str) -> None:
|
||||||
valid_name_pattern = r"^[a-zA-Z0-9\s_-]{1,50}$"
|
valid_name_pattern = r"^[a-zA-Z0-9_-]{1,50}$"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
sanitized_old_name = sanitize_filename(old_name, replacement_text="_")
|
sanitized_old_name = sanitize_filename(old_name, replacement_text="_")
|
||||||
|
|||||||
@ -120,7 +120,7 @@ class EmbeddingMaintainer(threading.Thread):
|
|||||||
if self.config.face_recognition.enabled:
|
if self.config.face_recognition.enabled:
|
||||||
self.realtime_processors.append(
|
self.realtime_processors.append(
|
||||||
FaceRealTimeProcessor(
|
FaceRealTimeProcessor(
|
||||||
self.config, self.requestor, self.event_metadata_publisher, metrics
|
self.config, self.event_metadata_publisher, metrics
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -206,7 +206,6 @@ class PtzAutoTracker:
|
|||||||
self.calibrating: dict[str, object] = {}
|
self.calibrating: dict[str, object] = {}
|
||||||
self.intercept: dict[str, object] = {}
|
self.intercept: dict[str, object] = {}
|
||||||
self.move_coefficients: dict[str, object] = {}
|
self.move_coefficients: dict[str, object] = {}
|
||||||
self.zoom_time: dict[str, float] = {}
|
|
||||||
self.zoom_factor: dict[str, object] = {}
|
self.zoom_factor: dict[str, object] = {}
|
||||||
|
|
||||||
# if cam is set to autotrack, onvif should be set up
|
# if cam is set to autotrack, onvif should be set up
|
||||||
@ -273,12 +272,7 @@ class PtzAutoTracker:
|
|||||||
|
|
||||||
move_status_supported = self.onvif.get_service_capabilities(camera)
|
move_status_supported = self.onvif.get_service_capabilities(camera)
|
||||||
|
|
||||||
if not (
|
if move_status_supported is None or move_status_supported.lower() != "true":
|
||||||
isinstance(move_status_supported, bool) and move_status_supported
|
|
||||||
) and not (
|
|
||||||
isinstance(move_status_supported, str)
|
|
||||||
and move_status_supported.lower() == "true"
|
|
||||||
):
|
|
||||||
logger.warning(
|
logger.warning(
|
||||||
f"Disabling autotracking for {camera}: ONVIF MoveStatus not supported"
|
f"Disabling autotracking for {camera}: ONVIF MoveStatus not supported"
|
||||||
)
|
)
|
||||||
@ -298,7 +292,7 @@ class PtzAutoTracker:
|
|||||||
self.move_threads[camera].start()
|
self.move_threads[camera].start()
|
||||||
|
|
||||||
if camera_config.onvif.autotracking.movement_weights:
|
if camera_config.onvif.autotracking.movement_weights:
|
||||||
if len(camera_config.onvif.autotracking.movement_weights) == 6:
|
if len(camera_config.onvif.autotracking.movement_weights) == 5:
|
||||||
camera_config.onvif.autotracking.movement_weights = [
|
camera_config.onvif.autotracking.movement_weights = [
|
||||||
float(val)
|
float(val)
|
||||||
for val in camera_config.onvif.autotracking.movement_weights
|
for val in camera_config.onvif.autotracking.movement_weights
|
||||||
@ -317,10 +311,7 @@ class PtzAutoTracker:
|
|||||||
camera_config.onvif.autotracking.movement_weights[2]
|
camera_config.onvif.autotracking.movement_weights[2]
|
||||||
)
|
)
|
||||||
self.move_coefficients[camera] = (
|
self.move_coefficients[camera] = (
|
||||||
camera_config.onvif.autotracking.movement_weights[3:5]
|
camera_config.onvif.autotracking.movement_weights[3:]
|
||||||
)
|
|
||||||
self.zoom_time[camera] = (
|
|
||||||
camera_config.onvif.autotracking.movement_weights[5]
|
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
camera_config.onvif.autotracking.enabled = False
|
camera_config.onvif.autotracking.enabled = False
|
||||||
@ -369,7 +360,6 @@ class PtzAutoTracker:
|
|||||||
!= ZoomingModeEnum.disabled
|
!= ZoomingModeEnum.disabled
|
||||||
):
|
):
|
||||||
logger.info(f"Calibration for {camera} in progress: 0% complete")
|
logger.info(f"Calibration for {camera} in progress: 0% complete")
|
||||||
self.zoom_time[camera] = 0
|
|
||||||
|
|
||||||
for i in range(2):
|
for i in range(2):
|
||||||
# absolute move to 0 - fully zoomed out
|
# absolute move to 0 - fully zoomed out
|
||||||
@ -413,7 +403,6 @@ class PtzAutoTracker:
|
|||||||
|
|
||||||
zoom_out_values.append(self.ptz_metrics[camera].zoom_level.value)
|
zoom_out_values.append(self.ptz_metrics[camera].zoom_level.value)
|
||||||
|
|
||||||
zoom_start_time = time.time()
|
|
||||||
# relative move to 0.01
|
# relative move to 0.01
|
||||||
self.onvif._move_relative(
|
self.onvif._move_relative(
|
||||||
camera,
|
camera,
|
||||||
@ -426,45 +415,13 @@ class PtzAutoTracker:
|
|||||||
while not self.ptz_metrics[camera].motor_stopped.is_set():
|
while not self.ptz_metrics[camera].motor_stopped.is_set():
|
||||||
self.onvif.get_camera_status(camera)
|
self.onvif.get_camera_status(camera)
|
||||||
|
|
||||||
zoom_stop_time = time.time()
|
|
||||||
|
|
||||||
full_relative_start_time = time.time()
|
|
||||||
|
|
||||||
self.onvif._move_relative(
|
|
||||||
camera,
|
|
||||||
-1,
|
|
||||||
-1,
|
|
||||||
-1e-2,
|
|
||||||
1,
|
|
||||||
)
|
|
||||||
|
|
||||||
while not self.ptz_metrics[camera].motor_stopped.is_set():
|
|
||||||
self.onvif.get_camera_status(camera)
|
|
||||||
|
|
||||||
full_relative_stop_time = time.time()
|
|
||||||
|
|
||||||
self.onvif._move_relative(
|
|
||||||
camera,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1e-2,
|
|
||||||
1,
|
|
||||||
)
|
|
||||||
|
|
||||||
while not self.ptz_metrics[camera].motor_stopped.is_set():
|
|
||||||
self.onvif.get_camera_status(camera)
|
|
||||||
|
|
||||||
self.zoom_time[camera] = (
|
|
||||||
full_relative_stop_time - full_relative_start_time
|
|
||||||
) - (zoom_stop_time - zoom_start_time)
|
|
||||||
|
|
||||||
zoom_in_values.append(self.ptz_metrics[camera].zoom_level.value)
|
zoom_in_values.append(self.ptz_metrics[camera].zoom_level.value)
|
||||||
|
|
||||||
self.ptz_metrics[camera].max_zoom.value = max(zoom_in_values)
|
self.ptz_metrics[camera].max_zoom.value = max(zoom_in_values)
|
||||||
self.ptz_metrics[camera].min_zoom.value = min(zoom_out_values)
|
self.ptz_metrics[camera].min_zoom.value = min(zoom_out_values)
|
||||||
|
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f"{camera}: Calibration values: max zoom: {self.ptz_metrics[camera].max_zoom.value}, min zoom: {self.ptz_metrics[camera].min_zoom.value}, zoom time: {self.zoom_time[camera]}"
|
f"{camera}: Calibration values: max zoom: {self.ptz_metrics[camera].max_zoom.value}, min zoom: {self.ptz_metrics[camera].min_zoom.value}"
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@ -580,7 +537,6 @@ class PtzAutoTracker:
|
|||||||
self.ptz_metrics[camera].max_zoom.value,
|
self.ptz_metrics[camera].max_zoom.value,
|
||||||
self.intercept[camera],
|
self.intercept[camera],
|
||||||
*self.move_coefficients[camera],
|
*self.move_coefficients[camera],
|
||||||
self.zoom_time[camera],
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1105,7 +1061,6 @@ class PtzAutoTracker:
|
|||||||
|
|
||||||
average_velocity = np.zeros((4,))
|
average_velocity = np.zeros((4,))
|
||||||
predicted_box = obj.obj_data["box"]
|
predicted_box = obj.obj_data["box"]
|
||||||
zoom_predicted_box = obj.obj_data["box"]
|
|
||||||
|
|
||||||
centroid_x = obj.obj_data["centroid"][0]
|
centroid_x = obj.obj_data["centroid"][0]
|
||||||
centroid_y = obj.obj_data["centroid"][1]
|
centroid_y = obj.obj_data["centroid"][1]
|
||||||
@ -1114,20 +1069,20 @@ class PtzAutoTracker:
|
|||||||
pan = ((centroid_x / camera_width) - 0.5) * 2
|
pan = ((centroid_x / camera_width) - 0.5) * 2
|
||||||
tilt = (0.5 - (centroid_y / camera_height)) * 2
|
tilt = (0.5 - (centroid_y / camera_height)) * 2
|
||||||
|
|
||||||
_, average_velocity = (
|
|
||||||
self._get_valid_velocity(camera, obj)
|
|
||||||
if "velocity" not in self.tracked_object_metrics[camera]
|
|
||||||
else (
|
|
||||||
self.tracked_object_metrics[camera]["valid_velocity"],
|
|
||||||
self.tracked_object_metrics[camera]["velocity"],
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
camera_config.onvif.autotracking.movement_weights
|
camera_config.onvif.autotracking.movement_weights
|
||||||
): # use estimates if we have available coefficients
|
): # use estimates if we have available coefficients
|
||||||
predicted_movement_time = self._predict_movement_time(camera, pan, tilt)
|
predicted_movement_time = self._predict_movement_time(camera, pan, tilt)
|
||||||
|
|
||||||
|
_, average_velocity = (
|
||||||
|
self._get_valid_velocity(camera, obj)
|
||||||
|
if "velocity" not in self.tracked_object_metrics[camera]
|
||||||
|
else (
|
||||||
|
self.tracked_object_metrics[camera]["valid_velocity"],
|
||||||
|
self.tracked_object_metrics[camera]["velocity"],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
if np.any(average_velocity):
|
if np.any(average_velocity):
|
||||||
# this box could exceed the frame boundaries if velocity is high
|
# this box could exceed the frame boundaries if velocity is high
|
||||||
# but we'll handle that in _enqueue_move() as two separate moves
|
# but we'll handle that in _enqueue_move() as two separate moves
|
||||||
@ -1156,34 +1111,6 @@ class PtzAutoTracker:
|
|||||||
camera, obj, predicted_box, predicted_movement_time, debug_zoom=True
|
camera, obj, predicted_box, predicted_movement_time, debug_zoom=True
|
||||||
)
|
)
|
||||||
|
|
||||||
if (
|
|
||||||
camera_config.onvif.autotracking.movement_weights
|
|
||||||
and camera_config.onvif.autotracking.zooming == ZoomingModeEnum.relative
|
|
||||||
and zoom != 0
|
|
||||||
):
|
|
||||||
zoom_predicted_movement_time = 0
|
|
||||||
|
|
||||||
if np.any(average_velocity):
|
|
||||||
zoom_predicted_movement_time = abs(zoom) * self.zoom_time[camera]
|
|
||||||
|
|
||||||
zoom_predicted_box = (
|
|
||||||
predicted_box
|
|
||||||
+ camera_fps * zoom_predicted_movement_time * average_velocity
|
|
||||||
)
|
|
||||||
|
|
||||||
zoom_predicted_box = np.round(zoom_predicted_box).astype(int)
|
|
||||||
|
|
||||||
centroid_x = round((zoom_predicted_box[0] + zoom_predicted_box[2]) / 2)
|
|
||||||
centroid_y = round((zoom_predicted_box[1] + zoom_predicted_box[3]) / 2)
|
|
||||||
|
|
||||||
# recalculate pan and tilt with new centroid
|
|
||||||
pan = ((centroid_x / camera_width) - 0.5) * 2
|
|
||||||
tilt = (0.5 - (centroid_y / camera_height)) * 2
|
|
||||||
|
|
||||||
logger.debug(
|
|
||||||
f"{camera}: Zoom amount: {zoom}, zoom predicted time: {zoom_predicted_movement_time}, zoom predicted box: {tuple(zoom_predicted_box)}"
|
|
||||||
)
|
|
||||||
|
|
||||||
self._enqueue_move(camera, obj.obj_data["frame_time"], pan, tilt, zoom)
|
self._enqueue_move(camera, obj.obj_data["frame_time"], pan, tilt, zoom)
|
||||||
|
|
||||||
def _autotrack_move_zoom_only(self, camera, obj):
|
def _autotrack_move_zoom_only(self, camera, obj):
|
||||||
@ -1315,7 +1242,7 @@ class PtzAutoTracker:
|
|||||||
return
|
return
|
||||||
|
|
||||||
# this is a brand new object that's on our camera, has our label, entered the zone,
|
# this is a brand new object that's on our camera, has our label, entered the zone,
|
||||||
# is not a false positive, and is active
|
# is not a false positive, and is not initially motionless
|
||||||
if (
|
if (
|
||||||
# new object
|
# new object
|
||||||
self.tracked_object[camera] is None
|
self.tracked_object[camera] is None
|
||||||
@ -1325,7 +1252,7 @@ class PtzAutoTracker:
|
|||||||
and not obj.previous["false_positive"]
|
and not obj.previous["false_positive"]
|
||||||
and not obj.false_positive
|
and not obj.false_positive
|
||||||
and not self.tracked_object_history[camera]
|
and not self.tracked_object_history[camera]
|
||||||
and obj.active
|
and obj.obj_data["motionless_count"] == 0
|
||||||
):
|
):
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f"{camera}: New object: {obj.obj_data['id']} {obj.obj_data['box']} {obj.obj_data['frame_time']}"
|
f"{camera}: New object: {obj.obj_data['id']} {obj.obj_data['box']} {obj.obj_data['frame_time']}"
|
||||||
|
|||||||
@ -1491,9 +1491,7 @@ class TestConfig(unittest.TestCase):
|
|||||||
"fps": 5,
|
"fps": 5,
|
||||||
},
|
},
|
||||||
"onvif": {
|
"onvif": {
|
||||||
"autotracking": {
|
"autotracking": {"movement_weights": "0, 1, 1.23, 2.34, 0.50"}
|
||||||
"movement_weights": "0, 1, 1.23, 2.34, 0.50, 1"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1506,7 +1504,6 @@ class TestConfig(unittest.TestCase):
|
|||||||
"1.23",
|
"1.23",
|
||||||
"2.34",
|
"2.34",
|
||||||
"0.5",
|
"0.5",
|
||||||
"1.0",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
def test_fails_invalid_movement_weights(self):
|
def test_fails_invalid_movement_weights(self):
|
||||||
|
|||||||
@ -319,21 +319,6 @@ def migrate_016_0(config: dict[str, dict[str, any]]) -> dict[str, dict[str, any]
|
|||||||
|
|
||||||
camera_config["live"] = live_config
|
camera_config["live"] = live_config
|
||||||
|
|
||||||
# add another value to movement_weights for autotracking cams
|
|
||||||
onvif_config = camera_config.get("onvif", {})
|
|
||||||
if "autotracking" in onvif_config:
|
|
||||||
movement_weights = (
|
|
||||||
camera_config.get("onvif", {})
|
|
||||||
.get("autotracking")
|
|
||||||
.get("movement_weights", {})
|
|
||||||
)
|
|
||||||
|
|
||||||
if movement_weights and len(movement_weights.split(",")) == 5:
|
|
||||||
onvif_config["autotracking"]["movement_weights"] = (
|
|
||||||
movement_weights + ", 0"
|
|
||||||
)
|
|
||||||
camera_config["onvif"] = onvif_config
|
|
||||||
|
|
||||||
new_config["cameras"][name] = camera_config
|
new_config["cameras"][name] = camera_config
|
||||||
|
|
||||||
new_config["version"] = "0.16-0"
|
new_config["version"] = "0.16-0"
|
||||||
|
|||||||
@ -6,10 +6,9 @@
|
|||||||
"classification": "Classification Settings - Frigate",
|
"classification": "Classification Settings - Frigate",
|
||||||
"masksAndZones": "Mask and Zone Editor - Frigate",
|
"masksAndZones": "Mask and Zone Editor - Frigate",
|
||||||
"motionTuner": "Motion Tuner - Frigate",
|
"motionTuner": "Motion Tuner - Frigate",
|
||||||
"object": "Debug - Frigate",
|
"object": "Object Settings - Frigate",
|
||||||
"general": "General Settings - Frigate",
|
"general": "General Settings - Frigate",
|
||||||
"frigatePlus": "Frigate+ Settings - Frigate",
|
"frigatePlus": "Frigate+ Settings - Frigate"
|
||||||
"notifications": "Notification Settings - Frigate"
|
|
||||||
},
|
},
|
||||||
"menu": {
|
"menu": {
|
||||||
"ui": "UI",
|
"ui": "UI",
|
||||||
|
|||||||
@ -34,7 +34,7 @@ import {
|
|||||||
useTheme,
|
useTheme,
|
||||||
} from "@/context/theme-provider";
|
} from "@/context/theme-provider";
|
||||||
import { IoColorPalette } from "react-icons/io5";
|
import { IoColorPalette } from "react-icons/io5";
|
||||||
import { useMemo, useState } from "react";
|
import { useState } from "react";
|
||||||
import { useRestart } from "@/api/ws";
|
import { useRestart } from "@/api/ws";
|
||||||
import {
|
import {
|
||||||
Tooltip,
|
Tooltip,
|
||||||
@ -62,7 +62,6 @@ import { toast } from "sonner";
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { FrigateConfig } from "@/types/frigateConfig";
|
import { FrigateConfig } from "@/types/frigateConfig";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { supportedLanguageKeys } from "@/lib/const";
|
|
||||||
|
|
||||||
type GeneralSettingsProps = {
|
type GeneralSettingsProps = {
|
||||||
className?: string;
|
className?: string;
|
||||||
@ -76,21 +75,20 @@ export default function GeneralSettings({ className }: GeneralSettingsProps) {
|
|||||||
|
|
||||||
// languages
|
// languages
|
||||||
|
|
||||||
const languages = useMemo(() => {
|
const languages = [
|
||||||
// Handle language keys that aren't directly used for translation key
|
{ code: "en", label: t("menu.language.en") },
|
||||||
const specialKeyMap: { [key: string]: string } = {
|
{ code: "es", label: t("menu.language.es") },
|
||||||
"nb-NO": "nb",
|
{ code: "fr", label: t("menu.language.fr") },
|
||||||
"yue-Hant": "yue",
|
{ code: "de", label: t("menu.language.de") },
|
||||||
"zh-CN": "zhCN",
|
{ code: "it", label: t("menu.language.it") },
|
||||||
};
|
{ code: "nl", label: t("menu.language.nl") },
|
||||||
|
{ code: "nb-NO", label: t("menu.language.nb") },
|
||||||
return supportedLanguageKeys.map((key) => {
|
{ code: "tr", label: t("menu.language.tr") },
|
||||||
return {
|
{ code: "pl", label: t("menu.language.pl") },
|
||||||
code: key,
|
{ code: "zh-CN", label: t("menu.language.zhCN") },
|
||||||
label: t(`menu.language.${specialKeyMap[key] || key}`),
|
{ code: "yue-Hant", label: t("menu.language.yue") },
|
||||||
};
|
{ code: "ru", label: t("menu.language.ru") },
|
||||||
});
|
];
|
||||||
}, [t]);
|
|
||||||
|
|
||||||
// settings
|
// settings
|
||||||
|
|
||||||
|
|||||||
@ -108,7 +108,7 @@ export default function GPUInfoDialog({
|
|||||||
<br />
|
<br />
|
||||||
<div>
|
<div>
|
||||||
{t("general.hardwareInfo.gpuInfo.nvidiaSMIOutput.driver", {
|
{t("general.hardwareInfo.gpuInfo.nvidiaSMIOutput.driver", {
|
||||||
driver: nvinfo["0"].driver,
|
name: nvinfo["0"].driver,
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
@ -116,14 +116,14 @@ export default function GPUInfoDialog({
|
|||||||
{t(
|
{t(
|
||||||
"general.hardwareInfo.gpuInfo.nvidiaSMIOutput.cudaComputerCapability",
|
"general.hardwareInfo.gpuInfo.nvidiaSMIOutput.cudaComputerCapability",
|
||||||
{
|
{
|
||||||
cuda_compute: nvinfo["0"].cuda_compute,
|
name: nvinfo["0"].cuda_compute,
|
||||||
},
|
},
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<div>
|
<div>
|
||||||
{t("general.hardwareInfo.gpuInfo.nvidiaSMIOutput.vbios", {
|
{t("general.hardwareInfo.gpuInfo.nvidiaSMIOutput.vbios", {
|
||||||
vbios: nvinfo["0"].vbios,
|
name: nvinfo["0"].vbios,
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,14 +1,15 @@
|
|||||||
import { createContext, useContext, useState, useEffect, useMemo } from "react";
|
import { createContext, useContext, useState, useEffect, useMemo } from "react";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { supportedLanguageKeys } from "@/lib/const";
|
|
||||||
|
|
||||||
type LanguageProviderState = {
|
type LanguageProviderState = {
|
||||||
language: string;
|
language: string;
|
||||||
|
systemLanguage: string;
|
||||||
setLanguage: (language: string) => void;
|
setLanguage: (language: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialState: LanguageProviderState = {
|
const initialState: LanguageProviderState = {
|
||||||
language: i18next.language || "en",
|
language: i18next.language || "en",
|
||||||
|
systemLanguage: "en",
|
||||||
setLanguage: () => null,
|
setLanguage: () => null,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -25,31 +26,10 @@ export function LanguageProvider({
|
|||||||
defaultLanguage?: string;
|
defaultLanguage?: string;
|
||||||
storageKey?: string;
|
storageKey?: string;
|
||||||
}) {
|
}) {
|
||||||
const systemLanguage = useMemo<string>(() => {
|
|
||||||
if (typeof window === "undefined") return defaultLanguage;
|
|
||||||
|
|
||||||
const systemLanguage = window.navigator.language;
|
|
||||||
|
|
||||||
if (supportedLanguageKeys.includes(systemLanguage)) {
|
|
||||||
return systemLanguage;
|
|
||||||
}
|
|
||||||
|
|
||||||
// browser languages may include a -REGION (ex: en-US)
|
|
||||||
if (systemLanguage.includes("-")) {
|
|
||||||
const shortenedSystemLanguage = systemLanguage.split("-")[0];
|
|
||||||
|
|
||||||
if (supportedLanguageKeys.includes(shortenedSystemLanguage)) {
|
|
||||||
return shortenedSystemLanguage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return defaultLanguage;
|
|
||||||
}, [defaultLanguage]);
|
|
||||||
|
|
||||||
const [language, setLanguage] = useState<string>(() => {
|
const [language, setLanguage] = useState<string>(() => {
|
||||||
try {
|
try {
|
||||||
const storedData = localStorage.getItem(storageKey);
|
const storedData = localStorage.getItem(storageKey);
|
||||||
const newLanguage = storedData || systemLanguage;
|
const newLanguage = storedData || defaultLanguage;
|
||||||
i18next.changeLanguage(newLanguage);
|
i18next.changeLanguage(newLanguage);
|
||||||
return newLanguage;
|
return newLanguage;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -59,6 +39,11 @@ export function LanguageProvider({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const systemLanguage = useMemo<string>(() => {
|
||||||
|
if (typeof window === "undefined") return "en";
|
||||||
|
return window.navigator.language;
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// set document lang for smart capitalization
|
// set document lang for smart capitalization
|
||||||
document.documentElement.lang = language;
|
document.documentElement.lang = language;
|
||||||
@ -69,6 +54,7 @@ export function LanguageProvider({
|
|||||||
|
|
||||||
const value = {
|
const value = {
|
||||||
language,
|
language,
|
||||||
|
systemLanguage,
|
||||||
setLanguage: (language: string) => {
|
setLanguage: (language: string) => {
|
||||||
localStorage.setItem(storageKey, language);
|
localStorage.setItem(storageKey, language);
|
||||||
setLanguage(language);
|
setLanguage(language);
|
||||||
|
|||||||
@ -1,14 +0,0 @@
|
|||||||
export const supportedLanguageKeys = [
|
|
||||||
"en",
|
|
||||||
"es",
|
|
||||||
"fr",
|
|
||||||
"de",
|
|
||||||
"it",
|
|
||||||
"nl",
|
|
||||||
"nb-NO",
|
|
||||||
"tr",
|
|
||||||
"pl",
|
|
||||||
"zh-CN",
|
|
||||||
"yue-Hant",
|
|
||||||
"ru",
|
|
||||||
];
|
|
||||||
@ -251,8 +251,8 @@ export default function CameraSettingsView({
|
|||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = t("documentTitle.camera");
|
document.title = "Camera Settings - Frigate";
|
||||||
}, [t]);
|
}, []);
|
||||||
|
|
||||||
if (!cameraConfig && !selectedCamera) {
|
if (!cameraConfig && !selectedCamera) {
|
||||||
return <ActivityIndicator />;
|
return <ActivityIndicator />;
|
||||||
|
|||||||
@ -299,10 +299,6 @@ export default function NotificationView({
|
|||||||
saveToConfig(values as NotificationSettingsValueType);
|
saveToConfig(values as NotificationSettingsValueType);
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
document.title = t("documentTitle.notifications");
|
|
||||||
}, [t]);
|
|
||||||
|
|
||||||
if (!("Notification" in window) || !window.isSecureContext) {
|
if (!("Notification" in window) || !window.isSecureContext) {
|
||||||
return (
|
return (
|
||||||
<div className="scrollbar-container order-last mb-10 mt-2 flex h-full w-full flex-col overflow-y-auto rounded-lg border-[1px] border-secondary-foreground bg-background_alt p-2 md:order-none md:mb-0 md:mr-2 md:mt-0">
|
<div className="scrollbar-container order-last mb-10 mt-2 flex h-full w-full flex-col overflow-y-auto rounded-lg border-[1px] border-secondary-foreground bg-background_alt p-2 md:order-none md:mb-0 md:mr-2 md:mt-0">
|
||||||
|
|||||||
@ -240,7 +240,7 @@ export default function GeneralMetrics({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.entries(stats.gpu_usages || {}).forEach(([key, stats]) => {
|
Object.entries(stats.gpu_usages || []).forEach(([key, stats]) => {
|
||||||
if (!(key in series)) {
|
if (!(key in series)) {
|
||||||
series[key] = { name: key, data: [] };
|
series[key] = { name: key, data: [] };
|
||||||
}
|
}
|
||||||
@ -316,7 +316,7 @@ export default function GeneralMetrics({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.entries(stats.gpu_usages || {}).forEach(([key, stats]) => {
|
Object.entries(stats.gpu_usages || []).forEach(([key, stats]) => {
|
||||||
if (!(key in series)) {
|
if (!(key in series)) {
|
||||||
series[key] = { name: key, data: [] };
|
series[key] = { name: key, data: [] };
|
||||||
}
|
}
|
||||||
@ -350,7 +350,7 @@ export default function GeneralMetrics({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.entries(stats.gpu_usages || {}).forEach(([key, stats]) => {
|
Object.entries(stats.gpu_usages || []).forEach(([key, stats]) => {
|
||||||
if (!(key in series)) {
|
if (!(key in series)) {
|
||||||
series[key] = { name: key, data: [] };
|
series[key] = { name: key, data: [] };
|
||||||
}
|
}
|
||||||
@ -386,7 +386,7 @@ export default function GeneralMetrics({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.entries(stats.npu_usages || {}).forEach(([key, stats]) => {
|
Object.entries(stats.npu_usages || []).forEach(([key, stats]) => {
|
||||||
if (!(key in series)) {
|
if (!(key in series)) {
|
||||||
series[key] = { name: key, data: [] };
|
series[key] = { name: key, data: [] };
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user