* frigate+ pane i18n fix

* catch more exceptions

* explore search result tooltip i18n fix

* i18n fix

* remove comments about deprecated strftime_fmt

* Catch producers exists but is None

* Formatting

* fix live camera view i18n

* Add default role config for proxy users

This allows users to specify a default role for users when using a proxy for auth. This can be useful for users who can't/don't want to define a header mapping for the remote-role header.

* update reference config and auth docs

* clarify face rec camera level config

* clarify auth docs

* Fix onnx not working with openvino

* Update openvino to fix failed npu plugin check

---------

Co-authored-by: Nicolas Mowen <nickmowen213@gmail.com>
This commit is contained in:
Josh Hawkins 2025-05-05 21:42:24 -05:00 committed by GitHub
parent 976863518b
commit 511542eaf8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 61 additions and 40 deletions

View File

@ -37,9 +37,9 @@ opencv-python-headless == 4.11.0.*
opencv-contrib-python == 4.11.0.*
scipy == 1.14.*
# OpenVino & ONNX
openvino == 2024.4.*
onnxruntime-openvino == 1.20.* ; platform_machine == 'x86_64'
onnxruntime == 1.20.* ; platform_machine == 'aarch64'
openvino == 2025.0.*
onnxruntime-openvino == 1.21.0 ; platform_machine == 'x86_64'
onnxruntime == 1.21.0 ; platform_machine == 'aarch64'
# Embeddings
transformers == 4.45.*
# Generative AI

View File

@ -13,5 +13,5 @@ nvidia-cudnn-cu12 == 9.5.0.*; platform_machine == 'x86_64'
nvidia-cufft-cu11==10.*; platform_machine == 'x86_64'
nvidia-cufft-cu12==11.*; platform_machine == 'x86_64'
onnx==1.16.*; platform_machine == 'x86_64'
onnxruntime-gpu==1.20.*; platform_machine == 'x86_64'
onnxruntime-gpu==1.21.0; platform_machine == 'x86_64'
protobuf==3.20.3; platform_machine == 'x86_64'

View File

@ -77,7 +77,7 @@ Changing the secret will invalidate current tokens.
Frigate can be configured to leverage features of common upstream authentication proxies such as Authelia, Authentik, oauth2_proxy, or traefik-forward-auth.
If you are leveraging the authentication of an upstream proxy, you likely want to disable Frigate's authentication. Optionally, if communication between the reverse proxy and Frigate is over an untrusted network, you should set an `auth_secret` in the `proxy` config and configure the proxy to send the secret value as a header named `X-Proxy-Secret`. Assuming this is an untrusted network, you will also want to [configure a real TLS certificate](tls.md) to ensure the traffic can't simply be sniffed to steal the secret.
If you are leveraging the authentication of an upstream proxy, you likely want to disable Frigate's authentication as there is no correspondence between users in Frigate's database and users authenticated via the proxy. Optionally, if communication between the reverse proxy and Frigate is over an untrusted network, you should set an `auth_secret` in the `proxy` config and configure the proxy to send the secret value as a header named `X-Proxy-Secret`. Assuming this is an untrusted network, you will also want to [configure a real TLS certificate](tls.md) to ensure the traffic can't simply be sniffed to steal the secret.
Here is an example of how to disable Frigate's authentication and also ensure the requests come only from your known proxy.
@ -109,6 +109,14 @@ proxy:
Frigate supports both `admin` and `viewer` roles (see below). When using port `8971`, Frigate validates these headers and subsequent requests use the headers `remote-user` and `remote-role` for authorization.
A default role can be provided. Any value in the mapped `role` header will override the default.
```yaml
proxy:
...
default_role: viewer
```
#### Port Considerations
**Authenticated Port (8971)**

View File

@ -47,7 +47,7 @@ face_recognition:
## Advanced Configuration
Fine-tune face recognition with these optional parameters:
Fine-tune face recognition with these optional parameters at the global level of your config. The only optional parameters that can be set at the camera level are `enabled` and `min_area`.
### Detection

View File

