forked from Github/frigate
Feature: camera debug/config enhancements (#6920)
* Add functionality to update YAML config file with PUT request in HTTP endpoint
* Refactor copying of text to clipboard with Clipboard API and fallback to document.execCommand('copy') in CameraMap.jsx file
* Update YAML file from URL query parameters in frigate/http.py and add functionality to save motion masks, zones, and object masks in CameraMap.jsx
* formatting
* fix merging fuckup
* Refactor camera zone coordinate saving to use single query parameter per zone in CameraMap.jsx
* remove unnecessary print statements in util.py
* Refactor update_yaml_file function to separate the logic for updating YAML data into a new function update_yaml().
* fix merge errors
* Refactor code to improve error handling and add dependencies to useEffect hooks
This commit is contained in:
committed by
GitHub
parent
22cc2712a6
commit
f48dd8c1ab
@@ -30,7 +30,7 @@ from playhouse.sqliteq import SqliteQueueDatabase
|
||||
from tzlocal import get_localzone_name
|
||||
|
||||
from frigate.config import FrigateConfig
|
||||
from frigate.const import CLIPS_DIR, MAX_SEGMENT_DURATION, RECORD_DIR
|
||||
from frigate.const import CLIPS_DIR, CONFIG_DIR, MAX_SEGMENT_DURATION, RECORD_DIR
|
||||
from frigate.events.external import ExternalEventProcessor
|
||||
from frigate.models import Event, Recordings, Timeline
|
||||
from frigate.object_processing import TrackedObject
|
||||
@@ -39,7 +39,11 @@ from frigate.ptz import OnvifController
|
||||
from frigate.record.export import PlaybackFactorEnum, RecordingExporter
|
||||
from frigate.stats import stats_snapshot
|
||||
from frigate.storage import StorageMaintainer
|
||||
from frigate.util.builtin import clean_camera_user_pass, get_tz_modifiers
|
||||
from frigate.util.builtin import (
|
||||
clean_camera_user_pass,
|
||||
get_tz_modifiers,
|
||||
update_yaml_from_url,
|
||||
)
|
||||
from frigate.util.services import ffprobe_stream, restart_frigate, vainfo_hwaccel
|
||||
from frigate.version import VERSION
|
||||
|
||||
@@ -1026,6 +1030,48 @@ def config_save():
|
||||
return "Config successfully saved.", 200
|
||||
|
||||
|
||||
@bp.route("/config/set", methods=["PUT"])
|
||||
def config_set():
|
||||
config_file = os.environ.get("CONFIG_FILE", f"{CONFIG_DIR}/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
|
||||
|
||||
with open(config_file, "r") as f:
|
||||
old_raw_config = f.read()
|
||||
f.close()
|
||||
|
||||
try:
|
||||
update_yaml_from_url(config_file, request.url)
|
||||
with open(config_file, "r") as f:
|
||||
new_raw_config = f.read()
|
||||
f.close()
|
||||
# Validate the config schema
|
||||
try:
|
||||
FrigateConfig.parse_raw(new_raw_config)
|
||||
except Exception:
|
||||
with open(config_file, "w") as f:
|
||||
f.write(old_raw_config)
|
||||
f.close()
|
||||
return make_response(
|
||||
jsonify(
|
||||
{
|
||||
"success": False,
|
||||
"message": f"\nConfig Error:\n\n{str(traceback.format_exc())}",
|
||||
}
|
||||
),
|
||||
400,
|
||||
)
|
||||
except Exception as e:
|
||||
logging.error(f"Error updating config: {e}")
|
||||
return "Error updating config", 500
|
||||
|
||||
return "Config successfully updated", 200
|
||||
|
||||
|
||||
@bp.route("/config/schema.json")
|
||||
def config_schema():
|
||||
return current_app.response_class(
|
||||
|
||||
@@ -14,10 +14,12 @@ from collections.abc import Mapping
|
||||
from queue import Empty, Full
|
||||
from typing import Any, Tuple
|
||||
|
||||
import numpy as np
|
||||
import pytz
|
||||
import yaml
|
||||
from faster_fifo import DEFAULT_CIRCULAR_BUFFER_SIZE, DEFAULT_TIMEOUT
|
||||
from faster_fifo import Queue as FFQueue
|
||||
from ruamel.yaml import YAML
|
||||
|
||||
from frigate.const import REGEX_HTTP_CAMERA_USER_PASS, REGEX_RTSP_CAMERA_USER_PASS
|
||||
|
||||
@@ -224,3 +226,76 @@ def to_relative_box(
|
||||
(box[2] - box[0]) / width, # w
|
||||
(box[3] - box[1]) / height, # h
|
||||
)
|
||||
|
||||
|
||||
def create_mask(frame_shape, mask):
|
||||
mask_img = np.zeros(frame_shape, np.uint8)
|
||||
mask_img[:] = 255
|
||||
|
||||
|
||||
def update_yaml_from_url(file_path, url):
|
||||
parsed_url = urllib.parse.urlparse(url)
|
||||
query_string = urllib.parse.parse_qs(parsed_url.query, keep_blank_values=True)
|
||||
|
||||
for key_path_str, new_value_list in query_string.items():
|
||||
key_path = key_path_str.split(".")
|
||||
for i in range(len(key_path)):
|
||||
try:
|
||||
index = int(key_path[i])
|
||||
key_path[i] = (key_path[i - 1], index)
|
||||
key_path.pop(i - 1)
|
||||
except ValueError:
|
||||
pass
|
||||
new_value = new_value_list[0]
|
||||
update_yaml_file(file_path, key_path, new_value)
|
||||
|
||||
|
||||
def update_yaml_file(file_path, key_path, new_value):
|
||||
yaml = YAML()
|
||||
with open(file_path, "r") as f:
|
||||
data = yaml.load(f)
|
||||
|
||||
data = update_yaml(data, key_path, new_value)
|
||||
|
||||
with open(file_path, "w") as f:
|
||||
yaml.dump(data, f)
|
||||
|
||||
|
||||
def update_yaml(data, key_path, new_value):
|
||||
temp = data
|
||||
for key in key_path[:-1]:
|
||||
if isinstance(key, tuple):
|
||||
if key[0] not in temp:
|
||||
temp[key[0]] = [{}] * max(1, key[1] + 1)
|
||||
elif len(temp[key[0]]) <= key[1]:
|
||||
temp[key[0]] += [{}] * (key[1] - len(temp[key[0]]) + 1)
|
||||
temp = temp[key[0]][key[1]]
|
||||
else:
|
||||
if key not in temp:
|
||||
temp[key] = {}
|
||||
temp = temp[key]
|
||||
|
||||
last_key = key_path[-1]
|
||||
if new_value == "":
|
||||
if isinstance(last_key, tuple):
|
||||
del temp[last_key[0]][last_key[1]]
|
||||
else:
|
||||
del temp[last_key]
|
||||
else:
|
||||
if isinstance(last_key, tuple):
|
||||
if last_key[0] not in temp:
|
||||
temp[last_key[0]] = [{}] * max(1, last_key[1] + 1)
|
||||
elif len(temp[last_key[0]]) <= last_key[1]:
|
||||
temp[last_key[0]] += [{}] * (last_key[1] - len(temp[last_key[0]]) + 1)
|
||||
temp[last_key[0]][last_key[1]] = new_value
|
||||
else:
|
||||
if (
|
||||
last_key in temp
|
||||
and isinstance(temp[last_key], dict)
|
||||
and isinstance(new_value, dict)
|
||||
):
|
||||
temp[last_key].update(new_value)
|
||||
else:
|
||||
temp[last_key] = new_value
|
||||
|
||||
return data
|
||||
|
||||
Reference in New Issue
Block a user