diff --git a/.cspell/frigate-dictionary.txt b/.cspell/frigate-dictionary.txt index dbab9600e..77e4ede62 100644 --- a/.cspell/frigate-dictionary.txt +++ b/.cspell/frigate-dictionary.txt @@ -108,7 +108,6 @@ imagestream imdecode imencode imread -imutils imwrite interp iostat diff --git a/.github/DISCUSSION_TEMPLATE/camera-support.yml b/.github/DISCUSSION_TEMPLATE/camera-support.yml index a76fd5caf..521d65ded 100644 --- a/.github/DISCUSSION_TEMPLATE/camera-support.yml +++ b/.github/DISCUSSION_TEMPLATE/camera-support.yml @@ -73,7 +73,7 @@ body: attributes: label: Operating system options: - - HassOS + - Home Assistant OS - Debian - Other Linux - Proxmox @@ -87,7 +87,7 @@ body: attributes: label: Install method options: - - HassOS Addon + - Home Assistant Add-on - Docker Compose - Docker CLI - Proxmox via Docker diff --git a/.github/DISCUSSION_TEMPLATE/config-support.yml b/.github/DISCUSSION_TEMPLATE/config-support.yml index 4934d7936..575f7f640 100644 --- a/.github/DISCUSSION_TEMPLATE/config-support.yml +++ b/.github/DISCUSSION_TEMPLATE/config-support.yml @@ -59,7 +59,7 @@ body: attributes: label: Operating system options: - - HassOS + - Home Assistant OS - Debian - Other Linux - Proxmox @@ -73,7 +73,7 @@ body: attributes: label: Install method options: - - HassOS Addon + - Home Assistant Add-on - Docker Compose - Docker CLI - Proxmox via Docker diff --git a/.github/DISCUSSION_TEMPLATE/detector-support.yml b/.github/DISCUSSION_TEMPLATE/detector-support.yml index 442b2527a..fb994500f 100644 --- a/.github/DISCUSSION_TEMPLATE/detector-support.yml +++ b/.github/DISCUSSION_TEMPLATE/detector-support.yml @@ -53,7 +53,7 @@ body: attributes: label: Install method options: - - HassOS Addon + - Home Assistant Add-on - Docker Compose - Docker CLI - Proxmox via Docker diff --git a/.github/DISCUSSION_TEMPLATE/general-support.yml b/.github/DISCUSSION_TEMPLATE/general-support.yml index 7af52bdf5..0b9f225b6 100644 --- a/.github/DISCUSSION_TEMPLATE/general-support.yml +++ b/.github/DISCUSSION_TEMPLATE/general-support.yml @@ -73,7 +73,7 @@ body: attributes: label: Install method options: - - HassOS Addon + - Home Assistant Add-on - Docker Compose - Docker CLI - Proxmox via Docker diff --git a/.github/DISCUSSION_TEMPLATE/hardware-acceleration-support.yml b/.github/DISCUSSION_TEMPLATE/hardware-acceleration-support.yml index 43fb3503b..861156696 100644 --- a/.github/DISCUSSION_TEMPLATE/hardware-acceleration-support.yml +++ b/.github/DISCUSSION_TEMPLATE/hardware-acceleration-support.yml @@ -69,7 +69,7 @@ body: attributes: label: Install method options: - - HassOS Addon + - Home Assistant Add-on - Docker Compose - Docker CLI - Proxmox via Docker diff --git a/.github/DISCUSSION_TEMPLATE/report-a-bug.yml b/.github/DISCUSSION_TEMPLATE/report-a-bug.yml index dba6d695e..21e4f746f 100644 --- a/.github/DISCUSSION_TEMPLATE/report-a-bug.yml +++ b/.github/DISCUSSION_TEMPLATE/report-a-bug.yml @@ -97,7 +97,7 @@ body: attributes: label: Operating system options: - - HassOS + - Home Assistant OS - Debian - Other Linux - Proxmox @@ -111,7 +111,7 @@ body: attributes: label: Install method options: - - HassOS Addon + - Home Assistant Add-on - Docker Compose - Docker CLI validations: diff --git a/docker-compose.yml b/docker-compose.yml index 2d905d385..db63297d5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,8 +1,8 @@ -version: "3" services: devcontainer: container_name: frigate-devcontainer - # add groups from host for render, plugdev, video + # Check host system's actual render/video/plugdev group IDs with 'getent group render', 'getent group video', and 'getent group plugdev' + # Must add these exact IDs in container's group_add section or OpenVINO GPU acceleration will fail group_add: - "109" # render - "110" # render @@ -24,8 +24,8 @@ services: # capabilities: [gpu] environment: YOLO_MODELS: "" - devices: - - /dev/bus/usb:/dev/bus/usb + # devices: + # - /dev/bus/usb:/dev/bus/usb # Uncomment for Google Coral USB # - /dev/dri:/dev/dri # for intel hwaccel, needs to be updated for your hardware volumes: - .:/workspace/frigate:cached @@ -33,9 +33,10 @@ services: - /etc/localtime:/etc/localtime:ro - ./config:/config - ./debug:/media/frigate - - /dev/bus/usb:/dev/bus/usb + # - /dev/bus/usb:/dev/bus/usb # Uncomment for Google Coral USB mqtt: container_name: mqtt - image: eclipse-mosquitto:1.6 + image: eclipse-mosquitto:2.0 + command: mosquitto -c /mosquitto-no-auth.conf # enable no-auth mode ports: - - "1883:1883" \ No newline at end of file + - "1883:1883" diff --git a/docker/main/Dockerfile b/docker/main/Dockerfile index 2a7d388bc..a71250813 100644 --- a/docker/main/Dockerfile +++ b/docker/main/Dockerfile @@ -78,8 +78,9 @@ COPY docker/main/requirements-ov.txt /requirements-ov.txt RUN apt-get -qq update \ && apt-get -qq install -y wget python3 python3-dev python3-distutils gcc pkg-config libhdf5-dev \ && wget -q https://bootstrap.pypa.io/get-pip.py -O get-pip.py \ + && sed -i 's/args.append("setuptools")/args.append("setuptools==77.0.3")/' get-pip.py \ && python3 get-pip.py "pip" \ - && pip install -r /requirements-ov.txt + && pip3 install -r /requirements-ov.txt # Get OpenVino Model RUN --mount=type=bind,source=docker/main/build_ov_model.py,target=/build_ov_model.py \ @@ -172,6 +173,7 @@ RUN apt-get -qq update \ RUN update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.11 1 RUN wget -q https://bootstrap.pypa.io/get-pip.py -O get-pip.py \ + && sed -i 's/args.append("setuptools")/args.append("setuptools==77.0.3")/' get-pip.py \ && python3 get-pip.py "pip" COPY docker/main/requirements.txt /requirements.txt @@ -235,6 +237,7 @@ ENV DEFAULT_FFMPEG_VERSION="7.0" ENV INCLUDED_FFMPEG_VERSIONS="${DEFAULT_FFMPEG_VERSION}:5.0" RUN wget -q https://bootstrap.pypa.io/get-pip.py -O get-pip.py \ + && sed -i 's/args.append("setuptools")/args.append("setuptools==77.0.3")/' get-pip.py \ && python3 get-pip.py "pip" RUN --mount=type=bind,from=wheels,source=/wheels,target=/deps/wheels \ @@ -262,7 +265,7 @@ HEALTHCHECK --start-period=300s --start-interval=5s --interval=15s --timeout=5s # Frigate deps with Node.js and NPM for devcontainer FROM deps AS devcontainer -# Do not start the actual Frigate service on devcontainer as it will be started by VSCode +# Do not start the actual Frigate service on devcontainer as it will be started by VS Code # But start a fake service for simulating the logs COPY docker/main/fake_frigate_run /etc/s6-overlay/s6-rc.d/frigate/run diff --git a/docker/main/build_nginx.sh b/docker/main/build_nginx.sh index 2591810e3..606682665 100755 --- a/docker/main/build_nginx.sh +++ b/docker/main/build_nginx.sh @@ -2,7 +2,7 @@ set -euxo pipefail -NGINX_VERSION="1.25.3" +NGINX_VERSION="1.27.4" VOD_MODULE_VERSION="1.31" SECURE_TOKEN_MODULE_VERSION="1.5" SET_MISC_MODULE_VERSION="v0.33" diff --git a/docker/main/requirements-wheels.txt b/docker/main/requirements-wheels.txt index 9368cabcd..4ab7e03e6 100644 --- a/docker/main/requirements-wheels.txt +++ b/docker/main/requirements-wheels.txt @@ -7,7 +7,6 @@ starlette-context == 0.3.6 fastapi == 0.115.* uvicorn == 0.30.* slowapi == 0.1.* -imutils == 0.5.* joserfc == 1.0.* pathvalidate == 3.2.* markupsafe == 3.0.* diff --git a/docker/main/rootfs/etc/s6-overlay/s6-rc.d/frigate/run b/docker/main/rootfs/etc/s6-overlay/s6-rc.d/frigate/run index f764fd6b0..a3a34e4f6 100755 --- a/docker/main/rootfs/etc/s6-overlay/s6-rc.d/frigate/run +++ b/docker/main/rootfs/etc/s6-overlay/s6-rc.d/frigate/run @@ -9,39 +9,6 @@ set -o errexit -o nounset -o pipefail # Tell S6-Overlay not to restart this service s6-svc -O . -function migrate_db_path() { - # Find config file in yaml or yml, but prefer yaml - local config_file="${CONFIG_FILE:-"/config/config.yml"}" - local config_file_yaml="${config_file//.yml/.yaml}" - if [[ -f "${config_file_yaml}" ]]; then - config_file="${config_file_yaml}" - elif [[ ! -f "${config_file}" ]]; then - # Frigate will create the config file on startup - return 0 - fi - unset config_file_yaml - - # Use yq to check if database.path is set - local user_db_path - user_db_path=$(yq eval '.database.path' "${config_file}") - - if [[ "${user_db_path}" == "null" ]]; then - local previous_db_path="/media/frigate/frigate.db" - local new_db_dir="/config" - if [[ -f "${previous_db_path}" ]]; then - if mountpoint --quiet "${new_db_dir}"; then - # /config is a mount point, move the db - echo "[INFO] Moving db from '${previous_db_path}' to the '${new_db_dir}' dir..." - # Move all files that starts with frigate.db to the new directory - mv -vf "${previous_db_path}"* "${new_db_dir}" - else - echo "[ERROR] Trying to migrate the db path from '${previous_db_path}' to the '${new_db_dir}' dir, but '${new_db_dir}' is not a mountpoint, please mount the '${new_db_dir}' dir" - return 1 - fi - fi - fi -} - function set_libva_version() { local ffmpeg_path ffmpeg_path=$(python3 /usr/local/ffmpeg/get_ffmpeg_path.py) @@ -50,8 +17,8 @@ function set_libva_version() { } echo "[INFO] Preparing Frigate..." -migrate_db_path set_libva_version + echo "[INFO] Starting Frigate..." cd /opt/frigate || echo "[ERROR] Failed to change working directory to /opt/frigate" diff --git a/docker/main/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/dependencies.d/base b/docker/main/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/dependencies.d/prepare similarity index 100% rename from docker/main/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/dependencies.d/base rename to docker/main/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/dependencies.d/prepare diff --git a/docker/main/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/run b/docker/main/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/run index 2c3a7ab6f..46bc3175f 100755 --- a/docker/main/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/run +++ b/docker/main/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/run @@ -61,7 +61,7 @@ if [[ ! -f "/dev/shm/go2rtc.yaml" ]]; then echo "[INFO] Preparing new go2rtc config..." if [[ -n "${SUPERVISOR_TOKEN:-}" ]]; then - # Running as a Home Assistant add-on, infer the IP address and port + # Running as a Home Assistant Add-on, infer the IP address and port get_ip_and_port_from_supervisor fi diff --git a/docker/main/rootfs/etc/s6-overlay/s6-rc.d/prepare/dependencies.d/base b/docker/main/rootfs/etc/s6-overlay/s6-rc.d/prepare/dependencies.d/base new file mode 100644 index 000000000..e69de29bb diff --git a/docker/main/rootfs/etc/s6-overlay/s6-rc.d/prepare/run b/docker/main/rootfs/etc/s6-overlay/s6-rc.d/prepare/run new file mode 100755 index 000000000..0460cd2b4 --- /dev/null +++ b/docker/main/rootfs/etc/s6-overlay/s6-rc.d/prepare/run @@ -0,0 +1,142 @@ +#!/command/with-contenv bash +# shellcheck shell=bash +# Do preparation tasks before starting the main services + +set -o errexit -o nounset -o pipefail + +function migrate_addon_config_dir() { + local home_assistant_config_dir="/homeassistant" + + if ! mountpoint --quiet "${home_assistant_config_dir}"; then + # Not running as a Home Assistant Add-on + return 0 + fi + + local config_dir="/config" + local new_config_file="${config_dir}/config.yml" + local new_config_file_yaml="${new_config_file//.yml/.yaml}" + if [[ -f "${new_config_file_yaml}" || -f "${new_config_file}" ]]; then + # Already migrated + return 0 + fi + + local old_config_file="${home_assistant_config_dir}/frigate.yml" + local old_config_file_yaml="${old_config_file//.yml/.yaml}" + if [[ -f "${old_config_file}" ]]; then + : + elif [[ -f "${old_config_file_yaml}" ]]; then + old_config_file="${old_config_file_yaml}" + new_config_file="${new_config_file_yaml}" + else + # Nothing to migrate + return 0 + fi + unset old_config_file_yaml new_config_file_yaml + + echo "[INFO] Starting migration from Home Assistant config dir to Add-on config dir..." >&2 + + local db_path + db_path=$(yq -r '.database.path' "${old_config_file}") + if [[ "${db_path}" == "null" ]]; then + db_path="${config_dir}/frigate.db" + fi + if [[ "${db_path}" == "${config_dir}/"* ]]; then + # replace /config/ prefix with /homeassistant/ + local old_db_path="${home_assistant_config_dir}/${db_path:8}" + + if [[ -f "${old_db_path}" ]]; then + local new_db_dir + new_db_dir="$(dirname "${db_path}")" + echo "[INFO] Migrating database from '${old_db_path}' to '${new_db_dir}' dir..." >&2 + mkdir -vp "${new_db_dir}" + mv -vf "${old_db_path}" "${new_db_dir}" + local db_file + for db_file in "${old_db_path}"-shm "${old_db_path}"-wal; do + if [[ -f "${db_file}" ]]; then + mv -vf "${db_file}" "${new_db_dir}" + fi + done + unset db_file + fi + fi + + local config_entry + for config_entry in .model.path .model.labelmap_path .ffmpeg.path .mqtt.tls_ca_certs .mqtt.tls_client_cert .mqtt.tls_client_key; do + local config_entry_path + config_entry_path=$(yq -r "${config_entry}" "${old_config_file}") + if [[ "${config_entry_path}" == "${config_dir}/"* ]]; then + # replace /config/ prefix with /homeassistant/ + local old_config_entry_path="${home_assistant_config_dir}/${config_entry_path:8}" + + if [[ -f "${old_config_entry_path}" ]]; then + local new_config_entry_entry + new_config_entry_entry="$(dirname "${config_entry_path}")" + echo "[INFO] Migrating ${config_entry} from '${old_config_entry_path}' to '${config_entry_path}'..." >&2 + mkdir -vp "${new_config_entry_entry}" + mv -vf "${old_config_entry_path}" "${config_entry_path}" + fi + fi + done + + local old_model_cache_path="${home_assistant_config_dir}/model_cache" + if [[ -d "${old_model_cache_path}" ]]; then + echo "[INFO] Migrating '${old_model_cache_path}' to '${config_dir}'..." >&2 + mv -f "${old_model_cache_path}" "${config_dir}" + fi + + echo "[INFO] Migrating other files from '${home_assistant_config_dir}' to '${config_dir}'..." >&2 + local file + for file in .exports .jwt_secret .timeline .vacuum go2rtc; do + file="${home_assistant_config_dir}/${file}" + if [[ -f "${file}" ]]; then + mv -vf "${file}" "${config_dir}" + fi + done + + echo "[INFO] Migrating config file from '${old_config_file}' to '${new_config_file}'..." >&2 + mv -vf "${old_config_file}" "${new_config_file}" + + echo "[INFO] Migration from Home Assistant config dir to Add-on config dir completed." >&2 +} + +function migrate_db_from_media_to_config() { + # Find config file in yml or yaml, but prefer yml + local config_file="${CONFIG_FILE:-"/config/config.yml"}" + local config_file_yaml="${config_file//.yml/.yaml}" + if [[ -f "${config_file}" ]]; then + : + elif [[ -f "${config_file_yaml}" ]]; then + config_file="${config_file_yaml}" + else + # Frigate will create the config file on startup + return 0 + fi + unset config_file_yaml + + local user_db_path + user_db_path=$(yq -r '.database.path' "${config_file}") + if [[ "${user_db_path}" == "null" ]]; then + local old_db_path="/media/frigate/frigate.db" + local new_db_dir="/config" + if [[ -f "${old_db_path}" ]]; then + echo "[INFO] Migrating database from '${old_db_path}' to '${new_db_dir}' dir..." >&2 + if mountpoint --quiet "${new_db_dir}"; then + # /config is a mount point, move the db + mv -vf "${old_db_path}" "${new_db_dir}" + local db_file + for db_file in "${old_db_path}"-shm "${old_db_path}"-wal; do + if [[ -f "${db_file}" ]]; then + mv -vf "${db_file}" "${new_db_dir}" + fi + done + unset db_file + else + echo "[ERROR] Trying to migrate the database path from '${old_db_path}' to '${new_db_dir}' dir, but '${new_db_dir}' is not a mountpoint, please mount the '${new_db_dir}' dir" >&2 + return 1 + fi + fi + fi +} + +migrate_addon_config_dir +migrate_db_from_media_to_config diff --git a/docker/main/rootfs/etc/s6-overlay/s6-rc.d/prepare/type b/docker/main/rootfs/etc/s6-overlay/s6-rc.d/prepare/type new file mode 100644 index 000000000..bdd22a185 --- /dev/null +++ b/docker/main/rootfs/etc/s6-overlay/s6-rc.d/prepare/type @@ -0,0 +1 @@ +oneshot diff --git a/docker/main/rootfs/etc/s6-overlay/s6-rc.d/prepare/up b/docker/main/rootfs/etc/s6-overlay/s6-rc.d/prepare/up new file mode 100644 index 000000000..ea17af548 --- /dev/null +++ b/docker/main/rootfs/etc/s6-overlay/s6-rc.d/prepare/up @@ -0,0 +1 @@ +/etc/s6-overlay/s6-rc.d/prepare/run diff --git a/docker/main/rootfs/usr/local/ffmpeg/get_ffmpeg_path.py b/docker/main/rootfs/usr/local/ffmpeg/get_ffmpeg_path.py index ed7f6a891..3de7d9f4a 100644 --- a/docker/main/rootfs/usr/local/ffmpeg/get_ffmpeg_path.py +++ b/docker/main/rootfs/usr/local/ffmpeg/get_ffmpeg_path.py @@ -1,5 +1,4 @@ import json -import os import sys from ruamel.yaml import YAML @@ -9,17 +8,13 @@ from frigate.const import ( DEFAULT_FFMPEG_VERSION, INCLUDED_FFMPEG_VERSIONS, ) +from frigate.util.config import find_config_file sys.path.remove("/opt/frigate") yaml = YAML() -config_file = os.environ.get("CONFIG_FILE", "/config/config.yml") - -# Check if we can use .yaml instead of .yml -config_file_yaml = config_file.replace(".yml", ".yaml") -if os.path.isfile(config_file_yaml): - config_file = config_file_yaml +config_file = find_config_file() try: with open(config_file) as f: diff --git a/docker/main/rootfs/usr/local/go2rtc/create_config.py b/docker/main/rootfs/usr/local/go2rtc/create_config.py index d7c21c7f7..ac44f1fe4 100644 --- a/docker/main/rootfs/usr/local/go2rtc/create_config.py +++ b/docker/main/rootfs/usr/local/go2rtc/create_config.py @@ -15,6 +15,7 @@ from frigate.const import ( LIBAVFORMAT_VERSION_MAJOR, ) from frigate.ffmpeg_presets import parse_preset_hardware_acceleration_encode +from frigate.util.config import find_config_file sys.path.remove("/opt/frigate") @@ -29,12 +30,7 @@ if os.path.isdir("/run/secrets"): Path(os.path.join("/run/secrets", secret_file)).read_text().strip() ) -config_file = os.environ.get("CONFIG_FILE", "/config/config.yml") - -# Check if we can use .yaml instead of .yml -config_file_yaml = config_file.replace(".yml", ".yaml") -if os.path.isfile(config_file_yaml): - config_file = config_file_yaml +config_file = find_config_file() try: with open(config_file) as f: diff --git a/docker/main/rootfs/usr/local/nginx/conf/nginx.conf b/docker/main/rootfs/usr/local/nginx/conf/nginx.conf index 64d6396b2..4abfd3587 100644 --- a/docker/main/rootfs/usr/local/nginx/conf/nginx.conf +++ b/docker/main/rootfs/usr/local/nginx/conf/nginx.conf @@ -30,7 +30,7 @@ http { gzip on; gzip_comp_level 6; - gzip_types text/plain text/css application/json application/x-javascript application/javascript text/javascript image/svg+xml image/x-icon image/bmp image/png image/gif image/jpeg image/jpg; + gzip_types text/plain text/css application/json application/x-javascript application/javascript text/javascript image/svg+xml image/x-icon image/bmp; gzip_proxied no-cache no-store private expired auth; gzip_vary on; @@ -300,6 +300,11 @@ http { add_header Cache-Control "public"; } + location /locales/ { + access_log off; + add_header Cache-Control "public"; + } + location ~ ^/.*-([A-Za-z0-9]+)\.webmanifest$ { access_log off; expires 1y; @@ -308,8 +313,8 @@ http { proxy_set_header Accept-Encoding ""; sub_filter_once off; sub_filter_types application/json; - sub_filter '"start_url": "/"' '"start_url" : "$http_x_ingress_path"'; - sub_filter '"src": "/' '"src": "$http_x_ingress_path/'; + sub_filter '"start_url": "/BASE_PATH/"' '"start_url" : "$http_x_ingress_path/"'; + sub_filter '"src": "/BASE_PATH/' '"src": "$http_x_ingress_path/'; } sub_filter 'href="/BASE_PATH/' 'href="$http_x_ingress_path/'; @@ -317,6 +322,7 @@ http { sub_filter '"/BASE_PATH/dist/' '"$http_x_ingress_path/dist/'; sub_filter '"/BASE_PATH/js/' '"$http_x_ingress_path/js/'; sub_filter '"/BASE_PATH/assets/' '"$http_x_ingress_path/assets/'; + sub_filter '"/BASE_PATH/locales/' '"$http_x_ingress_path/locales/'; sub_filter '"/BASE_PATH/monacoeditorwork/' '"$http_x_ingress_path/assets/'; sub_filter 'return"/BASE_PATH/"' 'return window.baseUrl'; sub_filter '
' ''; diff --git a/docker/main/rootfs/usr/local/nginx/get_tls_settings.py b/docker/main/rootfs/usr/local/nginx/get_tls_settings.py index f1a4c85de..2ababa282 100644 --- a/docker/main/rootfs/usr/local/nginx/get_tls_settings.py +++ b/docker/main/rootfs/usr/local/nginx/get_tls_settings.py @@ -1,18 +1,18 @@ """Prints the tls config as json to stdout.""" import json -import os +import sys from ruamel.yaml import YAML +sys.path.insert(0, "/opt/frigate") +from frigate.util.config import find_config_file + +sys.path.remove("/opt/frigate") + yaml = YAML() -config_file = os.environ.get("CONFIG_FILE", "/config/config.yml") - -# Check if we can use .yaml instead of .yml -config_file_yaml = config_file.replace(".yml", ".yaml") -if os.path.isfile(config_file_yaml): - config_file = config_file_yaml +config_file = find_config_file() try: with open(config_file) as f: diff --git a/docker/rockchip/conv2rknn.py b/docker/rockchip/conv2rknn.py index 4f4a315e1..4880d9868 100644 --- a/docker/rockchip/conv2rknn.py +++ b/docker/rockchip/conv2rknn.py @@ -14,7 +14,7 @@ try: with open("/config/conv2rknn.yaml", "r") as config_file: configuration = yaml.safe_load(config_file) except FileNotFoundError: - raise Exception("Please place a config.yaml file in /config/conv2rknn.yaml") + raise Exception("Please place a config file at /config/conv2rknn.yaml") if configuration["config"] != None: rknn_config = configuration["config"] diff --git a/docker/rocm/Dockerfile b/docker/rocm/Dockerfile index 78f91b96f..d04e93df3 100644 --- a/docker/rocm/Dockerfile +++ b/docker/rocm/Dockerfile @@ -39,6 +39,7 @@ WORKDIR /opt/frigate COPY --from=rootfs / / RUN wget -q https://bootstrap.pypa.io/get-pip.py -O get-pip.py \ + && sed -i 's/args.append("setuptools")/args.append("setuptools==77.0.3")/' get-pip.py \ && python3 get-pip.py "pip" --break-system-packages RUN python3 -m pip config set global.break-system-packages true diff --git a/docker/tensorrt/Dockerfile.arm64 b/docker/tensorrt/Dockerfile.arm64 index 5d5d5d939..6e2e0280f 100644 --- a/docker/tensorrt/Dockerfile.arm64 +++ b/docker/tensorrt/Dockerfile.arm64 @@ -9,9 +9,9 @@ ARG DEBIAN_FRONTEND # Add deadsnakes PPA for python3.11 RUN apt-get -qq update && \ - apt-get -qq install -y --no-install-recommends \ - software-properties-common \ - && add-apt-repository ppa:deadsnakes/ppa + apt-get -qq install -y --no-install-recommends \ + software-properties-common \ + && add-apt-repository ppa:deadsnakes/ppa # Use a separate container to build wheels to prevent build dependencies in final image RUN apt-get -qq update \ @@ -24,6 +24,7 @@ RUN apt-get -qq update \ RUN update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.11 1 RUN wget -q https://bootstrap.pypa.io/get-pip.py -O get-pip.py \ + && sed -i 's/args.append("setuptools")/args.append("setuptools==77.0.3")/' get-pip.py \ && python3 get-pip.py "pip" FROM build-wheels AS trt-wheels diff --git a/docker/tensorrt/Dockerfile.base b/docker/tensorrt/Dockerfile.base index 4305f1d74..79a7f3c98 100644 --- a/docker/tensorrt/Dockerfile.base +++ b/docker/tensorrt/Dockerfile.base @@ -21,7 +21,20 @@ RUN --mount=type=bind,source=docker/tensorrt/detector/tensorrt_libyolo.sh,target RUN mkdir -p /usr/local/cuda-deps RUN if [ "$TARGETARCH" = "amd64" ]; then \ cp /usr/local/cuda-12.3/targets/x86_64-linux/lib/libcurand.so.* /usr/local/cuda-deps/ && \ - cp /usr/local/cuda-12.3/targets/x86_64-linux/lib/libnvrtc.so.* /usr/local/cuda-deps/ ; \ + cp /usr/local/cuda-12.3/targets/x86_64-linux/lib/libnvrtc.so.* /usr/local/cuda-deps/ && \ + cd /usr/local/cuda-deps/ && \ + for lib in libnvrtc.so.*; do \ + if [[ "$lib" =~ libnvrtc.so\.([0-9]+\.[0-9]+\.[0-9]+) ]]; then \ + version="${BASH_REMATCH[1]}"; \ + ln -sf "libnvrtc.so.$version" libnvrtc.so; \ + fi; \ + done && \ + for lib in libcurand.so.*; do \ + if [[ "$lib" =~ libcurand.so\.([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+) ]]; then \ + version="${BASH_REMATCH[1]}"; \ + ln -sf "libcurand.so.$version" libcurand.so; \ + fi; \ + done; \ fi # Frigate w/ TensorRT Support as separate image diff --git a/docker/tensorrt/detector/rootfs/etc/ld.so.conf.d/cuda_tensorrt.conf b/docker/tensorrt/detector/rootfs/etc/ld.so.conf.d/cuda_tensorrt.conf index 72eec56e0..f66af7dc6 100644 --- a/docker/tensorrt/detector/rootfs/etc/ld.so.conf.d/cuda_tensorrt.conf +++ b/docker/tensorrt/detector/rootfs/etc/ld.so.conf.d/cuda_tensorrt.conf @@ -1,8 +1,7 @@ /usr/local/lib /usr/local/cuda +/usr/local/lib/python3.11/dist-packages/tensorrt /usr/local/lib/python3.11/dist-packages/nvidia/cudnn/lib /usr/local/lib/python3.11/dist-packages/nvidia/cuda_runtime/lib /usr/local/lib/python3.11/dist-packages/nvidia/cublas/lib -/usr/local/lib/python3.11/dist-packages/nvidia/cuda_nvrtc/lib -/usr/local/lib/python3.11/dist-packages/tensorrt /usr/local/lib/python3.11/dist-packages/nvidia/cufft/lib \ No newline at end of file diff --git a/docs/docs/configuration/advanced.md b/docs/docs/configuration/advanced.md index 1e128e0e3..818440fae 100644 --- a/docs/docs/configuration/advanced.md +++ b/docs/docs/configuration/advanced.md @@ -44,7 +44,7 @@ go2rtc: ### `environment_vars` -This section can be used to set environment variables for those unable to modify the environment of the container (ie. within HassOS) +This section can be used to set environment variables for those unable to modify the environment of the container, like within Home Assistant OS. Example: diff --git a/docs/docs/configuration/authentication.md b/docs/docs/configuration/authentication.md index 36994381d..129547d1b 100644 --- a/docs/docs/configuration/authentication.md +++ b/docs/docs/configuration/authentication.md @@ -43,13 +43,13 @@ Restarting Frigate will reset the rate limits. If you are running Frigate behind a proxy, you will want to set `trusted_proxies` or these rate limits will apply to the upstream proxy IP address. This means that a brute force attack will rate limit login attempts from other devices and could temporarily lock you out of your instance. In order to ensure rate limits only apply to the actual IP address where the requests are coming from, you will need to list the upstream networks that you want to trust. These trusted proxies are checked against the `X-Forwarded-For` header when looking for the IP address where the request originated. -If you are running a reverse proxy in the same docker compose file as Frigate, here is an example of how your auth config might look: +If you are running a reverse proxy in the same Docker Compose file as Frigate, here is an example of how your auth config might look: ```yaml auth: failed_login_rate_limit: "1/second;5/minute;20/hour" trusted_proxies: - - 172.18.0.0/16 # <---- this is the subnet for the internal docker compose network + - 172.18.0.0/16 # <---- this is the subnet for the internal Docker Compose network ``` ## JWT Token Secret @@ -66,7 +66,7 @@ Frigate looks for a JWT token secret in the following order: 1. An environment variable named `FRIGATE_JWT_SECRET` 2. A docker secret named `FRIGATE_JWT_SECRET` in `/run/secrets/` -3. A `jwt_secret` option from the Home Assistant Addon options +3. A `jwt_secret` option from the Home Assistant Add-on options 4. A `.jwt_secret` file in the config directory If no secret is found on startup, Frigate generates one and stores it in a `.jwt_secret` file in the config directory. diff --git a/docs/docs/configuration/bird_classification.md b/docs/docs/configuration/bird_classification.md new file mode 100644 index 000000000..398729290 --- /dev/null +++ b/docs/docs/configuration/bird_classification.md @@ -0,0 +1,31 @@ +--- +id: bird_classification +title: Bird Classification +--- + +Bird classification identifies known birds using a quantized Tensorflow model. When a known bird is recognized, its common name will be added as a `sub_label`. This information is included in the UI, filters, as well as in notifications. + +## Minimum System Requirements + +Bird classification runs a lightweight tflite model on the CPU, there are no significantly different system requirements than running Frigate itself. + +## Model + +The classification model used is the MobileNet INat Bird Classification, [available identifiers can be found here.](https://raw.githubusercontent.com/google-coral/test_data/master/inat_bird_labels.txt) + +## Configuration + +Bird classification is disabled by default, it must be enabled in your config file before it can be used. Bird classification is a global configuration setting. + +```yaml +classification: + bird: + enabled: true +``` + +## Advanced Configuration + +Fine-tune bird classification with these optional parameters: + +- `threshold`: Classification confidence score required to set the sub label on the object. + - Default: `0.9`. diff --git a/docs/docs/configuration/birdseye.md b/docs/docs/configuration/birdseye.md index 2c9fbbdf4..d4bd1a15e 100644 --- a/docs/docs/configuration/birdseye.md +++ b/docs/docs/configuration/birdseye.md @@ -4,7 +4,7 @@ In addition to Frigate's Live camera dashboard, Birdseye allows a portable heads Birdseye can be viewed by adding the "Birdseye" camera to a Camera Group in the Web UI. Add a Camera Group by pressing the "+" icon on the Live page, and choose "Birdseye" as one of the cameras. -Birdseye can also be used in HomeAssistant dashboards, cast to media devices, etc. +Birdseye can also be used in Home Assistant dashboards, cast to media devices, etc. ## Birdseye Behavior diff --git a/docs/docs/configuration/face_recognition.md b/docs/docs/configuration/face_recognition.md index aac1be9b5..8dc4ac7b2 100644 --- a/docs/docs/configuration/face_recognition.md +++ b/docs/docs/configuration/face_recognition.md @@ -7,21 +7,26 @@ Face recognition identifies known individuals by matching detected faces with pr ## Model Requirements -Frigate has support for CV2 Local Binary Pattern Face Recognizer to recognize faces, which runs locally. A lightweight face landmark detection model is also used to align faces before running them through the face recognizer. +### Face Detection -Users running a Frigate+ model (or any custom model that natively detects faces) should ensure that `face` is added to the [list of objects to track](../plus/#available-label-types) either globally or for a specific camera. This will allow face detection to run at the same time as object detection and be more efficient. +When running a Frigate+ model (or any custom model that natively detects faces) should ensure that `face` is added to the [list of objects to track](../plus/#available-label-types) either globally or for a specific camera. This will allow face detection to run at the same time as object detection and be more efficient. -Users without a model that detects faces can still run face recognition. Frigate uses a lightweight DNN face detection model that runs on the CPU. In this case, you should _not_ define `face` in your list of objects to track. +When running a default COCO model or another model that does not include `face` as a detectable label, face detection will run via CV2 using a lightweight DNN model that runs on the CPU. In this case, you should _not_ define `face` in your list of objects to track. -:::note +### Face Recognition -Frigate needs to first detect a `face` before it can recognize a face. +Frigate has support for two face recognition model types: -::: +- **small**: Frigate will run a FaceNet embedding model to recognize faces, which runs locally on the CPU. This model is optimized for efficiency and is not as accurate. +- **large**: Frigate will run a large ArcFace embedding model that is optimized for accuracy. It is only recommended to be run when an integrated or dedicated GPU is available. + +In both cases, a lightweight face landmark detection model is also used to align faces before running recognition. ## Minimum System Requirements -Face recognition is lightweight and runs on the CPU, there are no significantly different system requirements than running Frigate itself. +The `small` model is optimized for efficiency and runs on the CPU, most CPUs should run the model efficiently. + +The `large` model is optimized for accuracy, an integrated or discrete GPU is highly recommended. ## Configuration @@ -47,12 +52,17 @@ Fine-tune face recognition with these optional parameters: ### Recognition +- `model_size`: Which model size to use, options are `small` or `large` +- `unknown_score`: Min score to mark a person as a potential match, matches at or below this will be marked as unknown. + - Default: `0.8`. - `recognition_threshold`: Recognition confidence score required to add the face to the object as a sub label. - Default: `0.9`. +- `save_attempts`: Number of images of recognized faces to save for training. + - Default: `100`. - `blur_confidence_filter`: Enables a filter that calculates how blurry the face is and adjusts the confidence based on this. - Default: `True`. -## Dataset +## Creating a Robust Training Set The number of images needed for a sufficient training set for face recognition varies depending on several factors: @@ -61,11 +71,9 @@ The number of images needed for a sufficient training set for face recognition v However, here are some general guidelines: -- Minimum: For basic face recognition tasks, a minimum of 10-20 images per person is often recommended. -- Recommended: For more robust and accurate systems, 30-50 images per person is a good starting point. -- Ideal: For optimal performance, especially in challenging conditions, 100 or more images per person can be beneficial. - -## Creating a Robust Training Set +- Minimum: For basic face recognition tasks, a minimum of 5-10 images per person is often recommended. +- Recommended: For more robust and accurate systems, 20-30 images per person is a good starting point. +- Ideal: For optimal performance, especially in challenging conditions, 50-100 images per person can be beneficial. The accuracy of face recognition is heavily dependent on the quality of data given to it for training. It is recommended to build the face training library in phases. @@ -74,19 +82,46 @@ The accuracy of face recognition is heavily dependent on the quality of data giv When choosing images to include in the face training set it is recommended to always follow these recommendations: - If it is difficult to make out details in a persons face it will not be helpful in training. -- Avoid images with under/over-exposure. +- Avoid images with extreme under/over-exposure. - Avoid blurry / pixelated images. -- Be careful when uploading images of people when they are wearing clothing that covers a lot of their face as this may confuse the training. -- Do not upload too many images at the same time, it is recommended to train 4-6 images for each person each day so it is easier to know if the previously added images helped or hurt performance. +- Avoid training on infrared (gray-scale). The models are trained on color images and will be able to extract features from gray-scale images. +- Using images of people wearing hats / sunglasses may confuse the model. +- Do not upload too many similar images at the same time, it is recommended to train no more than 4-6 similar images for each person to avoid over-fitting. ::: ### Step 1 - Building a Strong Foundation -When first enabling face recognition it is important to build a foundation of strong images. It is recommended to start by uploading 1-2 photos taken by a smartphone for each person. It is important that the person's face in the photo is straight-on and not turned which will ensure a good starting point. +When first enabling face recognition it is important to build a foundation of strong images. It is recommended to start by uploading 1-5 "portrait" photos for each person. It is important that the person's face in the photo is straight-on and not turned which will ensure a good starting point. -Then it is recommended to use the `Face Library` tab in Frigate to select and train images for each person as they are detected. When building a strong foundation it is strongly recommended to only train on images that are straight-on. Ignore images from cameras that recognize faces from an angle. Once a person starts to be consistently recognized correctly on images that are straight-on, it is time to move on to the next step. +Then it is recommended to use the `Face Library` tab in Frigate to select and train images for each person as they are detected. When building a strong foundation it is strongly recommended to only train on images that are straight-on. Ignore images from cameras that recognize faces from an angle. + +Aim to strike a balance between the quality of images while also having a range of conditions (day / night, different weather conditions, different times of day, etc.) in order to have diversity in the images used for each person and not have over-fitting. + +Once a person starts to be consistently recognized correctly on images that are straight-on, it is time to move on to the next step. ### Step 2 - Expanding The Dataset Once straight-on images are performing well, start choosing slightly off-angle images to include for training. It is important to still choose images where enough face detail is visible to recognize someone. + +## FAQ + +### Why can't I bulk upload photos? + +It is important to methodically add photos to the library, bulk importing photos (especially from a general photo library) will lead to over-fitting in that particular scenario and hurt recognition performance. + +### Why can't I bulk reprocess faces? + +Face embedding models work by breaking apart faces into different features. This means that when reprocessing an image, only images from a similar angle will have its score affected. + +### Why do unknown people score similarly to known people? + +This can happen for a few different reasons, but this is usually an indicator that the training set needs to be improved. This is often related to over-fitting: + +- If you train with only a few images per person, especially if those images are very similar, the recognition model becomes overly specialized to those specific images. +- When you provide images with different poses, lighting, and expressions, the algorithm extracts features that are consistent across those variations. +- By training on a diverse set of images, the algorithm becomes less sensitive to minor variations and noise in the input image. + +### I see scores above the threshold in the train tab, but a sub label wasn't assigned? + +The Frigate considers the recognition scores across all recognition attempts for each person object. The scores are continually weighted based on the area of the face, and a sub label will only be assigned to person if a person is confidently recognized consistently. This avoids cases where a single high confidence recognition would throw off the results. diff --git a/docs/docs/configuration/hardware_acceleration.md b/docs/docs/configuration/hardware_acceleration.md index e3bff0a0e..e05a76e62 100644 --- a/docs/docs/configuration/hardware_acceleration.md +++ b/docs/docs/configuration/hardware_acceleration.md @@ -14,7 +14,7 @@ Depending on your system, these parameters may not be compatible. More informati ## Raspberry Pi 3/4 Ensure you increase the allocated RAM for your GPU to at least 128 (`raspi-config` > Performance Options > GPU Memory). -If you are using the HA addon, you may need to use the full access variant and turn off `Protection mode` for hardware acceleration. +If you are using the HA Add-on, you may need to use the full access variant and turn off _Protection mode_ for hardware acceleration. ```yaml # if you want to decode a h264 stream @@ -28,8 +28,8 @@ ffmpeg: :::note -If running Frigate in Docker, you either need to run in privileged mode or -map the `/dev/video*` devices to Frigate. With Docker compose add: +If running Frigate through Docker, you either need to run in privileged mode or +map the `/dev/video*` devices to Frigate. With Docker Compose add: ```yaml services: @@ -80,7 +80,7 @@ Or map in all the `/dev/video*` devices. :::note -The default driver is `iHD`. You may need to change the driver to `i965` by adding the following environment variable `LIBVA_DRIVER_NAME=i965` to your docker-compose file or [in the `frigate.yaml` for HA OS users](advanced.md#environment_vars). +The default driver is `iHD`. You may need to change the driver to `i965` by adding the following environment variable `LIBVA_DRIVER_NAME=i965` to your docker-compose file or [in the `config.yml` for HA Add-on users](advanced.md#environment_vars). See [The Intel Docs](https://www.intel.com/content/www/us/en/support/articles/000005505/processors.html) to figure out what generation your CPU is. @@ -191,7 +191,7 @@ VAAPI supports automatic profile selection so it will work automatically with bo :::note -You need to change the driver to `radeonsi` by adding the following environment variable `LIBVA_DRIVER_NAME=radeonsi` to your docker-compose file or [in the `frigate.yaml` for HA OS users](advanced.md#environment_vars). +You need to change the driver to `radeonsi` by adding the following environment variable `LIBVA_DRIVER_NAME=radeonsi` to your docker-compose file or [in the `config.yml` for HA Add-on users](advanced.md#environment_vars). ::: @@ -312,7 +312,6 @@ docker run -d \ ### Docker Compose - Jetson ```yaml -version: '2.4' services: frigate: ... diff --git a/docs/docs/configuration/index.md b/docs/docs/configuration/index.md index a60da3499..b1fa876f9 100644 --- a/docs/docs/configuration/index.md +++ b/docs/docs/configuration/index.md @@ -3,10 +3,12 @@ id: index title: Frigate Configuration --- -For Home Assistant Addon installations, the config file needs to be in the root of your Home Assistant config directory (same location as `configuration.yaml`). It can be named `frigate.yaml` or `frigate.yml`, but if both files exist `frigate.yaml` will be preferred and `frigate.yml` will be ignored. +For Home Assistant Add-on installations, the config file should be at `/addon_configs/clean_copy snapshots to be enabled in your config.",
+ "documentation": "Read the documentation",
+ "cleanCopyWarning": "Some cameras have snapshots enabled but have the clean copy disabled. You need to enable clean_copy in your snapshot config to be able to submit images from these cameras to Frigate+.",
+ "table": {
+ "camera": "Camera",
+ "snapshots": "Snapshots",
+ "cleanCopySnapshots": "clean_copy Snapshots"
+ }
+ },
+ "modelInfo": {
+ "title": "Model Information",
+ "modelType": "Model Type",
+ "trainDate": "Train Date",
+ "baseModel": "Base Model",
+ "supportedDetectors": "Supported Detectors",
+ "dimensions": "Dimensions",
+ "cameras": "Cameras",
+ "loading": "Loading model information...",
+ "error": "Failed to load model information",
+ "availableModels": "Available Models",
+ "loadingAvailableModels": "Loading available models...",
+ "modelSelect": "Your available models on Frigate+ can be selected here. Note that only models compatible with your current detector configuration can be selected."
+ },
+ "toast": {
+ "success": "Frigate+ settings have been saved. Restart Frigate to apply changes.",
+ "error": "Failed to save config changes: {{errorMessage}}"
+ }
}
}
diff --git a/web/public/locales/en/views/system.json b/web/public/locales/en/views/system.json
index 77516f3e1..98583134c 100644
--- a/web/public/locales/en/views/system.json
+++ b/web/public/locales/en/views/system.json
@@ -3,7 +3,7 @@
"cameras": "Cameras Stats - Frigate",
"storage": "Storage Stats - Frigate",
"general": "General Stats - Frigate",
- "features": "Features Stats - Frigate",
+ "enrichments": "Enrichments Stats - Frigate",
"logs": {
"frigate": "Frigate Logs - Frigate",
"go2rtc": "Go2RTC Logs - Frigate",
@@ -144,8 +144,9 @@
"healthy": "System is healthy",
"reindexingEmbeddings": "Reindexing embeddings ({{processed}}% complete)"
},
- "features": {
- "title": "Features",
+ "enrichments": {
+ "title": "Enrichments",
+ "infPerSecond": "Inferences Per Second",
"embeddings": {
"image_embedding_speed": "Image Embedding Speed",
"face_embedding_speed": "Face Embedding Speed",
diff --git a/web/public/locales/zh-CN/common.json b/web/public/locales/zh-CN/common.json
index 0da7064b1..aa2d9a825 100644
--- a/web/public/locales/zh-CN/common.json
+++ b/web/public/locales/zh-CN/common.json
@@ -76,6 +76,7 @@
"button": {
"apply": "应用",
"reset": "重置",
+ "done": "完成",
"enabled": "启用",
"enable": "启用",
"disabled": "禁用",
@@ -104,7 +105,8 @@
"play": "播放",
"unselect": "取消选择",
"export": "导出",
- "deleteNow": "立即删除"
+ "deleteNow": "立即删除",
+ "next": "下一个"
},
"menu": {
"system": "系统",
diff --git a/web/public/locales/zh-CN/views/faceLibrary.json b/web/public/locales/zh-CN/views/faceLibrary.json
index 72b3cba3d..f2dba6fc3 100644
--- a/web/public/locales/zh-CN/views/faceLibrary.json
+++ b/web/public/locales/zh-CN/views/faceLibrary.json
@@ -1,4 +1,13 @@
{
+ "description": {
+ "addFace": "我们将指导如何将新面孔添加到人脸库中。"
+ },
+ "details": {
+ "confidence": "置信度",
+ "face": "人脸详情",
+ "faceDesc": "人脸及相关对象的详细信息",
+ "timestamp": "时间戳"
+ },
"documentTitle": "人脸库 - Frigate",
"uploadFaceImage": {
"title": "上传人脸图片",
@@ -6,7 +15,9 @@
},
"createFaceLibrary": {
"title": "创建人脸库",
- "desc": "创建一个新的人脸库"
+ "desc": "创建一个新的人脸库",
+ "new": "新建人脸",
+ "nextSteps": "建议使用“训练”选项卡为每个检测到的人选择并训练图像。在打好基础前,强烈建议训练仅使用正面图像。而不是从摄像机中识别到的角度拍摄的人脸图像。"
},
"train": {
"title": "训练",
@@ -19,13 +30,13 @@
"uploadImage": "上传图片",
"reprocessFace": "重新处理人脸"
},
+ "readTheDocs": "阅读文档查看更多有关为人脸库优化图像的详细信息",
"trainFaceAs": "将人脸训练为:",
"trainFaceAsPerson": "将人脸训练为人物",
-
"toast": {
"success": {
"uploadedImage": "图片上传成功。",
- "addFaceLibrary": "人脸库添加成功。",
+ "addFaceLibrary": "{{name}} 成功添加至人脸库。",
"deletedFace": "人脸删除成功。",
"trainedFace": "人脸训练成功。",
"updatedFaceScore": "人脸分数更新成功。"
diff --git a/web/public/locales/zh-CN/views/settings.json b/web/public/locales/zh-CN/views/settings.json
index 8fcfb869e..779d4ca28 100644
--- a/web/public/locales/zh-CN/views/settings.json
+++ b/web/public/locales/zh-CN/views/settings.json
@@ -7,7 +7,19 @@
"masksAndZones": "遮罩和区域编辑器 - Frigate",
"motionTuner": "运动调整器 - Frigate",
"object": "对象设置 - Frigate",
- "general": "常规设置 - Frigate"
+ "general": "常规设置 - Frigate",
+ "frigatePlus": "Frigate+ 设置 - Frigate"
+ },
+ "menu": {
+ "ui": "界面设置",
+ "classification": "分类设置",
+ "cameras": "摄像头设置",
+ "masksAndZones": "遮罩/ 区域",
+ "motionTuner": "运动调整器",
+ "debug": "调试",
+ "users": "用户",
+ "notifications": "通知",
+ "frigateplus": "Frigate+"
},
"dialog": {
"unsavedChanges": {
@@ -15,15 +27,9 @@
"desc": "是否要在继续之前保存更改?"
}
},
- "menu": {
- "uiSettings": "界面设置",
- "classificationSettings": "分类设置",
- "cameraSettings": "摄像头设置",
- "masksAndZones": "遮罩/ 区域",
- "motionTuner": "运动调整器",
- "debug": "调试",
- "users": "用户",
- "notifications": "通知"
+ "cameraSetting": {
+ "camera": "摄像头",
+ "noCamera": "没有摄像头"
},
"general": {
"title": "常规设置",
@@ -81,9 +87,15 @@
"title": "语义搜索",
"desc": "Frigate的语义搜索能够让你使用自然语言根据图像本身、自定义的文本描述或自动生成的描述来搜索视频。",
"readTheDocumentation": "阅读文档(英文)",
- "reindexOnStartup": {
- "label": "启动时重新索引",
- "desc": "每次启动将重新索引并重新处理所有缩略图和描述。关闭该设置后不要忘记重启!"
+ "reindexNow": {
+ "label": "立即重建索引",
+ "desc": "重建索引将为所有跟踪对象重新生成特征向量。该过程将在后台运行,可能会使CPU满载,所需时间取决于跟踪对象的数量。",
+ "confirmTitle": "确认重建索引",
+ "confirmDesc": "确定要为所有跟踪对象重建特征向量索引吗?此过程将在后台运行,但可能会导致CPU满载并耗费较长时间。您可以在探索页面查看进度。",
+ "confirmButton": "重建索引",
+ "success": "重建索引已成功启动。",
+ "alreadyInProgress": "重建索引已在执行中。",
+ "error": "启动重建索引失败:{{errorMessage}}"
},
"modelSize": {
"label": "模型大小",
@@ -101,7 +113,19 @@
"faceRecognition": {
"title": "人脸识别",
"desc": "人脸识别功能允许为人物分配名称,当识别到他们的面孔时,Frigate 会将人物的名字作为子标签进行分配。这些信息会显示在界面、过滤器以及通知中。",
- "readTheDocumentation": "阅读文档(英文)"
+ "readTheDocumentation": "阅读文档(英文)",
+ "modelSize": {
+ "label": "模型大小",
+ "desc": "用于人脸识别的模型尺寸。",
+ "small": {
+ "title": "小模型",
+ "desc": "使用小模型将采用FaceNet人脸特征提取模型,可在大多数CPU上高效运行。"
+ },
+ "large": {
+ "title": "大模型",
+ "desc": "使用大模型将采用ArcFace人脸特征提取模型,若条件允许将自动使用GPU运行。"
+ }
+ }
},
"licensePlateRecognition": {
"title": "车牌识别",
@@ -109,7 +133,7 @@
"readTheDocumentation": "阅读文档(英文)"
},
"toast": {
- "success": "分类设置已保存。",
+ "success": "分类设置已保存,请重启 Frigate 以应用更改。",
"error": "保存配置更改失败:{{errorMessage}}"
}
},
@@ -512,8 +536,43 @@
}
}
},
- "cameraSetting": {
- "camera": "相机",
- "noCamera": "没有相机"
+ "frigatePlus": {
+ "title": "Frigate+ 设置",
+ "apiKey": {
+ "title": "Frigate+ API 密钥",
+ "validated": "Frigate+ API 密钥已检测并验证通过",
+ "notValidated": "未检测到 Frigate+ API 密钥或验证未通过",
+ "desc": "Frigate+ API 密钥用于启用与 Frigate+ 服务的集成。",
+ "plusLink": "了解更多关于 Frigate+"
+ },
+ "snapshotConfig": {
+ "title": "快照配置",
+ "desc": "提交到 Frigate+ 需要同时在配置中启用快照和 clean_copy 快照。",
+ "documentation": "阅读文档",
+ "cleanCopyWarning": "部分摄像头已启用快照但未启用 clean_copy。您需要在快照配置中启用 clean_copy,才能将这些摄像头的图像提交到 Frigate+。",
+ "table": {
+ "camera": "摄像头",
+ "snapshots": "快照",
+ "cleanCopySnapshots": "clean_copy 快照"
+ }
+ },
+ "modelInfo": {
+ "title": "模型信息",
+ "modelType": "模型类型",
+ "trainDate": "训练日期",
+ "baseModel": "基础模型",
+ "supportedDetectors": "支持的检测器",
+ "dimensions": "大小",
+ "cameras": "摄像头",
+ "loading": "正在加载模型信息...",
+ "error": "加载模型信息失败",
+ "availableModels": "可用模型",
+ "loadingAvailableModels": "正在加载可用模型...",
+ "modelSelect": "您可以在Frigate+上选择可用的模型。请注意,只能选择与当前探测器配置兼容的模型。"
+ },
+ "toast": {
+ "success": "Frigate+ 设置已保存。请重启 Frigate 以应用更改。",
+ "error": "配置更改保存失败:{{errorMessage}}"
+ }
}
}
diff --git a/web/public/locales/zh-CN/views/system.json b/web/public/locales/zh-CN/views/system.json
index 01251f3c5..8e442162a 100644
--- a/web/public/locales/zh-CN/views/system.json
+++ b/web/public/locales/zh-CN/views/system.json
@@ -3,7 +3,7 @@
"cameras": "摄像头统计 - Frigate",
"storage": "存储统计 - Frigate",
"general": "常规统计 - Frigate",
- "features": "功能统计 - Frigate",
+ "enrichments": "增强功能统计 - Frigate",
"logs": {
"frigate": "Frigate 日志 - Frigate",
"go2rtc": "Go2RTC 日志 - Frigate",
@@ -144,8 +144,9 @@
"healthy": "系统运行正常",
"reindexingEmbeddings": "正在重新索引嵌入(已完成 {{processed}}%)"
},
- "features": {
- "title": "功能",
+ "enrichments": {
+ "title": "增强功能",
+ "infPerSecond": "每秒推理次数",
"embeddings": {
"image_embedding_speed": "图像特征提取速度",
"face_embedding_speed": "人脸特征提取速度",
diff --git a/web/site.webmanifest b/web/site.webmanifest
index 94e455ec8..7040ce5c9 100644
--- a/web/site.webmanifest
+++ b/web/site.webmanifest
@@ -1,28 +1,28 @@
{
"name": "Frigate",
"short_name": "Frigate",
- "start_url": "/",
+ "start_url": "/BASE_PATH/",
"icons": [
{
- "src": "/images/android-chrome-512x512.png",
+ "src": "/BASE_PATH/images/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
},
{
- "src": "/images/android-chrome-192x192.png",
+ "src": "/BASE_PATH/images/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any"
},
{
- "src": "/images/maskable-icon.png",
+ "src": "/BASE_PATH/images/maskable-icon.png",
"sizes": "180x180",
"type": "image/png",
"purpose": "maskable"
},
{
- "src": "/images/maskable-badge.png",
+ "src": "/BASE_PATH/images/maskable-badge.png",
"sizes": "96x96",
"type": "image/png",
"purpose": "maskable"
diff --git a/web/src/api/ws.tsx b/web/src/api/ws.tsx
index 5eedcdbcd..3e9c8c14f 100644
--- a/web/src/api/ws.tsx
+++ b/web/src/api/ws.tsx
@@ -44,7 +44,8 @@ function useValue(): useValueReturn {
return;
}
- const cameraActivity: { [key: string]: object } = JSON.parse(activityValue);
+ const cameraActivity: { [key: string]: FrigateCameraState } =
+ JSON.parse(activityValue);
if (Object.keys(cameraActivity).length === 0) {
return;
@@ -64,9 +65,7 @@ function useValue(): useValueReturn {
autotracking,
alerts,
detections,
- } =
- // @ts-expect-error we know this is correct
- state["config"];
+ } = state["config"];
cameraStates[`${name}/recordings/state`] = record ? "ON" : "OFF";
cameraStates[`${name}/enabled/state`] = enabled ? "ON" : "OFF";
cameraStates[`${name}/detect/state`] = detect ? "ON" : "OFF";
@@ -174,7 +173,7 @@ export function useEnabledState(camera: string): {
value: { payload },
send,
} = useWs(`${camera}/enabled/state`, `${camera}/enabled/set`);
- return { payload: (payload ?? "ON") as ToggleableSetting, send };
+ return { payload: payload as ToggleableSetting, send };
}
export function useDetectState(camera: string): {
diff --git a/web/src/components/graph/CameraGraph.tsx b/web/src/components/graph/LineGraph.tsx
similarity index 59%
rename from web/src/components/graph/CameraGraph.tsx
rename to web/src/components/graph/LineGraph.tsx
index a347c2d37..ef55c9343 100644
--- a/web/src/components/graph/CameraGraph.tsx
+++ b/web/src/components/graph/LineGraph.tsx
@@ -143,3 +143,118 @@ export function CameraLineGraph({
);
}
+
+type EventsPerSecondLineGraphProps = {
+ graphId: string;
+ unit: string;
+ name: string;
+ updateTimes: number[];
+ data: ApexAxisChartSeries;
+};
+export function EventsPerSecondsLineGraph({
+ graphId,
+ unit,
+ name,
+ updateTimes,
+ data,
+}: EventsPerSecondLineGraphProps) {
+ const { data: config } = useSWR