@ -78,16 +78,19 @@ proxy:
# Optional: Mapping for headers from upstream proxies. Only used if Frigate's auth
# is disabled.
# NOTE: Many authentication proxies pass a header downstream with the authenticated
# user name. Not all values are supported. It must be a whitelisted header.
# user name and role. Not all values are supported. It must be a whitelisted header.
# See the docs for more info.
header_map:
user: x-forwarded-user
role: x-forwarded-role
# Optional: Url for logging out a user. This sets the location of the logout url in
# the UI.
logout_url: /api/logout
# Optional: Auth secret that is checked against the X-Proxy-Secret header sent from
# the proxy. If not set, all requests are trusted regardless of origin.
auth_secret: None
# Optional: The default role to use for proxy auth. Must be "admin" or "viewer"
default_role: viewer
# Optional: Authentication configuration
auth:

View File

@ -74,7 +74,7 @@ def go2rtc_streams():
)
stream_data = r.json()
for data in stream_data.values():
for producer in data.get("producers", []):
for producer in data.get("producers") or []:
producer["url"] = clean_camera_user_pass(producer.get("url", ""))
return JSONResponse(content=stream_data)

View File

@ -261,14 +261,14 @@ def auth(request: Request):
role_header = proxy_config.header_map.role
role = (
request.headers.get(role_header, default="viewer")
request.headers.get(role_header, default=proxy_config.default_role)
if role_header
else "viewer"
else proxy_config.default_role
)
# if comma-separated with "admin", use "admin", else "viewer"
# if comma-separated with "admin", use "admin", else use default role
success_response.headers["remote-role"] = (
"admin" if role and "admin" in role else "viewer"
"admin" if role and "admin" in role else proxy_config.default_role
)
return success_response

View File

@ -30,3 +30,6 @@ class ProxyConfig(FrigateBaseModel):
default=None,
title="Secret value for proxy authentication.",
)
default_role: Optional[str] = Field(
default="viewer", title="Default role for proxy users."
)

View File

