mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-05-09 15:05:26 +03:00
Compare commits
29 Commits
81288ac445
...
1d46f6bc88
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d46f6bc88 | ||
|
|
6ad7ff3f6f | ||
|
|
5f205b2da4 | ||
|
|
f2c6396567 | ||
|
|
ddb0ff794b | ||
|
|
3df52d8d6c | ||
|
|
a99e6d8170 | ||
|
|
e0f50747df | ||
|
|
4a7bcbf337 | ||
|
|
39efdf633d | ||
|
|
15dc4ffc82 | ||
|
|
1871e2b6e8 | ||
|
|
a5f32b94f6 | ||
|
|
afd199e99a | ||
|
|
b9ad1b693c | ||
|
|
78e5e8d6eb | ||
|
|
58fc7aa39c | ||
|
|
506975a42c | ||
|
|
bd6b585a68 | ||
|
|
53e742ffac | ||
|
|
ba499201e6 | ||
|
|
c244e6582a | ||
|
|
fff3594553 | ||
|
|
25bfb2c481 | ||
|
|
b7261c8e70 | ||
|
|
ad9092d0da | ||
|
|
20705a3e97 | ||
|
|
f4ac063b37 | ||
|
|
2dcaeb6809 |
@ -87,43 +87,43 @@ if [[ "${TARGETARCH}" == "amd64" ]]; then
|
|||||||
# intel packages use zst compression so we need to update dpkg
|
# intel packages use zst compression so we need to update dpkg
|
||||||
apt-get install -y dpkg
|
apt-get install -y dpkg
|
||||||
|
|
||||||
# use intel apt intel packages
|
# use intel apt repo for libmfx1 (legacy QSV, pre-Gen12)
|
||||||
wget -qO - https://repositories.intel.com/gpu/intel-graphics.key | gpg --yes --dearmor --output /usr/share/keyrings/intel-graphics.gpg
|
wget -qO - https://repositories.intel.com/gpu/intel-graphics.key | gpg --yes --dearmor --output /usr/share/keyrings/intel-graphics.gpg
|
||||||
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/intel-graphics.gpg] https://repositories.intel.com/gpu/ubuntu jammy client" | tee /etc/apt/sources.list.d/intel-gpu-jammy.list
|
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/intel-graphics.gpg] https://repositories.intel.com/gpu/ubuntu jammy client" | tee /etc/apt/sources.list.d/intel-gpu-jammy.list
|
||||||
apt-get -qq update
|
apt-get -qq update
|
||||||
|
|
||||||
# intel-media-va-driver-non-free is built from source in the
|
# intel-media-va-driver-non-free is built from source in the
|
||||||
# intel-media-driver Dockerfile stage for Battlemage (Xe2) support
|
# intel-media-driver Dockerfile stage for Battlemage (Xe2) support
|
||||||
apt-get -qq install --no-install-recommends --no-install-suggests -y \
|
apt-get -qq install --no-install-recommends --no-install-suggests -y \
|
||||||
libmfx1 libmfxgen1 libvpl2
|
libmfx1
|
||||||
|
rm -f /usr/share/keyrings/intel-graphics.gpg
|
||||||
|
rm -f /etc/apt/sources.list.d/intel-gpu-jammy.list
|
||||||
|
|
||||||
|
# upgrade libva2, oneVPL runtime, and libvpl2 from trixie for Battlemage support
|
||||||
|
echo "deb http://deb.debian.org/debian trixie main" > /etc/apt/sources.list.d/trixie.list
|
||||||
|
apt-get -qq update
|
||||||
|
apt-get -qq install -y -t trixie libva2 libva-drm2 libzstd1
|
||||||
|
apt-get -qq install -y -t trixie libmfx-gen1.2 libvpl2
|
||||||
|
rm -f /etc/apt/sources.list.d/trixie.list
|
||||||
|
apt-get -qq update
|
||||||
apt-get -qq install -y ocl-icd-libopencl1
|
apt-get -qq install -y ocl-icd-libopencl1
|
||||||
|
|
||||||
# install libtbb12 for NPU support
|
# install libtbb12 for NPU support
|
||||||
apt-get -qq install -y libtbb12
|
apt-get -qq install -y libtbb12
|
||||||
|
|
||||||
rm -f /usr/share/keyrings/intel-graphics.gpg
|
# install legacy and standard intel compute packages
|
||||||
rm -f /etc/apt/sources.list.d/intel-gpu-jammy.list
|
|
||||||
|
|
||||||
# install legacy and standard intel icd and level-zero-gpu
|
|
||||||
# see https://github.com/intel/compute-runtime/blob/master/LEGACY_PLATFORMS.md for more info
|
# see https://github.com/intel/compute-runtime/blob/master/LEGACY_PLATFORMS.md for more info
|
||||||
# newer intel packages (gmmlib 22.9+, igc 2.32+) require libstdc++ >= 13.1 and libzstd >= 1.5.5
|
|
||||||
echo "deb http://deb.debian.org/debian trixie main" > /etc/apt/sources.list.d/trixie.list
|
|
||||||
apt-get -qq update
|
|
||||||
apt-get -qq install -y -t trixie libstdc++6 libzstd1
|
|
||||||
rm -f /etc/apt/sources.list.d/trixie.list
|
|
||||||
apt-get -qq update
|
|
||||||
|
|
||||||
# needed core package
|
# needed core package
|
||||||
wget https://github.com/intel/compute-runtime/releases/download/26.14.37833.4/libigdgmm12_22.9.0_amd64.deb
|
wget https://github.com/intel/compute-runtime/releases/download/26.14.37833.4/libigdgmm12_22.9.0_amd64.deb
|
||||||
dpkg -i libigdgmm12_22.9.0_amd64.deb
|
dpkg -i libigdgmm12_22.9.0_amd64.deb
|
||||||
rm libigdgmm12_22.9.0_amd64.deb
|
rm libigdgmm12_22.9.0_amd64.deb
|
||||||
|
|
||||||
# legacy packages
|
# legacy compute-runtime packages
|
||||||
wget https://github.com/intel/compute-runtime/releases/download/24.35.30872.36/intel-opencl-icd-legacy1_24.35.30872.36_amd64.deb
|
wget https://github.com/intel/compute-runtime/releases/download/24.35.30872.36/intel-opencl-icd-legacy1_24.35.30872.36_amd64.deb
|
||||||
wget https://github.com/intel/compute-runtime/releases/download/24.35.30872.36/intel-level-zero-gpu-legacy1_1.5.30872.36_amd64.deb
|
wget https://github.com/intel/compute-runtime/releases/download/24.35.30872.36/intel-level-zero-gpu-legacy1_1.5.30872.36_amd64.deb
|
||||||
wget https://github.com/intel/intel-graphics-compiler/releases/download/igc-1.0.17537.24/intel-igc-opencl_1.0.17537.24_amd64.deb
|
wget https://github.com/intel/intel-graphics-compiler/releases/download/igc-1.0.17537.24/intel-igc-opencl_1.0.17537.24_amd64.deb
|
||||||
wget https://github.com/intel/intel-graphics-compiler/releases/download/igc-1.0.17537.24/intel-igc-core_1.0.17537.24_amd64.deb
|
wget https://github.com/intel/intel-graphics-compiler/releases/download/igc-1.0.17537.24/intel-igc-core_1.0.17537.24_amd64.deb
|
||||||
# standard packages
|
# standard compute-runtime packages
|
||||||
wget https://github.com/intel/compute-runtime/releases/download/26.14.37833.4/intel-opencl-icd_26.14.37833.4-0_amd64.deb
|
wget https://github.com/intel/compute-runtime/releases/download/26.14.37833.4/intel-opencl-icd_26.14.37833.4-0_amd64.deb
|
||||||
wget https://github.com/intel/compute-runtime/releases/download/26.14.37833.4/libze-intel-gpu1_26.14.37833.4-0_amd64.deb
|
wget https://github.com/intel/compute-runtime/releases/download/26.14.37833.4/libze-intel-gpu1_26.14.37833.4-0_amd64.deb
|
||||||
wget https://github.com/intel/intel-graphics-compiler/releases/download/v2.32.7/intel-igc-opencl-2_2.32.7+21184_amd64.deb
|
wget https://github.com/intel/intel-graphics-compiler/releases/download/v2.32.7/intel-igc-opencl-2_2.32.7+21184_amd64.deb
|
||||||
@ -137,6 +137,10 @@ if [[ "${TARGETARCH}" == "amd64" ]]; then
|
|||||||
dpkg -i *.deb
|
dpkg -i *.deb
|
||||||
rm *.deb
|
rm *.deb
|
||||||
apt-get -qq install -f -y
|
apt-get -qq install -f -y
|
||||||
|
|
||||||
|
# Battlemage uses the xe kernel driver, but the VA-API driver is still iHD.
|
||||||
|
# The oneVPL runtime may look for a driver named after the kernel module.
|
||||||
|
ln -sf /usr/lib/x86_64-linux-gnu/dri/iHD_drv_video.so /usr/lib/x86_64-linux-gnu/dri/xe_drv_video.so
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "${TARGETARCH}" == "arm64" ]]; then
|
if [[ "${TARGETARCH}" == "arm64" ]]; then
|
||||||
|
|||||||
@ -11,7 +11,7 @@ joserfc == 1.2.*
|
|||||||
cryptography == 44.0.*
|
cryptography == 44.0.*
|
||||||
pathvalidate == 3.3.*
|
pathvalidate == 3.3.*
|
||||||
markupsafe == 3.0.*
|
markupsafe == 3.0.*
|
||||||
python-multipart == 0.0.20
|
python-multipart == 0.0.26
|
||||||
# Classification Model Training
|
# Classification Model Training
|
||||||
tensorflow == 2.19.* ; platform_machine == 'aarch64'
|
tensorflow == 2.19.* ; platform_machine == 'aarch64'
|
||||||
tensorflow-cpu == 2.19.* ; platform_machine == 'x86_64'
|
tensorflow-cpu == 2.19.* ; platform_machine == 'x86_64'
|
||||||
@ -42,7 +42,7 @@ opencv-python-headless == 4.11.0.*
|
|||||||
opencv-contrib-python == 4.11.0.*
|
opencv-contrib-python == 4.11.0.*
|
||||||
scipy == 1.16.*
|
scipy == 1.16.*
|
||||||
# OpenVino & ONNX
|
# OpenVino & ONNX
|
||||||
openvino == 2025.3.*
|
openvino == 2025.4.*
|
||||||
onnxruntime == 1.22.*
|
onnxruntime == 1.22.*
|
||||||
# Embeddings
|
# Embeddings
|
||||||
transformers == 4.45.*
|
transformers == 4.45.*
|
||||||
|
|||||||
@ -111,13 +111,11 @@ TCP ensures that all data packets arrive in the correct order. This is crucial f
|
|||||||
|
|
||||||
You can still configure Frigate to use UDP by using ffmpeg input args or the preset `preset-rtsp-udp`. See the [ffmpeg presets](/configuration/ffmpeg_presets) documentation.
|
You can still configure Frigate to use UDP by using ffmpeg input args or the preset `preset-rtsp-udp`. See the [ffmpeg presets](/configuration/ffmpeg_presets) documentation.
|
||||||
|
|
||||||
### Frigate hangs on startup with a "probing detect stream" message in the logs
|
### Frigate is slow to start up with a "probing detect stream" message in the logs
|
||||||
|
|
||||||
On startup, Frigate probes each camera's detect stream with OpenCV to auto-detect its resolution. OpenCV's FFmpeg backend may attempt RTSP over UDP during this probe regardless of the `-rtsp_transport tcp` in your `input_args` or preset. For cameras that do not respond to UDP (common on some Reolink models and others behind firewalls that block UDP), the probe can hang indefinitely and block Frigate from finishing startup, or it can return zeroed-out dimensions that show up as width `0` and height `0` in Camera Probe Info under System Metrics.
|
When `detect.width` and `detect.height` are not set, Frigate probes each camera's detect stream on startup (and when saving the config) to auto-detect its resolution. For RTSP streams Frigate probes with ffprobe and automatically retries over TCP if UDP doesn't respond, with a 5 second timeout per attempt. A camera that cannot be reached over either transport will add up to ~10 seconds to startup before Frigate falls through with default dimensions, which may show up as width `0` and height `0` in Camera Probe Info under System Metrics.
|
||||||
|
|
||||||
There are two ways to avoid this:
|
To skip the probe entirely and make startup instant, set `detect.width` and `detect.height` explicitly in your camera config:
|
||||||
|
|
||||||
1. Set `detect.width` and `detect.height` explicitly in your camera config. When both are set, Frigate skips the auto-detect probe entirely:
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
cameras:
|
cameras:
|
||||||
@ -126,11 +124,3 @@ There are two ways to avoid this:
|
|||||||
width: 1280
|
width: 1280
|
||||||
height: 720
|
height: 720
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Force OpenCV's FFmpeg backend to use TCP for RTSP by setting the environment variable on your Frigate container:
|
|
||||||
|
|
||||||
```
|
|
||||||
OPENCV_FFMPEG_CAPTURE_OPTIONS=rtsp_transport;tcp
|
|
||||||
```
|
|
||||||
|
|
||||||
This is a process-wide setting and applies to all cameras. If you have any cameras that require `preset-rtsp-udp`, use option 1 instead.
|
|
||||||
|
|||||||
6
docs/package-lock.json
generated
6
docs/package-lock.json
generated
@ -10897,9 +10897,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/express/node_modules/path-to-regexp": {
|
"node_modules/express/node_modules/path-to-regexp": {
|
||||||
"version": "0.1.12",
|
"version": "0.1.13",
|
||||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
|
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz",
|
||||||
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
|
"integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/express/node_modules/range-parser": {
|
"node_modules/express/node_modules/range-parser": {
|
||||||
|
|||||||
@ -310,6 +310,10 @@ class EmbeddingMaintainer(threading.Thread):
|
|||||||
self._handle_custom_classification_update(topic, payload)
|
self._handle_custom_classification_update(topic, payload)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if topic == "config/genai":
|
||||||
|
self.config.genai = payload
|
||||||
|
self.genai_manager.update_config(self.config)
|
||||||
|
|
||||||
# Broadcast to all processors — each decides if the topic is relevant
|
# Broadcast to all processors — each decides if the topic is relevant
|
||||||
for processor in self.realtime_processors:
|
for processor in self.realtime_processors:
|
||||||
processor.update_config(topic, payload)
|
processor.update_config(topic, payload)
|
||||||
|
|||||||
@ -113,6 +113,15 @@ class OllamaClient(GenAIClient):
|
|||||||
schema = response_format.get("json_schema", {}).get("schema")
|
schema = response_format.get("json_schema", {}).get("schema")
|
||||||
if schema:
|
if schema:
|
||||||
ollama_options["format"] = self._clean_schema_for_ollama(schema)
|
ollama_options["format"] = self._clean_schema_for_ollama(schema)
|
||||||
|
logger.debug(
|
||||||
|
"Ollama generate request: model=%s, prompt_len=%s, image_count=%s, "
|
||||||
|
"has_format=%s, options=%s",
|
||||||
|
self.genai_config.model,
|
||||||
|
len(prompt),
|
||||||
|
len(images) if images else 0,
|
||||||
|
"format" in ollama_options,
|
||||||
|
{k: v for k, v in ollama_options.items() if k != "format"},
|
||||||
|
)
|
||||||
result = self.provider.generate(
|
result = self.provider.generate(
|
||||||
self.genai_config.model,
|
self.genai_config.model,
|
||||||
prompt,
|
prompt,
|
||||||
@ -120,9 +129,24 @@ class OllamaClient(GenAIClient):
|
|||||||
**ollama_options,
|
**ollama_options,
|
||||||
)
|
)
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f"Ollama tokens used: eval_count={result.get('eval_count')}, prompt_eval_count={result.get('prompt_eval_count')}"
|
"Ollama generate response: done=%s, done_reason=%s, eval_count=%s, "
|
||||||
|
"prompt_eval_count=%s, response_len=%s",
|
||||||
|
result.get("done"),
|
||||||
|
result.get("done_reason"),
|
||||||
|
result.get("eval_count"),
|
||||||
|
result.get("prompt_eval_count"),
|
||||||
|
len(result.get("response", "") or ""),
|
||||||
)
|
)
|
||||||
return str(result["response"]).strip()
|
response_text = str(result["response"]).strip()
|
||||||
|
if not response_text:
|
||||||
|
logger.warning(
|
||||||
|
"Ollama returned a blank response for model %s (done_reason=%s, "
|
||||||
|
"eval_count=%s). Check model output, ensure thinking is disabled.",
|
||||||
|
self.genai_config.model,
|
||||||
|
result.get("done_reason"),
|
||||||
|
result.get("eval_count"),
|
||||||
|
)
|
||||||
|
return response_text
|
||||||
except (
|
except (
|
||||||
TimeoutException,
|
TimeoutException,
|
||||||
ResponseError,
|
ResponseError,
|
||||||
|
|||||||
@ -80,7 +80,23 @@ class OpenAIClient(GenAIClient):
|
|||||||
and hasattr(result, "choices")
|
and hasattr(result, "choices")
|
||||||
and len(result.choices) > 0
|
and len(result.choices) > 0
|
||||||
):
|
):
|
||||||
return str(result.choices[0].message.content.strip())
|
message = result.choices[0].message
|
||||||
|
content = message.content
|
||||||
|
|
||||||
|
if not content:
|
||||||
|
# When reasoning is enabled for some OpenAI backends the actual response
|
||||||
|
# is incorrectly placed in reasoning_content instead of content.
|
||||||
|
# This is buggy/incorrect behavior — reasoning should not be
|
||||||
|
# enabled for these models.
|
||||||
|
reasoning_content = getattr(message, "reasoning_content", None)
|
||||||
|
if reasoning_content:
|
||||||
|
logger.warning(
|
||||||
|
"Response content was empty but reasoning_content was provided; "
|
||||||
|
"reasoning appears to be enabled and should be disabled for this model."
|
||||||
|
)
|
||||||
|
content = reasoning_content
|
||||||
|
|
||||||
|
return str(content.strip()) if content else None
|
||||||
return None
|
return None
|
||||||
except (TimeoutException, Exception) as e:
|
except (TimeoutException, Exception) as e:
|
||||||
logger.warning("OpenAI returned an error: %s", str(e))
|
logger.warning("OpenAI returned an error: %s", str(e))
|
||||||
|
|||||||
@ -807,10 +807,15 @@ async def get_video_properties(
|
|||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
async def probe_with_ffprobe(
|
async def probe_with_ffprobe(
|
||||||
url: str,
|
url: str,
|
||||||
|
rtsp_transport: Optional[str] = None,
|
||||||
) -> tuple[bool, int, int, Optional[str], float]:
|
) -> tuple[bool, int, int, Optional[str], float]:
|
||||||
"""Fallback using ffprobe: returns (valid, width, height, codec, duration)."""
|
"""Fallback using ffprobe: returns (valid, width, height, codec, duration)."""
|
||||||
cmd = [
|
cmd = [ffmpeg.ffprobe_path]
|
||||||
ffmpeg.ffprobe_path,
|
if rtsp_transport:
|
||||||
|
cmd += ["-rtsp_transport", rtsp_transport]
|
||||||
|
cmd += [
|
||||||
|
"-rw_timeout",
|
||||||
|
"5000000",
|
||||||
"-v",
|
"-v",
|
||||||
"quiet",
|
"quiet",
|
||||||
"-print_format",
|
"-print_format",
|
||||||
@ -872,13 +877,27 @@ async def get_video_properties(
|
|||||||
cap.release()
|
cap.release()
|
||||||
return valid, width, height, fourcc, duration
|
return valid, width, height, fourcc, duration
|
||||||
|
|
||||||
# try cv2 first
|
is_rtsp = url.startswith("rtsp://")
|
||||||
|
|
||||||
|
if is_rtsp:
|
||||||
|
# skip cv2 for RTSP: its FFmpeg backend has a hardcoded ~30s internal
|
||||||
|
# timeout that cannot be shortened per-call, and ffprobe bounded by
|
||||||
|
# -rw_timeout handles RTSP probing reliably
|
||||||
|
has_video, width, height, fourcc, duration = await probe_with_ffprobe(url)
|
||||||
|
else:
|
||||||
|
# try cv2 first for local files, HTTP, RTMP
|
||||||
has_video, width, height, fourcc, duration = probe_with_cv2(url)
|
has_video, width, height, fourcc, duration = probe_with_cv2(url)
|
||||||
|
|
||||||
# fallback to ffprobe if needed
|
# fallback to ffprobe if needed
|
||||||
if not has_video or (get_duration and duration < 0):
|
if not has_video or (get_duration and duration < 0):
|
||||||
has_video, width, height, fourcc, duration = await probe_with_ffprobe(url)
|
has_video, width, height, fourcc, duration = await probe_with_ffprobe(url)
|
||||||
|
|
||||||
|
# last resort for RTSP: try TCP transport, since default UDP may be blocked
|
||||||
|
if (not has_video or (get_duration and duration < 0)) and is_rtsp:
|
||||||
|
has_video, width, height, fourcc, duration = await probe_with_ffprobe(
|
||||||
|
url, rtsp_transport="tcp"
|
||||||
|
)
|
||||||
|
|
||||||
result: dict[str, Any] = {"has_valid_video": has_video}
|
result: dict[str, Any] = {"has_valid_video": has_video}
|
||||||
if has_video:
|
if has_video:
|
||||||
result.update({"width": width, "height": height})
|
result.update({"width": width, "height": height})
|
||||||
|
|||||||
14
web/package-lock.json
generated
14
web/package-lock.json
generated
@ -54,7 +54,7 @@
|
|||||||
"immer": "^10.1.1",
|
"immer": "^10.1.1",
|
||||||
"js-yaml": "^4.1.1",
|
"js-yaml": "^4.1.1",
|
||||||
"konva": "^10.2.3",
|
"konva": "^10.2.3",
|
||||||
"lodash": "^4.17.23",
|
"lodash": "^4.18.1",
|
||||||
"lucide-react": "^0.577.0",
|
"lucide-react": "^0.577.0",
|
||||||
"monaco-yaml": "^5.4.1",
|
"monaco-yaml": "^5.4.1",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
@ -9636,15 +9636,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lodash": {
|
"node_modules/lodash": {
|
||||||
"version": "4.17.23",
|
"version": "4.18.1",
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
|
||||||
"integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
|
"integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/lodash-es": {
|
"node_modules/lodash-es": {
|
||||||
"version": "4.17.23",
|
"version": "4.18.1",
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
|
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz",
|
||||||
"integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
|
"integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/lodash.merge": {
|
"node_modules/lodash.merge": {
|
||||||
|
|||||||
@ -68,7 +68,7 @@
|
|||||||
"immer": "^10.1.1",
|
"immer": "^10.1.1",
|
||||||
"js-yaml": "^4.1.1",
|
"js-yaml": "^4.1.1",
|
||||||
"konva": "^10.2.3",
|
"konva": "^10.2.3",
|
||||||
"lodash": "^4.17.23",
|
"lodash": "^4.18.1",
|
||||||
"lucide-react": "^0.577.0",
|
"lucide-react": "^0.577.0",
|
||||||
"monaco-yaml": "^5.4.1",
|
"monaco-yaml": "^5.4.1",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
|
|||||||
@ -177,6 +177,14 @@
|
|||||||
"success": "Els enregistraments de vídeo associats als elements de revisió seleccionats s’han suprimit correctament.",
|
"success": "Els enregistraments de vídeo associats als elements de revisió seleccionats s’han suprimit correctament.",
|
||||||
"error": "No s'ha pogut suprimir: {{error}}"
|
"error": "No s'ha pogut suprimir: {{error}}"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"shareTimestamp": {
|
||||||
|
"label": "Comparteix la marca horària",
|
||||||
|
"title": "Comparteix la marca horària",
|
||||||
|
"description": "Comparteix un URL amb marca horària de la posició actual del jugador o tria una marca horària personalitzada. Tingueu en compte que aquest no és un URL de compartició pública i només és accessible per als usuaris amb accés a Frigate i aquesta càmera.",
|
||||||
|
"custom": "Marca horària personalitzada",
|
||||||
|
"button": "Comparteix l'URL de la marca horària",
|
||||||
|
"shareTitle": "Marca de temps de revisió de Frigate: {{camera}}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"imagePicker": {
|
"imagePicker": {
|
||||||
|
|||||||
@ -32,7 +32,8 @@
|
|||||||
"noPreviewFoundFor": "No s'ha trobat cap previsualització per a {{cameraName}}",
|
"noPreviewFoundFor": "No s'ha trobat cap previsualització per a {{cameraName}}",
|
||||||
"submitFrigatePlus": {
|
"submitFrigatePlus": {
|
||||||
"title": "Enviar aquesta imatge a Frigate+?",
|
"title": "Enviar aquesta imatge a Frigate+?",
|
||||||
"submit": "Enviar"
|
"submit": "Enviar",
|
||||||
|
"previewError": "No s'ha pogut carregar la vista prèvia de la instantània. És possible que l'enregistrament no estigui disponible en aquest moment."
|
||||||
},
|
},
|
||||||
"livePlayerRequiredIOSVersion": "Es requereix iOS 17.1 o superior per a aquest tipus de reproducció en directe.",
|
"livePlayerRequiredIOSVersion": "Es requereix iOS 17.1 o superior per a aquest tipus de reproducció en directe.",
|
||||||
"streamOffline": {
|
"streamOffline": {
|
||||||
|
|||||||
@ -27,7 +27,9 @@
|
|||||||
},
|
},
|
||||||
"documentTitle": "Revisió - Frigate",
|
"documentTitle": "Revisió - Frigate",
|
||||||
"recordings": {
|
"recordings": {
|
||||||
"documentTitle": "Enregistraments - Frigate"
|
"documentTitle": "Enregistraments - Frigate",
|
||||||
|
"invalidSharedLink": "No s'ha pogut obrir l'enllaç d'enregistrament amb marques de temps a causa d'un error d'anàlisi.",
|
||||||
|
"invalidSharedCamera": "No s'ha pogut obrir l'enllaç d'enregistrament amb marques de temps a causa d'una càmera desconeguda o no autoritzada."
|
||||||
},
|
},
|
||||||
"calendarFilter": {
|
"calendarFilter": {
|
||||||
"last24Hours": "Últimes 24 hores"
|
"last24Hours": "Últimes 24 hores"
|
||||||
|
|||||||
@ -1280,7 +1280,8 @@
|
|||||||
},
|
},
|
||||||
"hikvision": {
|
"hikvision": {
|
||||||
"substreamWarning": "El substream 1 està bloquejat a una resolució baixa. Moltes càmeres Hikvision suporten subfluxos addicionals que han d'estar habilitats a la configuració de la càmera. Es recomana comprovar i utilitzar aquests corrents si estan disponibles."
|
"substreamWarning": "El substream 1 està bloquejat a una resolució baixa. Moltes càmeres Hikvision suporten subfluxos addicionals que han d'estar habilitats a la configuració de la càmera. Es recomana comprovar i utilitzar aquests corrents si estan disponibles."
|
||||||
}
|
},
|
||||||
|
"resolutionUnknown": "La resolució d'aquest flux no s'ha pogut investigar. Això causarà problemes a l'inici. Heu d'establir manualment la resolució de detecció a Configuració o a la configuració."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -415,6 +415,7 @@
|
|||||||
"audioCodecGood": "Audio codec is {{codec}}.",
|
"audioCodecGood": "Audio codec is {{codec}}.",
|
||||||
"resolutionHigh": "A resolution of {{resolution}} may cause increased resource usage.",
|
"resolutionHigh": "A resolution of {{resolution}} may cause increased resource usage.",
|
||||||
"resolutionLow": "A resolution of {{resolution}} may be too low for reliable detection of small objects.",
|
"resolutionLow": "A resolution of {{resolution}} may be too low for reliable detection of small objects.",
|
||||||
|
"resolutionUnknown": "The resolution of this stream could not be probed. This will cause issues on startup. You should manually set the detect resolution in Settings or your config.",
|
||||||
"noAudioWarning": "No audio detected for this stream, recordings will not have audio.",
|
"noAudioWarning": "No audio detected for this stream, recordings will not have audio.",
|
||||||
"audioCodecRecordError": "The AAC audio codec is required to support audio in recordings.",
|
"audioCodecRecordError": "The AAC audio codec is required to support audio in recordings.",
|
||||||
"audioCodecRequired": "An audio stream is required to support audio detection.",
|
"audioCodecRequired": "An audio stream is required to support audio detection.",
|
||||||
|
|||||||
@ -178,7 +178,8 @@
|
|||||||
"configuration": "Konfiguráció",
|
"configuration": "Konfiguráció",
|
||||||
"systemLogs": "Rendszer naplók",
|
"systemLogs": "Rendszer naplók",
|
||||||
"settings": "Beállítások",
|
"settings": "Beállítások",
|
||||||
"classification": "Osztályozás"
|
"classification": "Osztályozás",
|
||||||
|
"profiles": "Profilok"
|
||||||
},
|
},
|
||||||
"role": {
|
"role": {
|
||||||
"viewer": "Néző",
|
"viewer": "Néző",
|
||||||
|
|||||||
@ -3,7 +3,8 @@
|
|||||||
"noPreviewFound": "Nincs elérhető előkép",
|
"noPreviewFound": "Nincs elérhető előkép",
|
||||||
"submitFrigatePlus": {
|
"submitFrigatePlus": {
|
||||||
"title": "Elküldi ezt a képet a Frigate+-nak?",
|
"title": "Elküldi ezt a képet a Frigate+-nak?",
|
||||||
"submit": "Küldés"
|
"submit": "Küldés",
|
||||||
|
"previewError": "Nem sikerült betölteni a pillanatkép előnézetét. Előfordulhat, hogy a felvétel jelenleg nem elérhető."
|
||||||
},
|
},
|
||||||
"noPreviewFoundFor": "Nem található előnézet {{cameraName}}-hoz/-hez/-höz",
|
"noPreviewFoundFor": "Nem található előnézet {{cameraName}}-hoz/-hez/-höz",
|
||||||
"livePlayerRequiredIOSVersion": "iOS 17.1 vagy újabb szükséges ehhez az élő adás típushoz.",
|
"livePlayerRequiredIOSVersion": "iOS 17.1 vagy újabb szükséges ehhez az élő adás típushoz.",
|
||||||
|
|||||||
@ -50,7 +50,15 @@
|
|||||||
"description": "A hangalapú eseményérzékelés engedélyezése vagy letiltása ennél a kameránál."
|
"description": "A hangalapú eseményérzékelés engedélyezése vagy letiltása ennél a kameránál."
|
||||||
},
|
},
|
||||||
"max_not_heard": {
|
"max_not_heard": {
|
||||||
"description": "Ennyi másodperc után fejeződik be a hangesemény, ha a beállított hangtípus nem észlelhető."
|
"description": "Ennyi másodperc után fejeződik be a hangesemény, ha a beállított hangtípus nem észlelhető.",
|
||||||
|
"label": "Időtúllépés befejezése"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"audio_transcription": {
|
||||||
|
"label": "Hang Feliratozás",
|
||||||
|
"description": "„Beállítások élő hang és beszéd automatikus szöveggé alakításához, eseményekhez és élő feliratozáshoz.",
|
||||||
|
"enabled": {
|
||||||
|
"label": "Hangról szövegre alakítás engedélyezése"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -59,10 +59,18 @@
|
|||||||
"label": "Hangalapú eseményérzékelés engedélyezése"
|
"label": "Hangalapú eseményérzékelés engedélyezése"
|
||||||
},
|
},
|
||||||
"max_not_heard": {
|
"max_not_heard": {
|
||||||
"description": "Ennyi másodperc után fejeződik be a hangesemény, ha a beállított hangtípus nem észlelhető."
|
"description": "Ennyi másodperc után fejeződik be a hangesemény, ha a beállított hangtípus nem észlelhető.",
|
||||||
|
"label": "Időtúllépés befejezése"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"auth": {
|
"auth": {
|
||||||
"label": "Azonosítás"
|
"label": "Azonosítás"
|
||||||
|
},
|
||||||
|
"audio_transcription": {
|
||||||
|
"label": "Hang Feliratozás",
|
||||||
|
"description": "„Beállítások élő hang és beszéd automatikus szöveggé alakításához, eseményekhez és élő feliratozáshoz.",
|
||||||
|
"enabled": {
|
||||||
|
"label": "Hangról szövegre alakítás engedélyezése"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,5 +31,13 @@
|
|||||||
"global": {
|
"global": {
|
||||||
"resolution": "Globális Felbontás"
|
"resolution": "Globális Felbontás"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"snapshots": {
|
||||||
|
"global": {
|
||||||
|
"display": "Globális kijelző"
|
||||||
|
},
|
||||||
|
"cameras": {
|
||||||
|
"display": "Kijelző"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,5 +10,6 @@
|
|||||||
"pattern": "Érvénytelen formátum",
|
"pattern": "Érvénytelen formátum",
|
||||||
"required": "Ezt a mezőt kötelező kitölteni",
|
"required": "Ezt a mezőt kötelező kitölteni",
|
||||||
"type": "Érvénytelen értéktípus",
|
"type": "Érvénytelen értéktípus",
|
||||||
"enum": "Az engedélyezett értékek közül legalább egy kell legyen"
|
"enum": "Az engedélyezett értékek közül legalább egy kell legyen",
|
||||||
|
"const": "Az érték nem egyezik a várt állandóval"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,7 +14,9 @@
|
|||||||
"cameraManagement": "Kamerák kezelése - Frigate",
|
"cameraManagement": "Kamerák kezelése - Frigate",
|
||||||
"cameraReview": "Kamera beállítások áttekintése – Frigate",
|
"cameraReview": "Kamera beállítások áttekintése – Frigate",
|
||||||
"globalConfig": "Globális Konfiguráció - Frigate",
|
"globalConfig": "Globális Konfiguráció - Frigate",
|
||||||
"cameraConfig": "Kamera Konfiguráció - Frigate"
|
"cameraConfig": "Kamera Konfiguráció - Frigate",
|
||||||
|
"maintenance": "Karbantartás - Fregatt",
|
||||||
|
"profiles": "Profilok - Fregatt"
|
||||||
},
|
},
|
||||||
"menu": {
|
"menu": {
|
||||||
"ui": "UI",
|
"ui": "UI",
|
||||||
@ -30,7 +32,24 @@
|
|||||||
"triggers": "Triggerek",
|
"triggers": "Triggerek",
|
||||||
"roles": "Szerepkörök",
|
"roles": "Szerepkörök",
|
||||||
"cameraManagement": "Menedzsment",
|
"cameraManagement": "Menedzsment",
|
||||||
"cameraReview": "Vizsgálat"
|
"cameraReview": "Vizsgálat",
|
||||||
|
"general": "Általános",
|
||||||
|
"globalConfig": "Globális konfiguráció",
|
||||||
|
"system": "Rendszer",
|
||||||
|
"integrations": "Integrációk",
|
||||||
|
"uiSettings": "UI beállítások",
|
||||||
|
"profiles": "Profilok",
|
||||||
|
"globalDetect": "Tárgy felismerés",
|
||||||
|
"globalRecording": "Felvétel",
|
||||||
|
"globalSnapshots": "Pillanatképek",
|
||||||
|
"globalFfmpeg": "FFmpeg",
|
||||||
|
"globalMotion": "Mozgásérzékelés",
|
||||||
|
"globalObjects": "Tárgyak",
|
||||||
|
"globalReview": "Áttekintés",
|
||||||
|
"globalAudioEvents": "Hangesemények",
|
||||||
|
"cameraAudioEvents": "Hangesemények",
|
||||||
|
"cameraAudioTranscription": "Hang Feliratozás",
|
||||||
|
"integrationAudioTranscription": "Hang Feliratozás"
|
||||||
},
|
},
|
||||||
"dialog": {
|
"dialog": {
|
||||||
"unsavedChanges": {
|
"unsavedChanges": {
|
||||||
@ -863,5 +882,11 @@
|
|||||||
"streamConfiguration": "Stream beállítások",
|
"streamConfiguration": "Stream beállítások",
|
||||||
"validationAndTesting": "Validálás és tesztelés"
|
"validationAndTesting": "Validálás és tesztelés"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"button": {
|
||||||
|
"overriddenGlobal": "Felülírt (Globális)",
|
||||||
|
"overriddenGlobalTooltip": "Ez a kamera felülírja a globális konfigurációs beállításokat ebben a részben",
|
||||||
|
"overriddenBaseConfig": "Felülírt (Alapbeállítás)",
|
||||||
|
"overriddenBaseConfigTooltip": "A {{profile}} profil felülírja a konfigurációs beállításokat ebben a részben"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -133,7 +133,7 @@
|
|||||||
"unsuspended": "再開",
|
"unsuspended": "再開",
|
||||||
"play": "再生",
|
"play": "再生",
|
||||||
"unselect": "選択解除",
|
"unselect": "選択解除",
|
||||||
"export": "書き出し",
|
"export": "エクスポート",
|
||||||
"deleteNow": "今すぐ削除",
|
"deleteNow": "今すぐ削除",
|
||||||
"next": "次へ",
|
"next": "次へ",
|
||||||
"continue": "続行"
|
"continue": "続行"
|
||||||
@ -181,7 +181,7 @@
|
|||||||
},
|
},
|
||||||
"review": "レビュー",
|
"review": "レビュー",
|
||||||
"explore": "ブラウズ",
|
"explore": "ブラウズ",
|
||||||
"export": "書き出し",
|
"export": "エクスポート",
|
||||||
"uiPlayground": "UI テスト環境",
|
"uiPlayground": "UI テスト環境",
|
||||||
"faceLibrary": "顔データベース",
|
"faceLibrary": "顔データベース",
|
||||||
"user": {
|
"user": {
|
||||||
|
|||||||
@ -46,23 +46,32 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"placeholder": "書き出しに名前を付ける"
|
"placeholder": "エクスポートに名前を付ける"
|
||||||
},
|
},
|
||||||
"select": "選択",
|
"select": "選択",
|
||||||
"export": "書き出し",
|
"export": "エクスポート",
|
||||||
"selectOrExport": "選択または書き出し",
|
"selectOrExport": "選択またはエクスポート",
|
||||||
"toast": {
|
"toast": {
|
||||||
"success": "書き出しを開始しました。出力ページでファイルを確認できます。",
|
"success": "エクスポートを開始しました。エクスポートページでファイルを確認できます。",
|
||||||
"error": {
|
"error": {
|
||||||
"failed": "書き出しの開始に失敗しました: {{error}}",
|
"failed": "エクスポートキューの開始に失敗しました: {{error}}",
|
||||||
"endTimeMustAfterStartTime": "終了時間は開始時間より後である必要があります",
|
"endTimeMustAfterStartTime": "終了時間は開始時間より後である必要があります",
|
||||||
"noVaildTimeSelected": "有効な時間範囲が選択されていません"
|
"noVaildTimeSelected": "有効な時間範囲が選択されていません"
|
||||||
},
|
},
|
||||||
"view": "表示"
|
"view": "表示",
|
||||||
|
"queued": "エクスポートがキューに追加されました。進捗状況はエクスポートページで確認できます。",
|
||||||
|
"batchQueuedSuccess_other": "{{count}} 件のエクスポートがキューに登録されました。現在ケースをオープンしています。",
|
||||||
|
"batchQueuedPartial": "{{total}} 件中 {{successful}} 件のエクスポートがキューに追加されました。失敗したカメラ: {{failedCameras}}",
|
||||||
|
"batchQueueFailed": "{{total}} 件のエクスポートをキューに追加できませんでした。失敗したカメラ: {{failedCameras}}"
|
||||||
},
|
},
|
||||||
"fromTimeline": {
|
"fromTimeline": {
|
||||||
"saveExport": "書き出しを保存",
|
"saveExport": "エクスポートを保存",
|
||||||
"previewExport": "書き出しをプレビュー"
|
"previewExport": "エクスポートをプレビュー",
|
||||||
|
"queueingExport": "エクスポートをキューイングしています..."
|
||||||
|
},
|
||||||
|
"queueing": "エクスポートをキューイングしています...",
|
||||||
|
"multiCamera": {
|
||||||
|
"queueingButton": "エクスポートをキューイングしています..."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"streaming": {
|
"streaming": {
|
||||||
@ -105,7 +114,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"button": {
|
"button": {
|
||||||
"export": "書き出し",
|
"export": "エクスポート",
|
||||||
"markAsReviewed": "レビュー済みにする",
|
"markAsReviewed": "レビュー済みにする",
|
||||||
"deleteNow": "今すぐ削除",
|
"deleteNow": "今すぐ削除",
|
||||||
"markAsUnreviewed": "未レビューに戻す"
|
"markAsUnreviewed": "未レビューに戻す"
|
||||||
|
|||||||
@ -114,7 +114,7 @@
|
|||||||
},
|
},
|
||||||
"trackedObjectDelete": {
|
"trackedObjectDelete": {
|
||||||
"title": "削除の確認",
|
"title": "削除の確認",
|
||||||
"desc": "これら {{objectLength}} 件の追跡オブジェクトを削除すると、スナップショット、保存された埋め込み、関連するオブジェクトのライフサイクル項目が削除されます。履歴ビューの録画映像は削除<em>されません</em>。<br /><br />続行してもよろしいですか?<br /><br />今後このダイアログを表示しない場合は <em>Shift</em> キーを押しながら操作してください。",
|
"desc": "これら {{objectLength}} 件の追跡オブジェクトを削除すると、スナップショット、保存された埋め込み、関連するオブジェクトのライフサイクル項目が削除されます。履歴ビューの録画映像は<em>削除されません。</em><br /><br />続行してもよろしいですか?<br /><br />今後このダイアログを表示しない場合は <em>Shift</em> キーを押しながら操作してください。",
|
||||||
"toast": {
|
"toast": {
|
||||||
"success": "追跡オブジェクトを削除しました。",
|
"success": "追跡オブジェクトを削除しました。",
|
||||||
"error": "追跡オブジェクトの削除に失敗しました: {{errorMessage}}"
|
"error": "追跡オブジェクトの削除に失敗しました: {{errorMessage}}"
|
||||||
|
|||||||
@ -12,11 +12,11 @@
|
|||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"success": {
|
"success": {
|
||||||
"deletedImage_other": "削除された画像",
|
"deletedImage_other": "{{count}} 件の削除された画像",
|
||||||
"categorizedImage": "画像の分類に成功しました",
|
"categorizedImage": "画像の分類に成功しました",
|
||||||
"trainedModel": "モデルを正常に学習させました。",
|
"trainedModel": "モデルを正常に学習させました。",
|
||||||
"trainingModel": "モデルのトレーニングを正常に開始しました。",
|
"trainingModel": "モデルのトレーニングを正常に開始しました。",
|
||||||
"deletedCategory_other": "クラスを削除しました",
|
"deletedCategory_other": "{{count}} 件のクラスを削除しました",
|
||||||
"deletedModel_other": "{{count}} 件のモデルを削除しました",
|
"deletedModel_other": "{{count}} 件のモデルを削除しました",
|
||||||
"updatedModel": "モデル設定を更新しました",
|
"updatedModel": "モデル設定を更新しました",
|
||||||
"renamedCategory": "クラス名を {{name}} に変更しました"
|
"renamedCategory": "クラス名を {{name}} に変更しました"
|
||||||
|
|||||||
@ -36,8 +36,8 @@
|
|||||||
"label": "新しいレビュー項目を表示",
|
"label": "新しいレビュー項目を表示",
|
||||||
"button": "レビューすべき新規項目"
|
"button": "レビューすべき新規項目"
|
||||||
},
|
},
|
||||||
"selected_one": "{{count}} 件選択",
|
"selected_one": "{{count}} 選択済み",
|
||||||
"selected_other": "{{count}} 件選択",
|
"selected_other": "{{count}} 選択済み",
|
||||||
"detected": "検出",
|
"detected": "検出",
|
||||||
"suspiciousActivity": "不審なアクティビティ",
|
"suspiciousActivity": "不審なアクティビティ",
|
||||||
"threateningActivity": "脅威となるアクティビティ",
|
"threateningActivity": "脅威となるアクティビティ",
|
||||||
|
|||||||
@ -224,7 +224,7 @@
|
|||||||
"dialog": {
|
"dialog": {
|
||||||
"confirmDelete": {
|
"confirmDelete": {
|
||||||
"title": "削除の確認",
|
"title": "削除の確認",
|
||||||
"desc": "この追跡オブジェクトを削除すると、スナップショット、保存された埋め込み、および関連する追跡詳細項目が削除されます。履歴ビューの録画映像は削除<em>されません</em>。<br /><br />続行してもよろしいですか?"
|
"desc": "この追跡オブジェクトを削除すると、スナップショット、保存された埋め込み、および関連する追跡詳細項目が削除されます。履歴ビューの録画映像は<em>削除されません。</em><br /><br />続行してもよろしいですか?"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"noTrackedObjects": "追跡オブジェクトは見つかりませんでした",
|
"noTrackedObjects": "追跡オブジェクトは見つかりませんでした",
|
||||||
|
|||||||
@ -1,19 +1,19 @@
|
|||||||
{
|
{
|
||||||
"documentTitle": "エクスポート - Frigate",
|
"documentTitle": "エクスポート - Frigate",
|
||||||
"noExports": "書き出しは見つかりません",
|
"noExports": "エクスポートが見つかりません",
|
||||||
"search": "検索",
|
"search": "検索",
|
||||||
"deleteExport": {
|
"deleteExport": {
|
||||||
"label": "エクスポートを削除"
|
"label": "エクスポートを削除"
|
||||||
},
|
},
|
||||||
"deleteExport.desc": "{{exportName}} を削除してもよろしいですか?",
|
"deleteExport.desc": "{{exportName}} を削除してもよろしいですか?",
|
||||||
"editExport": {
|
"editExport": {
|
||||||
"title": "書き出し名を変更",
|
"title": "エクスポート名を変更",
|
||||||
"desc": "この書き出しの新しい名前を入力してください。",
|
"desc": "このエクスポートの新しい名前を入力してください。",
|
||||||
"saveExport": "書き出しを保存"
|
"saveExport": "エクスポートを保存"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"error": {
|
"error": {
|
||||||
"renameExportFailed": "書き出し名の変更に失敗しました: {{errorMessage}}",
|
"renameExportFailed": "エクスポート名の変更に失敗しました: {{errorMessage}}",
|
||||||
"assignCaseFailed": "ケース割り当ての更新に失敗しました: {{errorMessage}}",
|
"assignCaseFailed": "ケース割り当ての更新に失敗しました: {{errorMessage}}",
|
||||||
"caseSaveFailed": "ケースの保存に失敗しました: {{errorMessage}}",
|
"caseSaveFailed": "ケースの保存に失敗しました: {{errorMessage}}",
|
||||||
"caseDeleteFailed": "ケースの削除に失敗しました: {{errorMessage}}"
|
"caseDeleteFailed": "ケースの削除に失敗しました: {{errorMessage}}"
|
||||||
@ -47,6 +47,81 @@
|
|||||||
"caseDialog": {
|
"caseDialog": {
|
||||||
"title": "ケースに追加",
|
"title": "ケースに追加",
|
||||||
"description": "既存のケースを選択するか、新しいケースを作成してください。",
|
"description": "既存のケースを選択するか、新しいケースを作成してください。",
|
||||||
"selectLabel": "ケース"
|
"selectLabel": "ケース",
|
||||||
|
"newCaseOption": "新しいケースを作成",
|
||||||
|
"nameLabel": "ケース名",
|
||||||
|
"descriptionLabel": "説明"
|
||||||
|
},
|
||||||
|
"caseCard": {
|
||||||
|
"emptyCase": "まだエクスポートされていません"
|
||||||
|
},
|
||||||
|
"jobCard": {
|
||||||
|
"defaultName": "{{camera}} エクスポート",
|
||||||
|
"queued": "キューに追加しました",
|
||||||
|
"running": "実行中",
|
||||||
|
"preparing": "準備中",
|
||||||
|
"copying": "コピー中",
|
||||||
|
"encoding": "エンコード中",
|
||||||
|
"encodingRetry": "エンコード中 (再試行)",
|
||||||
|
"finalizing": "終了処理中"
|
||||||
|
},
|
||||||
|
"caseView": {
|
||||||
|
"noDescription": "説明がありません",
|
||||||
|
"exportCount_one": "1 件のエクスポート",
|
||||||
|
"exportCount_other": "{{count}} エクスポート",
|
||||||
|
"cameraCount_other": "{{count}} カメラ",
|
||||||
|
"showMore": "さらに表示",
|
||||||
|
"showLess": "表示を減らす",
|
||||||
|
"emptyTitle": "このケースは空です",
|
||||||
|
"emptyDescription": "既存の分類されていないエクスポートを追加して、ケースを整理しましょう。",
|
||||||
|
"emptyDescriptionNoExports": "まだ追加可能な未分類のエクスポートはありません。",
|
||||||
|
"createdAt": "作成日 {{value}}"
|
||||||
|
},
|
||||||
|
"caseEditor": {
|
||||||
|
"createTitle": "ケースを作成",
|
||||||
|
"editTitle": "ケースを編集",
|
||||||
|
"namePlaceholder": "ケース名",
|
||||||
|
"descriptionPlaceholder": "このケースに関するメモや背景情報を追加する"
|
||||||
|
},
|
||||||
|
"addExportDialog": {
|
||||||
|
"title": "{{caseName}} にエクスポートを追加",
|
||||||
|
"searchPlaceholder": "未分類のエクスポートを検索",
|
||||||
|
"empty": "この検索条件に一致する未分類のエクスポートはありません。",
|
||||||
|
"addButton_one": "1 件のエクスポートを追加",
|
||||||
|
"addButton_other": "{{count}} 件のエクスポートを追加",
|
||||||
|
"adding": "追加中..."
|
||||||
|
},
|
||||||
|
"selected_one": "{{count}} 選択済み",
|
||||||
|
"selected_other": "{{count}} 選択済み",
|
||||||
|
"bulkActions": {
|
||||||
|
"addToCase": "ケースに追加",
|
||||||
|
"moveToCase": "ケースに移動",
|
||||||
|
"removeFromCase": "ケースから削除",
|
||||||
|
"delete": "削除",
|
||||||
|
"deleteNow": "今すぐ削除"
|
||||||
|
},
|
||||||
|
"bulkDelete": {
|
||||||
|
"title": "エクスポートを削除",
|
||||||
|
"desc_one": "{{count}} 件のエクスポートを削除してもよろしいですか?",
|
||||||
|
"desc_other": "{{count}} 件のエクスポートを削除してもよろしいですか?"
|
||||||
|
},
|
||||||
|
"bulkRemoveFromCase": {
|
||||||
|
"title": "ケースから削除",
|
||||||
|
"desc_one": "このケースから {{count}} 件のエクスポートを削除しますか?",
|
||||||
|
"desc_other": "このケースから {{count}} 件のエクスポートを削除しますか?",
|
||||||
|
"descKeepExports": "エクスポートは未分類に移動されます。",
|
||||||
|
"descDeleteExports": "エクスポートは完全に削除されます。",
|
||||||
|
"deleteExports": "代わりにエクスポートを削除する"
|
||||||
|
},
|
||||||
|
"bulkToast": {
|
||||||
|
"success": {
|
||||||
|
"delete": "エクスポートの削除に成功しました",
|
||||||
|
"reassign": "ケース割り当ての更新に成功しました",
|
||||||
|
"remove": "ケースからエクスポートを正常に削除しました"
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"deleteFailed": "エクスポートの削除に失敗しました: {{errorMessage}}",
|
||||||
|
"reassignFailed": "ケース割り当ての更新に失敗しました: {{errorMessage}}"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -244,7 +244,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"motionMaskLabel": "モーションマスク {{number}}",
|
"motionMaskLabel": "モーションマスク {{number}}",
|
||||||
"objectMaskLabel": "オブジェクトマスク {{number}}({{label}})",
|
"objectMaskLabel": "オブジェクトマスク {{number}}",
|
||||||
"form": {
|
"form": {
|
||||||
"zoneName": {
|
"zoneName": {
|
||||||
"error": {
|
"error": {
|
||||||
@ -594,7 +594,7 @@
|
|||||||
"admin": "管理者",
|
"admin": "管理者",
|
||||||
"adminDesc": "すべての機能にフルアクセス。",
|
"adminDesc": "すべての機能にフルアクセス。",
|
||||||
"viewer": "閲覧者",
|
"viewer": "閲覧者",
|
||||||
"viewerDesc": "ライブ、レビュー、探索、書き出しに限定。",
|
"viewerDesc": "ライブ、レビュー、探索、エクスポートに限定。",
|
||||||
"customDesc": "特定のカメラアクセスを持つカスタムロール。"
|
"customDesc": "特定のカメラアクセスを持つカスタムロール。"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -725,7 +725,7 @@
|
|||||||
"snapshotConfig": {
|
"snapshotConfig": {
|
||||||
"title": "スナップショット設定",
|
"title": "スナップショット設定",
|
||||||
"desc": "Frigate+ への送信には、設定でスナップショットと <code>clean_copy</code> スナップショットの両方を有効にする必要があります。",
|
"desc": "Frigate+ への送信には、設定でスナップショットと <code>clean_copy</code> スナップショットの両方を有効にする必要があります。",
|
||||||
"cleanCopyWarning": "一部のカメラではスナップショットは有効ですが、クリーンコピーが無効です。これらのカメラから Frigate+ へ画像を送信するには、スナップショット設定で <code>clean_copy</code> を有効にしてください。",
|
"cleanCopyWarning": "一部のカメラではスナップショット機能が無効になっています",
|
||||||
"table": {
|
"table": {
|
||||||
"camera": "カメラ",
|
"camera": "カメラ",
|
||||||
"snapshots": "スナップショット",
|
"snapshots": "スナップショット",
|
||||||
@ -1227,5 +1227,12 @@
|
|||||||
"success": "レビュー分類の設定を保存しました。変更を適用するには Frigate を再起動してください。"
|
"success": "レビュー分類の設定を保存しました。変更を適用するには Frigate を再起動してください。"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"maintenance": {
|
||||||
|
"sync": {
|
||||||
|
"status": {
|
||||||
|
"queued": "キューに追加済み"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -178,6 +178,14 @@
|
|||||||
"markAsReviewed": "Merk som inspisert",
|
"markAsReviewed": "Merk som inspisert",
|
||||||
"deleteNow": "Slett nå",
|
"deleteNow": "Slett nå",
|
||||||
"markAsUnreviewed": "Merk som ikke inspisert"
|
"markAsUnreviewed": "Merk som ikke inspisert"
|
||||||
|
},
|
||||||
|
"shareTimestamp": {
|
||||||
|
"description": "Del en tidsstemplet URL fra avspillerens nåværende posisjon, eller velg et egendefinert tidsstempel. Merk at dette ikke er en offentlig delingslenke, og at den kun er tilgjengelig for brukere med tilgang til Frigate og dette kameraet.",
|
||||||
|
"custom": "Egendefinert tidsstempel",
|
||||||
|
"title": "Del tidsstempel",
|
||||||
|
"label": "Del tidsstempel",
|
||||||
|
"button": "Del URL med tidsstempel",
|
||||||
|
"shareTitle": "Tidsstempel for Frigate-inspeksjon: {{camera}}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"imagePicker": {
|
"imagePicker": {
|
||||||
|
|||||||
@ -32,7 +32,8 @@
|
|||||||
"noPreviewFoundFor": "Ingen forhåndsvisning funnet for {{cameraName}}",
|
"noPreviewFoundFor": "Ingen forhåndsvisning funnet for {{cameraName}}",
|
||||||
"submitFrigatePlus": {
|
"submitFrigatePlus": {
|
||||||
"title": "Send dette bildet til Frigate+?",
|
"title": "Send dette bildet til Frigate+?",
|
||||||
"submit": "Send"
|
"submit": "Send",
|
||||||
|
"previewError": "Kunne ikke laste forhåndsvisning av stillbilde. Opptaket er kanskje ikke tilgjengelig for øyeblikket."
|
||||||
},
|
},
|
||||||
"livePlayerRequiredIOSVersion": "iOS 17.1 eller høyere kreves for denne typen direkte-strømming.",
|
"livePlayerRequiredIOSVersion": "iOS 17.1 eller høyere kreves for denne typen direkte-strømming.",
|
||||||
"streamOffline": {
|
"streamOffline": {
|
||||||
|
|||||||
@ -31,7 +31,9 @@
|
|||||||
"timeline.aria": "Velg tidslinje",
|
"timeline.aria": "Velg tidslinje",
|
||||||
"documentTitle": "Inspeksjon - Frigate",
|
"documentTitle": "Inspeksjon - Frigate",
|
||||||
"recordings": {
|
"recordings": {
|
||||||
"documentTitle": "Opptak - Frigate"
|
"documentTitle": "Opptak - Frigate",
|
||||||
|
"invalidSharedLink": "Kunne ikke åpne tidsstemplet opptakslenke på grunn av tolkningsfeil.",
|
||||||
|
"invalidSharedCamera": "Kunne ikke åpne tidsstemplet opptakslenke på grunn av et ukjent eller uautorisert kamera."
|
||||||
},
|
},
|
||||||
"calendarFilter": {
|
"calendarFilter": {
|
||||||
"last24Hours": "Siste 24 timer"
|
"last24Hours": "Siste 24 timer"
|
||||||
|
|||||||
@ -59,6 +59,14 @@
|
|||||||
"desc": {
|
"desc": {
|
||||||
"selected": "Ești sigur că vrei să ștergi toate videoclipurile înregistrate asociate acestui element de revizuire?<br /><br />Ține apăsată tasta <em>Shift</em> pentru a sări peste această confirmare pe viitor."
|
"selected": "Ești sigur că vrei să ștergi toate videoclipurile înregistrate asociate acestui element de revizuire?<br /><br />Ține apăsată tasta <em>Shift</em> pentru a sări peste această confirmare pe viitor."
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"shareTimestamp": {
|
||||||
|
"label": "Partajează timestamp-ul",
|
||||||
|
"title": "Partajează timestamp-ul",
|
||||||
|
"description": "Partajează un URL cu timestamp al poziției actuale din player sau alege un timestamp personalizat. Reține că acesta nu este un URL de partajare publică și este accesibil doar utilizatorilor care au acces la Frigate și la această cameră.",
|
||||||
|
"custom": "Timestamp personalizat",
|
||||||
|
"button": "Partajează URL-ul cu timestamp",
|
||||||
|
"shareTitle": "Timestamp review Frigate: {{camera}}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"export": {
|
"export": {
|
||||||
|
|||||||
@ -4,7 +4,8 @@
|
|||||||
"noPreviewFoundFor": "Nu există previzualizari pentru {{cameraName}}",
|
"noPreviewFoundFor": "Nu există previzualizari pentru {{cameraName}}",
|
||||||
"submitFrigatePlus": {
|
"submitFrigatePlus": {
|
||||||
"title": "Trimiteti acest cadru catre Frigate+?",
|
"title": "Trimiteti acest cadru catre Frigate+?",
|
||||||
"submit": "Trimite"
|
"submit": "Trimite",
|
||||||
|
"previewError": "Nu s-a putut încărca previzualizarea snapshot-ului. S-ar putea ca înregistrarea să nu fie disponibilă în acest moment."
|
||||||
},
|
},
|
||||||
"livePlayerRequiredIOSVersion": "iOS 17.1 sau mai recent este necesar pentru acest tip de stream live.",
|
"livePlayerRequiredIOSVersion": "iOS 17.1 sau mai recent este necesar pentru acest tip de stream live.",
|
||||||
"streamOffline": {
|
"streamOffline": {
|
||||||
|
|||||||
@ -25,7 +25,9 @@
|
|||||||
},
|
},
|
||||||
"documentTitle": "Revizuieste - Frigate",
|
"documentTitle": "Revizuieste - Frigate",
|
||||||
"recordings": {
|
"recordings": {
|
||||||
"documentTitle": "Inregistrari - frigate"
|
"documentTitle": "Inregistrari - frigate",
|
||||||
|
"invalidSharedLink": "Nu s-a putut deschide linkul înregistrării cu timestamp din cauza unei erori de parsare.",
|
||||||
|
"invalidSharedCamera": "Nu s-a putut deschide linkul înregistrării cu timestamp din cauza unei camere necunoscute sau neautorizate."
|
||||||
},
|
},
|
||||||
"calendarFilter": {
|
"calendarFilter": {
|
||||||
"last24Hours": "Ultimele 24 de ore"
|
"last24Hours": "Ultimele 24 de ore"
|
||||||
|
|||||||
@ -218,7 +218,7 @@ export default function CameraReviewClassification({
|
|||||||
<Label
|
<Label
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex flex-row items-center text-base",
|
"flex flex-row items-center text-base",
|
||||||
alertsZonesModified && "text-danger",
|
alertsZonesModified && "text-unsaved",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Trans ns="views/settings">cameraReview.review.alerts</Trans>
|
<Trans ns="views/settings">cameraReview.review.alerts</Trans>
|
||||||
@ -286,7 +286,7 @@ export default function CameraReviewClassification({
|
|||||||
<Label
|
<Label
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex flex-row items-center text-base",
|
"flex flex-row items-center text-base",
|
||||||
detectionsZonesModified && "text-danger",
|
detectionsZonesModified && "text-unsaved",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Trans ns="views/settings">
|
<Trans ns="views/settings">
|
||||||
|
|||||||
@ -1012,7 +1012,7 @@ export function ConfigSection({
|
|||||||
>
|
>
|
||||||
{hasChanges && (
|
{hasChanges && (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span className="text-sm text-danger">
|
<span className="text-sm text-unsaved">
|
||||||
{t("unsavedChanges", {
|
{t("unsavedChanges", {
|
||||||
ns: "views/settings",
|
ns: "views/settings",
|
||||||
defaultValue: "You have unsaved changes",
|
defaultValue: "You have unsaved changes",
|
||||||
@ -1299,7 +1299,7 @@ export function ConfigSection({
|
|||||||
{hasChanges && (
|
{hasChanges && (
|
||||||
<Badge
|
<Badge
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
className="cursor-default bg-danger text-xs text-white hover:bg-danger"
|
className="cursor-default bg-unsaved text-xs text-black hover:bg-unsaved"
|
||||||
>
|
>
|
||||||
{t("button.modified", {
|
{t("button.modified", {
|
||||||
ns: "common",
|
ns: "common",
|
||||||
|
|||||||
@ -154,7 +154,7 @@ export function KnownPlatesField(props: FieldProps) {
|
|||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<CardTitle
|
<CardTitle
|
||||||
className={cn("text-sm", isModified && "text-danger")}
|
className={cn("text-sm", isModified && "text-unsaved")}
|
||||||
>
|
>
|
||||||
{title}
|
{title}
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
|
|||||||
@ -142,7 +142,7 @@ export function ReplaceRulesField(props: FieldProps) {
|
|||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<CardTitle
|
<CardTitle
|
||||||
className={cn("text-sm", isModified && "text-danger")}
|
className={cn("text-sm", isModified && "text-unsaved")}
|
||||||
>
|
>
|
||||||
{title}
|
{title}
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
|
|||||||
@ -497,7 +497,7 @@ export function FieldTemplate(props: FieldTemplateProps) {
|
|||||||
htmlFor={id}
|
htmlFor={id}
|
||||||
className={cn(
|
className={cn(
|
||||||
"text-sm font-medium",
|
"text-sm font-medium",
|
||||||
isModified && "text-danger",
|
isModified && "text-unsaved",
|
||||||
hasFieldErrors && "text-destructive",
|
hasFieldErrors && "text-destructive",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@ -516,7 +516,7 @@ export function FieldTemplate(props: FieldTemplateProps) {
|
|||||||
return (
|
return (
|
||||||
<Label
|
<Label
|
||||||
htmlFor={id}
|
htmlFor={id}
|
||||||
className={cn("text-sm font-medium", isModified && "text-danger")}
|
className={cn("text-sm font-medium", isModified && "text-unsaved")}
|
||||||
>
|
>
|
||||||
{finalLabel}
|
{finalLabel}
|
||||||
{required && <span className="ml-1 text-destructive">*</span>}
|
{required && <span className="ml-1 text-destructive">*</span>}
|
||||||
@ -535,7 +535,7 @@ export function FieldTemplate(props: FieldTemplateProps) {
|
|||||||
htmlFor={id}
|
htmlFor={id}
|
||||||
className={cn(
|
className={cn(
|
||||||
"text-sm font-medium",
|
"text-sm font-medium",
|
||||||
isModified && "text-danger",
|
isModified && "text-unsaved",
|
||||||
hasFieldErrors && "text-destructive",
|
hasFieldErrors && "text-destructive",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -467,7 +467,7 @@ export function ObjectFieldTemplate(props: ObjectFieldTemplateProps) {
|
|||||||
<CardTitle
|
<CardTitle
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex items-center text-sm",
|
"flex items-center text-sm",
|
||||||
hasModifiedDescendants && "text-danger",
|
hasModifiedDescendants && "text-unsaved",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{inferredLabel}
|
{inferredLabel}
|
||||||
|
|||||||
@ -607,23 +607,38 @@ function StreamIssues({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stream.roles.includes("detect") && stream.resolution) {
|
if (stream.roles.includes("detect") && stream.testResult) {
|
||||||
const [width, height] = stream.resolution.split("x").map(Number);
|
const probedResolution = stream.testResult.resolution;
|
||||||
if (!isNaN(width) && !isNaN(height) && width > 0 && height > 0) {
|
let probedWidth = 0;
|
||||||
const minDimension = Math.min(width, height);
|
let probedHeight = 0;
|
||||||
const maxDimension = Math.max(width, height);
|
if (probedResolution) {
|
||||||
|
const [w, h] = probedResolution.split("x").map(Number);
|
||||||
|
if (!isNaN(w) && !isNaN(h)) {
|
||||||
|
probedWidth = w;
|
||||||
|
probedHeight = h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (probedWidth <= 0 || probedHeight <= 0) {
|
||||||
|
result.push({
|
||||||
|
type: "error",
|
||||||
|
message: t("cameraWizard.step4.issues.resolutionUnknown"),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const minDimension = Math.min(probedWidth, probedHeight);
|
||||||
|
const maxDimension = Math.max(probedWidth, probedHeight);
|
||||||
if (minDimension > 1080) {
|
if (minDimension > 1080) {
|
||||||
result.push({
|
result.push({
|
||||||
type: "warning",
|
type: "warning",
|
||||||
message: t("cameraWizard.step4.issues.resolutionHigh", {
|
message: t("cameraWizard.step4.issues.resolutionHigh", {
|
||||||
resolution: stream.resolution,
|
resolution: probedResolution,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
} else if (maxDimension < 640) {
|
} else if (maxDimension < 640) {
|
||||||
result.push({
|
result.push({
|
||||||
type: "error",
|
type: "error",
|
||||||
message: t("cameraWizard.step4.issues.resolutionLow", {
|
message: t("cameraWizard.step4.issues.resolutionLow", {
|
||||||
resolution: stream.resolution,
|
resolution: probedResolution,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1435,7 +1435,7 @@ export default function Settings() {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{showUnsavedDot && (
|
{showUnsavedDot && (
|
||||||
<span className="inline-block size-2 rounded-full bg-danger" />
|
<span className="inline-block size-2 rounded-full bg-unsaved" />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -1516,7 +1516,7 @@ export default function Settings() {
|
|||||||
<div className="sticky bottom-0 z-50 mt-2 bg-background p-4">
|
<div className="sticky bottom-0 z-50 mt-2 bg-background p-4">
|
||||||
<div className="flex flex-col items-center gap-2">
|
<div className="flex flex-col items-center gap-2">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span className="text-sm text-danger">
|
<span className="text-sm text-unsaved">
|
||||||
{t("unsavedChanges", {
|
{t("unsavedChanges", {
|
||||||
ns: "views/settings",
|
ns: "views/settings",
|
||||||
defaultValue: "You have unsaved changes",
|
defaultValue: "You have unsaved changes",
|
||||||
|
|||||||
@ -79,11 +79,11 @@ const PROFILE_COLORS: ProfileColor[] = [
|
|||||||
bgMuted: "bg-green-400/20",
|
bgMuted: "bg-green-400/20",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
bg: "bg-amber-400",
|
bg: "bg-fuchsia-500",
|
||||||
text: "text-amber-400",
|
text: "text-fuchsia-500",
|
||||||
dot: "bg-amber-400",
|
dot: "bg-fuchsia-500",
|
||||||
border: "border-amber-400",
|
border: "border-fuchsia-500",
|
||||||
bgMuted: "bg-amber-400/20",
|
bgMuted: "bg-fuchsia-500/20",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
bg: "bg-slate-400",
|
bg: "bg-slate-400",
|
||||||
@ -93,11 +93,11 @@ const PROFILE_COLORS: ProfileColor[] = [
|
|||||||
bgMuted: "bg-slate-400/20",
|
bgMuted: "bg-slate-400/20",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
bg: "bg-orange-300",
|
bg: "bg-stone-500",
|
||||||
text: "text-orange-300",
|
text: "text-stone-500",
|
||||||
dot: "bg-orange-300",
|
dot: "bg-stone-500",
|
||||||
border: "border-orange-300",
|
border: "border-stone-500",
|
||||||
bgMuted: "bg-orange-300/20",
|
bgMuted: "bg-stone-500/20",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
bg: "bg-blue-300",
|
bg: "bg-blue-300",
|
||||||
|
|||||||
@ -380,7 +380,9 @@ export default function Go2RtcStreamsSettingsView({
|
|||||||
>
|
>
|
||||||
{hasChanges && (
|
{hasChanges && (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span className="text-sm text-danger">{t("unsavedChanges")}</span>
|
<span className="text-sm text-unsaved">
|
||||||
|
{t("unsavedChanges")}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="flex w-full items-center gap-2 md:w-auto">
|
<div className="flex w-full items-center gap-2 md:w-auto">
|
||||||
|
|||||||
@ -212,7 +212,7 @@ export function SingleSectionPage({
|
|||||||
{sectionStatus.hasChanges && (
|
{sectionStatus.hasChanges && (
|
||||||
<Badge
|
<Badge
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
className="cursor-default bg-danger text-xs text-white hover:bg-danger"
|
className="cursor-default bg-unsaved text-xs text-black hover:bg-unsaved"
|
||||||
>
|
>
|
||||||
{t("button.modified", {
|
{t("button.modified", {
|
||||||
ns: "common",
|
ns: "common",
|
||||||
@ -250,7 +250,7 @@ export function SingleSectionPage({
|
|||||||
{sectionStatus.hasChanges && (
|
{sectionStatus.hasChanges && (
|
||||||
<Badge
|
<Badge
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
className="cursor-default bg-danger text-xs text-white hover:bg-danger"
|
className="cursor-default bg-unsaved text-xs text-black hover:bg-unsaved"
|
||||||
>
|
>
|
||||||
{t("button.modified", { ns: "common", defaultValue: "Modified" })}
|
{t("button.modified", { ns: "common", defaultValue: "Modified" })}
|
||||||
</Badge>
|
</Badge>
|
||||||
|
|||||||
@ -65,6 +65,7 @@ module.exports = {
|
|||||||
ring: "hsl(var(--ring))",
|
ring: "hsl(var(--ring))",
|
||||||
danger: "#ef4444",
|
danger: "#ef4444",
|
||||||
success: "#22c55e",
|
success: "#22c55e",
|
||||||
|
unsaved: "#f59e0b",
|
||||||
background: "hsl(var(--background))",
|
background: "hsl(var(--background))",
|
||||||
background_alt: "hsl(var(--background-alt))",
|
background_alt: "hsl(var(--background-alt))",
|
||||||
foreground: "hsl(var(--foreground))",
|
foreground: "hsl(var(--foreground))",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user