@ -74,7 +74,7 @@ class OnvifController:
"features": [],
"presets": {},
}
except ONVIFError as e:
except (Fault, ONVIFError, TransportError, Exception) as e:
logger.error(f"Failed to create ONVIF camera instance for {cam_name}: {e}")
# track initial failures
self.failed_cams[cam_name] = {
@ -100,7 +100,7 @@ class OnvifController:
# this will fire an exception if camera is not a ptz
capabilities = onvif.get_definition("ptz")
logger.debug(f"Onvif capabilities for {camera_name}: {capabilities}")
except (ONVIFError, Fault, TransportError) as e:
except (Fault, ONVIFError, TransportError, Exception) as e:
logger.error(
f"Unable to get Onvif capabilities for camera: {camera_name}: {e}"
)
@ -109,7 +109,7 @@ class OnvifController:
try:
profiles = await media.GetProfiles()
logger.debug(f"Onvif profiles for {camera_name}: {profiles}")
except (ONVIFError, Fault, TransportError) as e:
except (Fault, ONVIFError, TransportError, Exception) as e:
logger.error(
f"Unable to get Onvif media profiles for camera: {camera_name}: {e}"
)
@ -263,7 +263,7 @@ class OnvifController:
# setup existing presets
try:
presets: list[dict] = await ptz.GetPresets({"ProfileToken": profile.token})
except ONVIFError as e:
except (Fault, ONVIFError, TransportError, Exception) as e:
logger.warning(f"Unable to get presets from camera: {camera_name}: {e}")
presets = []
@ -392,7 +392,7 @@ class OnvifController:
try:
asyncio.run(self.cams[camera_name]["ptz"].ContinuousMove(move_request))
except ONVIFError as e:
except (Fault, ONVIFError, TransportError, Exception) as e:
logger.warning(f"Onvif sending move request to {camera_name} failed: {e}")
def _move_relative(self, camera_name: str, pan, tilt, zoom, speed) -> None:
@ -593,7 +593,7 @@ class OnvifController:
self._zoom(camera_name, command)
else:
self._move(camera_name, command)
except ONVIFError as e:
except (Fault, ONVIFError, TransportError, Exception) as e:
logger.error(f"Unable to handle onvif command: {e}")
async def get_camera_info(self, camera_name: str) -> dict[str, any]:

View File

@ -340,7 +340,6 @@ def get_ort_providers(
providers.append(provider)
options.append(
{
"arena_extend_strategy": "kSameAsRequested",
"cache_dir": os.path.join(MODEL_CACHE_DIR, "openvino/ort"),
"device_type": device,
}

View File

@ -33,5 +33,6 @@
},
"selected_one": "{{count}} selected",
"selected_other": "{{count}} selected",
"camera": "Camera"
"camera": "Camera",
"detected": "detected"
}

View File

@ -190,6 +190,7 @@
"trackedObjectsCount_one": "{{count}} tracked object ",
"trackedObjectsCount_other": "{{count}} tracked objects ",
"searchResult": {
"tooltip": "Matched {{type}} at {{confidence}}%",
"deleteTrackedObject": {
"toast": {
"success": "Tracked object deleted successfully.",

View File

@ -229,7 +229,7 @@ export function AnimatedEventCard({
.map((text) => text.charAt(0).toUpperCase() + text.substring(1))
.sort()
.join(", ")
.replaceAll("-verified", "")} detected`}
.replaceAll("-verified", "")} ` + t("detected")}
</TooltipContent>
</Tooltip>
);

View File

@ -1167,9 +1167,9 @@ export function ObjectSnapshotTab({
ns="components/dialog"
values={{
untranslatedLabel: search?.label,
translatedLabel: t(
"filter.label." + search?.label,
),
translatedLabel: t(search?.label, {
ns: "objects",
}),
}}
>
explore.plus.review.question.ask_full

View File

@ -28,9 +28,6 @@ export const getNowYesterdayInLong = (): number => {
* The `timezone` option allows you to specify a specific timezone for the output, otherwise the user's browser timezone will be used.
* The `use12hour` option allows you to display time in a 12-hour format if true, and 24-hour format if false.
* The `dateStyle` and `timeStyle` options allow you to specify pre-defined formats for displaying the date and time.
* The `strftime_fmt` option allows you to specify a custom format using the strftime syntax.
*
* If both `strftime_fmt` and `dateStyle`/`timeStyle` are provided, `strftime_fmt` takes precedence.
*
* @param unixTimestamp The Unix timestamp to format
* @param config An object containing the configuration options for date/time display

View File

@ -531,11 +531,9 @@ export default function LiveCameraView({
Icon={mic ? FaMicrophone : FaMicrophoneSlash}
isActive={mic}
title={
(mic
? t("button.disable", { ns: "common" })
: t("button.enable", { ns: "common" })) +
" " +
t("button.twoWayTalk", { ns: "common" })
mic
? t("twoWayTalk.disable", { ns: "views/live" })
: t("twoWayTalk.enable", { ns: "views/live" })
}
onClick={() => {
setMic(!mic);
@ -553,11 +551,9 @@ export default function LiveCameraView({
Icon={audio ? GiSpeaker : GiSpeakerOff}
isActive={audio ?? false}
title={
(audio
? t("button.disable", { ns: "common" })
: t("button.enable", { ns: "common" })) +
" " +
t("button.cameraAudio", { ns: "common" })
audio
? t("cameraAudio.disable", { ns: "views/live" })
: t("cameraAudio.enable", { ns: "views/live" })
}
onClick={() => setAudio(!audio)}
disabled={!cameraEnabled}

View File

@ -31,7 +31,7 @@ import {
import Chip from "@/components/indicators/Chip";
import { TooltipPortal } from "@radix-ui/react-tooltip";
import SearchActionGroup from "@/components/filter/SearchActionGroup";
import { useTranslation } from "react-i18next";
import { Trans, useTranslation } from "react-i18next";
type SearchViewProps = {
search: string;
@ -608,8 +608,21 @@ export default function SearchView({
</TooltipTrigger>
<TooltipPortal>
<TooltipContent>
Matched {value.search_source} at{" "}
{zScoreToConfidence(value.search_distance)}%
<Trans
ns="views/explore"
values={{
type: t(
"filter.searchType." +
value.search_source,
{ ns: "views/search" },
),
confidence: zScoreToConfidence(
value.search_distance,
),
}}
>
searchResult.tooltip
</Trans>
</TooltipContent>
</TooltipPortal>
</Tooltip>