mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-05-07 14:05:28 +03:00
Compare commits
22 Commits
9f68bb4217
...
38e2cd124f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
38e2cd124f | ||
|
|
76f747f19d | ||
|
|
b0e7b1efde | ||
|
|
b4bcfeb566 | ||
|
|
73542535c0 | ||
|
|
4d988054c3 | ||
|
|
ba2ef536f9 | ||
|
|
36a3daeba6 | ||
|
|
14df935ffd | ||
|
|
697df366d5 | ||
|
|
681d11f3a7 | ||
|
|
00902f36c8 | ||
|
|
fe02e75a6d | ||
|
|
119baf4669 | ||
|
|
7ec28539cd | ||
|
|
d1f46b95a3 | ||
|
|
dbd0ccce28 | ||
|
|
b75ed1d287 | ||
|
|
64380afb1c | ||
|
|
bfd1cf71f2 | ||
|
|
0549b031f5 | ||
|
|
1403c30348 |
2
.github/pull_request_template.md
vendored
2
.github/pull_request_template.md
vendored
@ -26,7 +26,7 @@ _Please read the [contributing guidelines](https://github.com/blakeblackshear/fr
|
||||
|
||||
- This PR fixes or closes issue: fixes #
|
||||
- This PR is related to issue:
|
||||
- Link to discussion with maintainers (**required** for any large or "planned" features):
|
||||
- Link to discussion with maintainers (**required** for large/pinned features):
|
||||
|
||||
## For new features
|
||||
|
||||
|
||||
2
.github/workflows/pull_request.yml
vendored
2
.github/workflows/pull_request.yml
vendored
@ -72,7 +72,7 @@ jobs:
|
||||
run: npm run e2e
|
||||
working-directory: ./web
|
||||
- name: Upload test artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: playwright-report
|
||||
|
||||
4
.github/workflows/stale.yml
vendored
4
.github/workflows/stale.yml
vendored
@ -19,8 +19,8 @@ jobs:
|
||||
days-before-stale: 30
|
||||
days-before-close: 3
|
||||
exempt-draft-pr: true
|
||||
exempt-issue-labels: "planned,security"
|
||||
exempt-pr-labels: "planned,security,dependencies"
|
||||
exempt-issue-labels: "pinned,security"
|
||||
exempt-pr-labels: "pinned,security,dependencies"
|
||||
operations-per-run: 120
|
||||
- name: Print outputs
|
||||
env:
|
||||
|
||||
@ -12,7 +12,7 @@ If you've found a bug and want to fix it, go for it. Link to the relevant issue
|
||||
|
||||
Every new feature adds scope that the maintainers must test, maintain, and support long-term. Before writing code for a new feature:
|
||||
|
||||
1. **Check for existing discussion.** Search [feature requests](https://github.com/blakeblackshear/frigate/issues) and [discussions](https://github.com/blakeblackshear/frigate/discussions) to see if it's been proposed or discussed. Feature requests tagged with "planned" are on our radar — we plan to get to them, but we don't maintain a public roadmap or timeline. Check in with us first if you have interest in contributing to one.
|
||||
1. **Check for existing discussion.** Search [feature requests](https://github.com/blakeblackshear/frigate/issues) and [discussions](https://github.com/blakeblackshear/frigate/discussions) to see if it's been proposed or discussed. Pinned feature requests are on our radar — we plan to get to them, but we don't maintain a public roadmap or timeline. Check in with us first if you have interest in contributing to one.
|
||||
2. **Start a discussion or feature request first.** This helps ensure your idea aligns with Frigate's direction before you invest time building it. Community interest in a feature request helps us gauge demand, though a great idea is a great idea even without a crowd behind it.
|
||||
3. **Be open to "no".** We try to be thoughtful about what we take on, and sometimes that means saying no to good code if the feature isn't the right fit for the project. These calls are sometimes subjective, and we won't always get them right. We're happy to discuss and reconsider.
|
||||
|
||||
|
||||
@ -171,7 +171,7 @@ When choosing images to include in the face training set it is recommended to al
|
||||
- If it is difficult to make out details in a persons face it will not be helpful in training.
|
||||
- Avoid images with extreme under/over-exposure.
|
||||
- Avoid blurry / pixelated images.
|
||||
- Avoid training on infrared (gray-scale). The models are trained on color images and will not be able to extract features from gray-scale images.
|
||||
- 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.
|
||||
|
||||
|
||||
@ -1368,17 +1368,12 @@ def preview_gif(
|
||||
file_start = f"preview_{camera_name}-"
|
||||
start_file = f"{file_start}{start_ts}.{PREVIEW_FRAME_TYPE}"
|
||||
end_file = f"{file_start}{end_ts}.{PREVIEW_FRAME_TYPE}"
|
||||
|
||||
camera_files = [
|
||||
entry.name
|
||||
for entry in os.scandir(preview_dir)
|
||||
if entry.name.startswith(file_start)
|
||||
]
|
||||
camera_files.sort()
|
||||
|
||||
selected_previews = []
|
||||
|
||||
for file in camera_files:
|
||||
for file in sorted(os.listdir(preview_dir)):
|
||||
if not file.startswith(file_start):
|
||||
continue
|
||||
|
||||
if file < start_file:
|
||||
continue
|
||||
|
||||
@ -1555,17 +1550,12 @@ def preview_mp4(
|
||||
file_start = f"preview_{camera_name}-"
|
||||
start_file = f"{file_start}{start_ts}.{PREVIEW_FRAME_TYPE}"
|
||||
end_file = f"{file_start}{end_ts}.{PREVIEW_FRAME_TYPE}"
|
||||
|
||||
camera_files = [
|
||||
entry.name
|
||||
for entry in os.scandir(preview_dir)
|
||||
if entry.name.startswith(file_start)
|
||||
]
|
||||
camera_files.sort()
|
||||
|
||||
selected_previews = []
|
||||
|
||||
for file in camera_files:
|
||||
for file in sorted(os.listdir(preview_dir)):
|
||||
if not file.startswith(file_start):
|
||||
continue
|
||||
|
||||
if file < start_file:
|
||||
continue
|
||||
|
||||
|
||||
@ -148,17 +148,12 @@ def get_preview_frames_from_cache(camera_name: str, start_ts: float, end_ts: flo
|
||||
file_start = f"preview_{camera_name}-"
|
||||
start_file = f"{file_start}{start_ts}.{PREVIEW_FRAME_TYPE}"
|
||||
end_file = f"{file_start}{end_ts}.{PREVIEW_FRAME_TYPE}"
|
||||
|
||||
camera_files = [
|
||||
entry.name
|
||||
for entry in os.scandir(preview_dir)
|
||||
if entry.name.startswith(file_start)
|
||||
]
|
||||
camera_files.sort()
|
||||
|
||||
selected_previews = []
|
||||
|
||||
for file in camera_files:
|
||||
for file in sorted(os.listdir(preview_dir)):
|
||||
if not file.startswith(file_start):
|
||||
continue
|
||||
|
||||
if file < start_file:
|
||||
continue
|
||||
|
||||
|
||||
@ -15,7 +15,7 @@ TRIGGER_DIR = f"{CLIPS_DIR}/triggers"
|
||||
BIRDSEYE_PIPE = "/tmp/cache/birdseye"
|
||||
CACHE_DIR = "/tmp/cache"
|
||||
REPLAY_CAMERA_PREFIX = "_replay_"
|
||||
REPLAY_DIR = os.path.join(CLIPS_DIR, "replay")
|
||||
REPLAY_DIR = os.path.join(CACHE_DIR, "replay")
|
||||
PLUS_ENV_VAR = "PLUS_API_KEY"
|
||||
PLUS_API_HOST = "https://api.frigate.video"
|
||||
|
||||
|
||||
@ -366,17 +366,12 @@ class ReviewDescriptionProcessor(PostProcessorApi):
|
||||
file_start = f"preview_{camera}-"
|
||||
start_file = f"{file_start}{start_time}.webp"
|
||||
end_file = f"{file_start}{end_time}.webp"
|
||||
|
||||
camera_files = [
|
||||
entry.name
|
||||
for entry in os.scandir(preview_dir)
|
||||
if entry.name.startswith(file_start)
|
||||
]
|
||||
camera_files.sort()
|
||||
|
||||
all_frames: list[str] = []
|
||||
|
||||
for file in camera_files:
|
||||
for file in sorted(os.listdir(preview_dir)):
|
||||
if not file.startswith(file_start):
|
||||
continue
|
||||
|
||||
if file < start_file:
|
||||
if len(all_frames):
|
||||
all_frames[0] = os.path.join(preview_dir, file)
|
||||
|
||||
@ -4,7 +4,6 @@ import base64
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import threading
|
||||
from json.decoder import JSONDecodeError
|
||||
from multiprocessing.synchronize import Event as MpEvent
|
||||
@ -53,14 +52,6 @@ class EmbeddingProcess(FrigateProcess):
|
||||
self.stop_event,
|
||||
)
|
||||
maintainer.start()
|
||||
maintainer.join()
|
||||
|
||||
# If the maintainer thread exited but no shutdown was requested, it
|
||||
# crashed. Surface as a non-zero exit so the watchdog restarts us
|
||||
# instead of treating the silent thread death as a clean shutdown.
|
||||
if not self.stop_event.is_set():
|
||||
logger.error("Embeddings maintainer thread exited unexpectedly")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
class EmbeddingsContext:
|
||||
|
||||
@ -153,6 +153,9 @@ Each line represents a detection state, not necessarily unique individuals. The
|
||||
if "other_concerns" in schema.get("required", []):
|
||||
schema["required"].remove("other_concerns")
|
||||
|
||||
# OpenAI strict mode requires additionalProperties: false on all objects
|
||||
schema["additionalProperties"] = False
|
||||
|
||||
response_format = {
|
||||
"type": "json_schema",
|
||||
"json_schema": {
|
||||
|
||||
@ -136,44 +136,22 @@ class GeminiClient(GenAIClient):
|
||||
)
|
||||
)
|
||||
elif role == "assistant":
|
||||
parts: list[types.Part] = []
|
||||
if content:
|
||||
parts.append(types.Part.from_text(text=content))
|
||||
for tc in msg.get("tool_calls") or []:
|
||||
func = tc.get("function") or {}
|
||||
tc_name = func.get("name") or ""
|
||||
tc_args: Any = func.get("arguments")
|
||||
if isinstance(tc_args, str):
|
||||
try:
|
||||
tc_args = json.loads(tc_args)
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
tc_args = {}
|
||||
if not isinstance(tc_args, dict):
|
||||
tc_args = {}
|
||||
if tc_name:
|
||||
parts.append(
|
||||
types.Part.from_function_call(
|
||||
name=tc_name, args=tc_args
|
||||
)
|
||||
)
|
||||
if not parts:
|
||||
parts.append(types.Part.from_text(text=" "))
|
||||
gemini_messages.append(types.Content(role="model", parts=parts))
|
||||
gemini_messages.append(
|
||||
types.Content(
|
||||
role="model", parts=[types.Part.from_text(text=content)]
|
||||
)
|
||||
)
|
||||
elif role == "tool":
|
||||
# Handle tool response
|
||||
response_payload = (
|
||||
content if isinstance(content, dict) else {"result": content}
|
||||
)
|
||||
function_response = {
|
||||
"name": msg.get("name", ""),
|
||||
"response": content,
|
||||
}
|
||||
gemini_messages.append(
|
||||
types.Content(
|
||||
role="function",
|
||||
parts=[
|
||||
types.Part.from_function_response(
|
||||
name=msg.get("name")
|
||||
or msg.get("tool_call_id")
|
||||
or "",
|
||||
response=response_payload,
|
||||
)
|
||||
types.Part.from_function_response(function_response) # type: ignore[misc,call-arg,arg-type]
|
||||
],
|
||||
)
|
||||
)
|
||||
@ -365,44 +343,22 @@ class GeminiClient(GenAIClient):
|
||||
)
|
||||
)
|
||||
elif role == "assistant":
|
||||
parts: list[types.Part] = []
|
||||
if content:
|
||||
parts.append(types.Part.from_text(text=content))
|
||||
for tc in msg.get("tool_calls") or []:
|
||||
func = tc.get("function") or {}
|
||||
tc_name = func.get("name") or ""
|
||||
tc_args: Any = func.get("arguments")
|
||||
if isinstance(tc_args, str):
|
||||
try:
|
||||
tc_args = json.loads(tc_args)
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
tc_args = {}
|
||||
if not isinstance(tc_args, dict):
|
||||
tc_args = {}
|
||||
if tc_name:
|
||||
parts.append(
|
||||
types.Part.from_function_call(
|
||||
name=tc_name, args=tc_args
|
||||
)
|
||||
)
|
||||
if not parts:
|
||||
parts.append(types.Part.from_text(text=" "))
|
||||
gemini_messages.append(types.Content(role="model", parts=parts))
|
||||
gemini_messages.append(
|
||||
types.Content(
|
||||
role="model", parts=[types.Part.from_text(text=content)]
|
||||
)
|
||||
)
|
||||
elif role == "tool":
|
||||
# Handle tool response
|
||||
response_payload = (
|
||||
content if isinstance(content, dict) else {"result": content}
|
||||
)
|
||||
function_response = {
|
||||
"name": msg.get("name", ""),
|
||||
"response": content,
|
||||
}
|
||||
gemini_messages.append(
|
||||
types.Content(
|
||||
role="function",
|
||||
parts=[
|
||||
types.Part.from_function_response(
|
||||
name=msg.get("name")
|
||||
or msg.get("tool_call_id")
|
||||
or "",
|
||||
response=response_payload,
|
||||
)
|
||||
types.Part.from_function_response(function_response) # type: ignore[misc,call-arg,arg-type]
|
||||
],
|
||||
)
|
||||
)
|
||||
|
||||
@ -44,7 +44,6 @@ class LlamaCppClient(GenAIClient):
|
||||
_supports_tools: bool
|
||||
_image_token_cache: dict[tuple[int, int], int]
|
||||
_text_baseline_tokens: int | None
|
||||
_media_marker: str
|
||||
|
||||
def _init_provider(self) -> str | None:
|
||||
"""Initialize the client and query model metadata from the server."""
|
||||
@ -57,7 +56,6 @@ class LlamaCppClient(GenAIClient):
|
||||
self._supports_tools = False
|
||||
self._image_token_cache = {}
|
||||
self._text_baseline_tokens = None
|
||||
self._media_marker = "<__media__>"
|
||||
|
||||
base_url = (
|
||||
self.genai_config.base_url.rstrip("/")
|
||||
@ -143,13 +141,6 @@ class LlamaCppClient(GenAIClient):
|
||||
chat_caps = props.get("chat_template_caps", {})
|
||||
self._supports_tools = chat_caps.get("supports_tools", False)
|
||||
|
||||
# Media marker for multimodal embeddings; the server randomizes this
|
||||
# per startup unless LLAMA_MEDIA_MARKER is set, so we must read it
|
||||
# from /props rather than hardcoding "<__media__>".
|
||||
media_marker = props.get("media_marker")
|
||||
if isinstance(media_marker, str) and media_marker:
|
||||
self._media_marker = media_marker
|
||||
|
||||
logger.info(
|
||||
"llama.cpp model '%s' initialized — context: %s, vision: %s, audio: %s, tools: %s",
|
||||
configured_model,
|
||||
@ -474,11 +465,10 @@ class LlamaCppClient(GenAIClient):
|
||||
jpeg_bytes = _to_jpeg(img)
|
||||
to_encode = jpeg_bytes if jpeg_bytes is not None else img
|
||||
encoded = base64.b64encode(to_encode).decode("utf-8")
|
||||
# prompt_string must contain the server's media marker placeholder.
|
||||
# The marker is randomized per server startup (read from /props).
|
||||
# prompt_string must contain <__media__> placeholder for image tokenization
|
||||
content.append(
|
||||
{
|
||||
"prompt_string": f"{self._media_marker}\n",
|
||||
"prompt_string": "<__media__>\n",
|
||||
"multimodal_data": [encoded], # type: ignore[dict-item]
|
||||
}
|
||||
)
|
||||
|
||||
@ -73,17 +73,8 @@ class OpenAIClient(GenAIClient):
|
||||
**self.genai_config.runtime_options,
|
||||
}
|
||||
if response_format:
|
||||
# OpenAI strict mode requires additionalProperties: false on the schema
|
||||
if response_format.get("type") == "json_schema" and response_format.get(
|
||||
"json_schema", {}
|
||||
).get("strict"):
|
||||
schema = response_format.get("json_schema", {}).get("schema")
|
||||
if isinstance(schema, dict):
|
||||
schema["additionalProperties"] = False
|
||||
request_params["response_format"] = response_format
|
||||
|
||||
result = self.provider.chat.completions.create(**request_params)
|
||||
|
||||
if (
|
||||
result is not None
|
||||
and hasattr(result, "choices")
|
||||
|
||||
@ -882,7 +882,7 @@ class Birdseye:
|
||||
coordinates = self.birdseye_manager.get_camera_coordinates()
|
||||
self.requestor.send_data(UPDATE_BIRDSEYE_LAYOUT, coordinates)
|
||||
if self._idle_interval:
|
||||
now = datetime.datetime.now().timestamp()
|
||||
now = time.monotonic()
|
||||
is_idle = len(self.birdseye_manager.camera_layout) == 0
|
||||
if (
|
||||
is_idle
|
||||
|
||||
@ -24,7 +24,7 @@ from frigate.config.camera.updater import (
|
||||
)
|
||||
from frigate.const import PROCESS_PRIORITY_HIGH
|
||||
from frigate.log import LogPipe
|
||||
from frigate.util.builtin import EventsPerSecond, get_ffmpeg_arg_list
|
||||
from frigate.util.builtin import EventsPerSecond
|
||||
from frigate.util.ffmpeg import start_or_restart_ffmpeg, stop_ffmpeg
|
||||
from frigate.util.image import (
|
||||
FrameManager,
|
||||
@ -34,23 +34,6 @@ from frigate.util.process import FrigateProcess
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# all built-in record presets use this segment_time
|
||||
DEFAULT_RECORD_SEGMENT_TIME = 10
|
||||
|
||||
|
||||
def _get_record_segment_time(config: CameraConfig) -> int:
|
||||
"""Extract -segment_time from the camera's record output args."""
|
||||
record_args = get_ffmpeg_arg_list(config.ffmpeg.output_args.record)
|
||||
|
||||
if record_args and record_args[0].startswith("preset"):
|
||||
return DEFAULT_RECORD_SEGMENT_TIME
|
||||
|
||||
try:
|
||||
idx = record_args.index("-segment_time")
|
||||
return int(record_args[idx + 1])
|
||||
except (ValueError, IndexError):
|
||||
return DEFAULT_RECORD_SEGMENT_TIME
|
||||
|
||||
|
||||
def capture_frames(
|
||||
ffmpeg_process: sp.Popen[Any],
|
||||
@ -181,12 +164,6 @@ class CameraWatchdog(threading.Thread):
|
||||
self.latest_cache_segment_time: float = 0
|
||||
self.record_enable_time: datetime | None = None
|
||||
|
||||
# `valid` segments are published with the segment's start time, so the
|
||||
# gap between consecutive publishes can reach 2 * segment_time. Pad the
|
||||
# staleness threshold so it's never tighter than that worst case.
|
||||
segment_time = _get_record_segment_time(self.config)
|
||||
self.record_stale_threshold = max(120, 2 * segment_time + 30)
|
||||
|
||||
# Stall tracking (based on last processed frame)
|
||||
self._stall_timestamps: deque[float] = deque()
|
||||
self._stall_active: bool = False
|
||||
@ -436,17 +413,16 @@ class CameraWatchdog(threading.Thread):
|
||||
|
||||
# ensure segments are still being created and that they have valid video data
|
||||
# Skip checks during grace period to allow segments to start being created
|
||||
stale_window = timedelta(seconds=self.record_stale_threshold)
|
||||
cache_stale = not in_grace_period and now_utc > (
|
||||
latest_cache_dt + stale_window
|
||||
latest_cache_dt + timedelta(seconds=120)
|
||||
)
|
||||
valid_stale = not in_grace_period and now_utc > (
|
||||
latest_valid_dt + stale_window
|
||||
latest_valid_dt + timedelta(seconds=120)
|
||||
)
|
||||
invalid_stale_condition = (
|
||||
self.latest_invalid_segment_time > 0
|
||||
and not in_grace_period
|
||||
and now_utc > (latest_invalid_dt + stale_window)
|
||||
and now_utc > (latest_invalid_dt + timedelta(seconds=120))
|
||||
and self.latest_valid_segment_time
|
||||
<= self.latest_invalid_segment_time
|
||||
)
|
||||
@ -463,7 +439,7 @@ class CameraWatchdog(threading.Thread):
|
||||
)
|
||||
|
||||
self.logger.error(
|
||||
f"{reason} for {self.config.name} in the last {self.record_stale_threshold}s. Restarting the ffmpeg record process..."
|
||||
f"{reason} for {self.config.name} in the last 120s. Restarting the ffmpeg record process..."
|
||||
)
|
||||
p["process"] = start_or_restart_ffmpeg(
|
||||
p["cmd"],
|
||||
|
||||
@ -28,7 +28,6 @@ class MonitoredProcess:
|
||||
restart_timestamps: deque[float] = field(
|
||||
default_factory=lambda: deque(maxlen=MAX_RESTARTS)
|
||||
)
|
||||
clean_exit_logged: bool = False
|
||||
|
||||
def is_restarting_too_fast(self, now: float) -> bool:
|
||||
while (
|
||||
@ -73,9 +72,7 @@ class FrigateWatchdog(threading.Thread):
|
||||
|
||||
exitcode = entry.process.exitcode
|
||||
if exitcode == 0:
|
||||
if not entry.clean_exit_logged:
|
||||
logger.info("Process %s exited cleanly, not restarting", entry.name)
|
||||
entry.clean_exit_logged = True
|
||||
logger.info("Process %s exited cleanly, not restarting", entry.name)
|
||||
return
|
||||
|
||||
logger.warning(
|
||||
|
||||
@ -71,17 +71,7 @@
|
||||
},
|
||||
"queueing": "エクスポートをキューイングしています...",
|
||||
"multiCamera": {
|
||||
"queueingButton": "エクスポートをキューイングしています...",
|
||||
"timeRange": "期間",
|
||||
"selectFromTimeline": "タイムラインから選択",
|
||||
"cameraSelection": "カメラ",
|
||||
"cameraSelectionHelp": "この期間に追跡対象が含まれるカメラは、あらかじめ選択されています",
|
||||
"checkingActivity": "カメラの動作を確認中...",
|
||||
"noCameras": "利用可能なカメラがありません",
|
||||
"detectionCount_other": "{{count}} 追跡対象",
|
||||
"nameLabel": "エクスポート名",
|
||||
"namePlaceholder": "これらのエクスポート用オプションのベース名",
|
||||
"exportButton_other": "{{count}} 台のカメラをエクスポート"
|
||||
"queueingButton": "エクスポートをキューイングしています..."
|
||||
},
|
||||
"case": {
|
||||
"newCaseOption": "新しいケースを作成する",
|
||||
@ -90,10 +80,6 @@
|
||||
"label": "ケース",
|
||||
"nonAdminHelp": "これらのエクスポートに対して新しいケースが作成されます。",
|
||||
"placeholder": "ケースを選択"
|
||||
},
|
||||
"tabs": {
|
||||
"export": "シングルカメラ",
|
||||
"multiCamera": "マルチカメラ"
|
||||
}
|
||||
},
|
||||
"streaming": {
|
||||
|
||||
@ -114,7 +114,7 @@
|
||||
},
|
||||
"trackedObjectDelete": {
|
||||
"title": "削除の確認",
|
||||
"desc": "これら {{objectLength}} 件の追跡オブジェクトを削除すると、スナップショット、保存された埋め込み、関連するオブジェクトのライフサイクル項目が削除されます。履歴ビューの録画映像は削除され<em>ません</em>。<br /><br />続行してもよろしいですか?<br /><br />今後このダイアログを表示しない場合は <em>Shift</em> キーを押しながら操作してください。",
|
||||
"desc": "これら {{objectLength}} 件の追跡オブジェクトを削除すると、スナップショット、保存された埋め込み、関連するオブジェクトのライフサイクル項目が削除されます。履歴ビューの録画映像は<em>削除されません。</em><br /><br />続行してもよろしいですか?<br /><br />今後このダイアログを表示しない場合は <em>Shift</em> キーを押しながら操作してください。",
|
||||
"toast": {
|
||||
"success": "追跡オブジェクトを削除しました。",
|
||||
"error": "追跡オブジェクトの削除に失敗しました: {{errorMessage}}"
|
||||
|
||||
@ -46,51 +46,16 @@
|
||||
},
|
||||
"audio_transcription": {
|
||||
"label": "音声文字起こし",
|
||||
"description": "イベントやリアルタイム字幕に使用される、ライブ音声およびスピーチ音声の文字起こし設定。",
|
||||
"description": "イベントやライブ字幕に使用される、ライブ音声およびスピーチ音声の文字起こし設定。",
|
||||
"enabled": {
|
||||
"label": "音声文字起こしを有効にする",
|
||||
"label": "文字起こしを有効にする",
|
||||
"description": "手動でトリガーされる音声イベントの文字起こしを有効または無効にします。"
|
||||
},
|
||||
"enabled_in_config": {
|
||||
"label": "元の文字起こし状態"
|
||||
},
|
||||
"live_enabled": {
|
||||
"label": "ライブ文字起こし",
|
||||
"description": "音声を受信した時点で、リアルタイム文字起こしを有効にします。"
|
||||
"label": "ライブ文字起こし"
|
||||
}
|
||||
},
|
||||
"birdseye": {
|
||||
"label": "バードアイ",
|
||||
"description": "複数のカメラ映像を1つのレイアウトに合成する「バードアイ」合成ビューの設定。",
|
||||
"enabled": {
|
||||
"label": "バードアイを有効にする",
|
||||
"description": "バードアイビュー機能を有効または無効にします。"
|
||||
},
|
||||
"mode": {
|
||||
"label": "トラッキングモード",
|
||||
"description": "バードアイにカメラを含めるモード:「オブジェクト」「モーション」または「連続」。"
|
||||
},
|
||||
"order": {
|
||||
"label": "位置",
|
||||
"description": "バードアイレイアウトにおけるカメラの並び順を決定する数値。"
|
||||
}
|
||||
},
|
||||
"detect": {
|
||||
"label": "物体検出",
|
||||
"description": "物体検出の実行やトラッカーの初期化に使用される、検出や検出ロールの設定。",
|
||||
"enabled": {
|
||||
"label": "物体検知を有効にする",
|
||||
"description": "このカメラの物体検知機能を有効または無効にします。"
|
||||
},
|
||||
"height": {
|
||||
"label": "高さを検出",
|
||||
"description": "検出ストリームに使用するフレーム高さ(ピクセル)。ネイティブストリーム解像度を使用する場合は、空欄のままにしてください。"
|
||||
},
|
||||
"width": {
|
||||
"label": "幅を検出"
|
||||
}
|
||||
},
|
||||
"mqtt": {
|
||||
"label": "MQTT"
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,22 +79,6 @@
|
||||
"failed_login_rate_limit": {
|
||||
"label": "ログイン失敗回数の上限",
|
||||
"description": "ログイン失敗時の試行回数を制限するルールを設けることで、総当たり攻撃を軽減する。"
|
||||
},
|
||||
"trusted_proxies": {
|
||||
"label": "信頼できるプロキシ",
|
||||
"description": "レート制限のためクライアントIPアドレスを特定する際に使用される、信頼できるプロキシIPのリスト。"
|
||||
},
|
||||
"hash_iterations": {
|
||||
"label": "ハッシュ反復処理",
|
||||
"description": "ユーザーパスワードのハッシュ化に使用するPBKDF2-SHA256の反復回数。"
|
||||
},
|
||||
"roles": {
|
||||
"label": "ロールのマッピング",
|
||||
"description": "ロールをカメラリストに割り当てます。リストが空の場合、そのロールのユーザーは全てのカメラにアクセスできます。"
|
||||
},
|
||||
"admin_first_time_login": {
|
||||
"label": "初回管理者フラグ",
|
||||
"description": "この設定が「true」の場合、ログインページにヘルプリンクが表示され、管理者パスワードのリセット後にログインする方法がユーザーに案内されることがあります。 "
|
||||
}
|
||||
},
|
||||
"version": {
|
||||
@ -103,65 +87,9 @@
|
||||
},
|
||||
"audio_transcription": {
|
||||
"label": "音声文字起こし",
|
||||
"description": "イベントやリアルタイム字幕に使用される、ライブ音声およびスピーチ音声の文字起こし設定。",
|
||||
"description": "イベントやライブ字幕に使用される、ライブ音声およびスピーチ音声の文字起こし設定。",
|
||||
"live_enabled": {
|
||||
"label": "ライブ文字起こし",
|
||||
"description": "音声を受信した時点で、リアルタイム文字起こしを有効にします。"
|
||||
},
|
||||
"enabled": {
|
||||
"label": "音声文字起こしを有効にする"
|
||||
"label": "ライブ文字起こし"
|
||||
}
|
||||
},
|
||||
"birdseye": {
|
||||
"label": "バードアイ",
|
||||
"description": "複数のカメラ映像を1つのレイアウトに合成する「バードアイ」合成ビューの設定。",
|
||||
"enabled": {
|
||||
"label": "バードアイを有効にする",
|
||||
"description": "バードアイビュー機能を有効または無効にします。"
|
||||
},
|
||||
"mode": {
|
||||
"label": "トラッキングモード",
|
||||
"description": "バードアイにカメラを含めるモード:「オブジェクト」「モーション」または「連続」。"
|
||||
},
|
||||
"order": {
|
||||
"label": "位置",
|
||||
"description": "バードアイレイアウトにおけるカメラの並び順を決定する数値。"
|
||||
}
|
||||
},
|
||||
"database": {
|
||||
"label": "データベース",
|
||||
"description": "Frigateが追跡対象や録画メタデータを保存するために使用するSQLiteデータベースの設定。",
|
||||
"path": {
|
||||
"label": "データベースパス",
|
||||
"description": "FrigateのSQLiteデータベースファイルが保存されるファイルシステムパス。"
|
||||
}
|
||||
},
|
||||
"detect": {
|
||||
"label": "物体検出",
|
||||
"description": "物体検出の実行やトラッカーの初期化に使用される、検出や検出ロールの設定。",
|
||||
"enabled": {
|
||||
"label": "物体検知を有効にする"
|
||||
},
|
||||
"height": {
|
||||
"label": "高さを検出",
|
||||
"description": "検出ストリームに使用するフレーム高さ(ピクセル)。ネイティブストリーム解像度を使用する場合は、空欄のままにしてください。"
|
||||
},
|
||||
"width": {
|
||||
"label": "幅を検出"
|
||||
}
|
||||
},
|
||||
"go2rtc": {
|
||||
"label": "go2rtc",
|
||||
"description": "ライブストリーム中継および変換に利用される、統合型go2rtcリストリーミングサービスの設定。"
|
||||
},
|
||||
"mqtt": {
|
||||
"label": "MQTT",
|
||||
"description": "テレメトリー、スナップショット、およびイベントの詳細をMQTTブローカーに接続して公開するための設定。",
|
||||
"enabled": {
|
||||
"label": "MQTTを有効にする"
|
||||
}
|
||||
},
|
||||
"telemetry": {
|
||||
"label": "テレメトリー"
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,8 +30,7 @@
|
||||
"trainingFailed": "モデルの学習に失敗しました。Frigate のログを確認してください。",
|
||||
"trainingFailedToStart": "モデルの学習を開始できませんでした: {{errorMessage}}",
|
||||
"updateModelFailed": "モデルの更新に失敗しました: {{errorMessage}}",
|
||||
"renameCategoryFailed": "クラス名の変更に失敗しました: {{errorMessage}}",
|
||||
"reclassifyFailed": "画像の再分類に失敗しました:{{errorMessage}}"
|
||||
"renameCategoryFailed": "クラス名の変更に失敗しました: {{errorMessage}}"
|
||||
}
|
||||
},
|
||||
"train": {
|
||||
|
||||
@ -27,9 +27,7 @@
|
||||
},
|
||||
"documentTitle": "レビュー - Frigate",
|
||||
"recordings": {
|
||||
"documentTitle": "録画 - Frigate",
|
||||
"invalidSharedLink": "解析エラーのため、タイムスタンプ付きの録画リンクを開くことができません。",
|
||||
"invalidSharedCamera": "不明または未承認のカメラのため、タイムスタンプ付き録画のリンクを開くことができません。"
|
||||
"documentTitle": "録画 - Frigate"
|
||||
},
|
||||
"calendarFilter": {
|
||||
"last24Hours": "直近24時間"
|
||||
|
||||
@ -93,7 +93,5 @@
|
||||
"trainFailed": "学習に失敗しました: {{errorMessage}}",
|
||||
"updateFaceScoreFailed": "顔スコアの更新に失敗しました: {{errorMessage}}"
|
||||
}
|
||||
},
|
||||
"reclassifyFaceAs": "顔を再分類する:",
|
||||
"reclassifyFace": "顔の再分類"
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,24 +38,7 @@
|
||||
"uiSettings": "UI設定",
|
||||
"profiles": "プロファイル",
|
||||
"globalDetect": "物体検出",
|
||||
"globalRecording": "録画",
|
||||
"globalSnapshots": "スナップショット",
|
||||
"globalFfmpeg": "FFmpeg",
|
||||
"globalMotion": "動体検出",
|
||||
"globalObjects": "オブジェクト",
|
||||
"globalReview": "レビュー",
|
||||
"globalAudioEvents": "オーディオイベント",
|
||||
"globalLivePlayback": "ライブ再生",
|
||||
"globalTimestampStyle": "タイムスタンプ形式",
|
||||
"systemDatabase": "データベース",
|
||||
"systemTls": "TLS",
|
||||
"systemAuthentication": "認証",
|
||||
"systemNetworking": "ネットワーキング",
|
||||
"systemProxy": "プロキシ",
|
||||
"systemUi": "UI",
|
||||
"systemLogging": "ロギング",
|
||||
"systemEnvironmentVariables": "環境変数",
|
||||
"systemTelemetry": "テレメトリー"
|
||||
"globalRecording": "録画"
|
||||
},
|
||||
"dialog": {
|
||||
"unsavedChanges": {
|
||||
@ -961,7 +944,7 @@
|
||||
"quality": "品質",
|
||||
"selectQuality": "品質を選択",
|
||||
"roleLabels": {
|
||||
"detect": "物体検出",
|
||||
"detect": "オブジェクト検出",
|
||||
"record": "録画",
|
||||
"audio": "音声"
|
||||
},
|
||||
@ -976,7 +959,7 @@
|
||||
"detectRoleWarning": "続行するには、少なくとも 1 つのストリームに「検出」ロールが必要です。",
|
||||
"rolesPopover": {
|
||||
"title": "ストリーム ロール",
|
||||
"detect": "物体検出用のメイン フィードです。",
|
||||
"detect": "オブジェクト検出用のメイン フィードです。",
|
||||
"record": "設定に基づいて映像フィードのセグメントを保存します。",
|
||||
"audio": "音声ベース検出用のフィードです。"
|
||||
},
|
||||
|
||||
@ -55,12 +55,7 @@
|
||||
"cameras_count_one": "{{count}} カメラ",
|
||||
"cameras_count_other": "{{count}} カメラ"
|
||||
},
|
||||
"empty": "まだメッセージは記録されていません",
|
||||
"count_one": "{{count}} メッセージ",
|
||||
"count_other": "{{count}} メッセージ",
|
||||
"expanded": {
|
||||
"payload": "ペイロード"
|
||||
}
|
||||
"empty": "まだメッセージは記録されていません"
|
||||
}
|
||||
},
|
||||
"general": {
|
||||
|
||||
@ -1,5 +1 @@
|
||||
{
|
||||
"time": {
|
||||
"untilForTime": "{{time}}ವರೆಗೆ"
|
||||
}
|
||||
}
|
||||
{}
|
||||
|
||||
@ -1,5 +1 @@
|
||||
{
|
||||
"form": {
|
||||
"user": "ಬಳಕೆದಾರರಹೆಸರು"
|
||||
}
|
||||
}
|
||||
{}
|
||||
|
||||
@ -1,5 +1 @@
|
||||
{
|
||||
"group": {
|
||||
"label": "ಕ್ಯಾಮೆರಾ ಗುಂಪು"
|
||||
}
|
||||
}
|
||||
{}
|
||||
|
||||
@ -1,5 +1 @@
|
||||
{
|
||||
"restart": {
|
||||
"title": "ನೀವು ಫ್ರಿಗೇಟನ್ನು ಖಂಡಿತವಗೆ ರೀಸ್ಟಾರ್ಟ್ ಮಾಡಬಯಸುತ್ತೀರಾ?"
|
||||
}
|
||||
}
|
||||
{}
|
||||
|
||||
@ -1,5 +1 @@
|
||||
{
|
||||
"iconPicker": {
|
||||
"selectIcon": "ಬಿಂಬವನ್ನು ಆಯ್ಕೆಮಾಡಿ"
|
||||
}
|
||||
}
|
||||
{}
|
||||
|
||||
@ -1,7 +1 @@
|
||||
{
|
||||
"audio": {
|
||||
"global": {
|
||||
"detection": "ಜಾಗತಿಕ ಪತ್ತೆದಾರಿ"
|
||||
}
|
||||
}
|
||||
}
|
||||
{}
|
||||
|
||||
@ -1,3 +1 @@
|
||||
{
|
||||
"person": "ವ್ಯಕ್ತಿ"
|
||||
}
|
||||
{}
|
||||
|
||||
@ -1,3 +1 @@
|
||||
{
|
||||
"documentTitle": "ಕಾನ್ಫಿಗ್ ಸಂಪಾದಕ - ಫ್ರಿಗೇಟ್"
|
||||
}
|
||||
{}
|
||||
|
||||
@ -1,3 +1 @@
|
||||
{
|
||||
"export": "ರಪ್ತು ಮಾಡು"
|
||||
}
|
||||
{}
|
||||
|
||||
@ -1,5 +1 @@
|
||||
{
|
||||
"documentTitle": {
|
||||
"default": "ಸೆಟ್ಟಿಂಗ್ಗಳು - ಫ್ರಿಗೇಟ್"
|
||||
}
|
||||
}
|
||||
{}
|
||||
|
||||
@ -1,5 +1 @@
|
||||
{
|
||||
"documentTitle": {
|
||||
"cameras": "ಕ್ಯಾಮೆರಾ ಅಂಕಿಅಂಶಗಳು - ಫ್ರಿಗೇಟ್"
|
||||
}
|
||||
}
|
||||
{}
|
||||
|
||||
@ -33,8 +33,7 @@
|
||||
"noPreviewFoundFor": "Nie znaleziono podglądu dla {{cameraName}}",
|
||||
"submitFrigatePlus": {
|
||||
"title": "Wyślij tę klatkę do Frigate+?",
|
||||
"submit": "Wyślij",
|
||||
"previewError": "Nie udało się załadować podglądu nagrania. Nagranie może być niedostępne w podanym czasie."
|
||||
"submit": "Wyślij"
|
||||
},
|
||||
"livePlayerRequiredIOSVersion": "Wymagana wersja iOS 17.1 lub nowsza dla tego typu transmisji na żywo.",
|
||||
"streamOffline": {
|
||||
|
||||
@ -42,31 +42,6 @@
|
||||
"description": "Liczbowa lub znakowa wersja aktywnej konfiguracji w celu wykrywania migracji lub zmiany formatu."
|
||||
},
|
||||
"safe_mode": {
|
||||
"label": "Tryb bezpieczny",
|
||||
"description": "Po włączeniu Frigate uruchomi się w trybie awaryjnym z ograniczonymi funkcjami, co ułatwi rozwiązywanie problemów."
|
||||
},
|
||||
"environment_vars": {
|
||||
"label": "Zmienne środowiskowe",
|
||||
"description": "Pary klucz-wartość zmiennych środowiskowych, które należy ustawić dla procesu Frigate w systemie Home Assistant. Użytkownicy niekorzystający z Home Assistant powinni zamiast tego skorzystać z konfiguracji zmiennych środowiskowych w Dockerze."
|
||||
},
|
||||
"logger": {
|
||||
"label": "Rejestrowanie",
|
||||
"description": "Konfiguracja domyślnej szczegółowości logów oraz indywidualnych ustawień dla komponentów.",
|
||||
"default": {
|
||||
"label": "Poziom szczegółowości logów",
|
||||
"description": "Domyślny poziom szczegółowości logów globalnych (debug, info, warning, error)."
|
||||
},
|
||||
"logs": {
|
||||
"label": "Poziom logowania dla poszczególnych procesów",
|
||||
"description": "Zmiana poziomu logowania dla poszczególnych komponentów w celu zwiększenia lub zmniejszenia szczegółowości logów dla określonych modułów."
|
||||
}
|
||||
},
|
||||
"auth": {
|
||||
"label": "Uwierzytelnianie",
|
||||
"description": "Ustawienia związane z uwierzytelnianiem i sesjami, w tym opcje dotyczące plików cookie i ograniczeń częstotliwości.",
|
||||
"enabled": {
|
||||
"label": "Włącz uwierzytelnianie",
|
||||
"description": "Włącz natywne uwierzytelnianie dla interfejsu Frigate."
|
||||
}
|
||||
"label": "Tryb bezpieczny"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,18 +1,5 @@
|
||||
{
|
||||
"minimum": "Musi wynosić przynajmniej {{limit}}",
|
||||
"maximum": "Może wynosić najwyżej {{limit}}",
|
||||
"exclusiveMinimum": "Musi być większe niż {{limit}}",
|
||||
"exclusiveMaximum": "Musi być mniejsze niż {{limit}}",
|
||||
"minLength": "Musi być co najmniej {{limit}} znaków",
|
||||
"maxLength": "Maksymalnie może być {{limit}} znaków",
|
||||
"minItems": "Musi zawierać co najmniej {{limit}} pozycji",
|
||||
"maxItems": "Maksymalnie {{limit}} pozycji",
|
||||
"pattern": "Nieprawidłowy format",
|
||||
"required": "To pole jest obowiązkowe",
|
||||
"type": "Nieprawidłowy typ wartości",
|
||||
"enum": "Musi to być jedna z dozwolonych wartości",
|
||||
"const": "Wartość niezgodna z oczekiwaną stałą",
|
||||
"uniqueItems": "Wszystkie pozycje muszą być unikalne",
|
||||
"format": "Nieprawidłowy format",
|
||||
"additionalProperties": "Nieznana właściwość jest niedozwolona"
|
||||
"exclusiveMinimum": "Musi być większe niż {{limit}}"
|
||||
}
|
||||
|
||||
@ -16,9 +16,7 @@
|
||||
"description": "Elementy przeglądu można tworzyć dla kamery tylko wtedy, gdy dla tej kamery włączono nagrywanie."
|
||||
}
|
||||
},
|
||||
"timeline": {
|
||||
"label": "Oś czasu"
|
||||
},
|
||||
"timeline": "Oś czasu",
|
||||
"timeline.aria": "Wybierz oś czasu",
|
||||
"events": {
|
||||
"label": "Zdarzenia",
|
||||
|
||||
@ -2,9 +2,7 @@
|
||||
"search": "Szukaj",
|
||||
"documentTitle": "Eksportuj - Frigate",
|
||||
"noExports": "Nie znaleziono eksportów",
|
||||
"deleteExport": {
|
||||
"label": "Usuń eksport"
|
||||
},
|
||||
"deleteExport": "Usuń eksport",
|
||||
"deleteExport.desc": "Czy na pewno chcesz usunąć {{exportName}}?",
|
||||
"editExport": {
|
||||
"title": "Zmień nazwę eksportu",
|
||||
@ -20,11 +18,9 @@
|
||||
"shareExport": "Udostępnij eksport",
|
||||
"downloadVideo": "Pobierz wideo",
|
||||
"editName": "Edytuj nazwę",
|
||||
"deleteExport": "Usuń eksport",
|
||||
"assignToCase": "Dodaj do sprawy"
|
||||
"deleteExport": "Usuń eksport"
|
||||
},
|
||||
"headings": {
|
||||
"cases": "Przypadki",
|
||||
"uncategorizedExports": "Bez kategorii Eksport"
|
||||
"cases": "Przypadki"
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,8 +17,7 @@
|
||||
"clickMove": {
|
||||
"label": "Kliknij w ramce, aby wyśrodkować kamerę",
|
||||
"enable": "Włącz kliknięcie do przesuwania",
|
||||
"disable": "Wyłącz kliknięcie do przesuwania",
|
||||
"enableWithZoom": "Włącz kliknij, aby przesunąć / przeciągnij, aby przybliżyć"
|
||||
"disable": "Wyłącz kliknięcie do przesuwania"
|
||||
},
|
||||
"left": {
|
||||
"label": "Przesuń kamerę PTZ w lewo"
|
||||
|
||||
@ -96,11 +96,7 @@
|
||||
"notifications": "Ustawienia powiadomień - Frigate",
|
||||
"enrichments": "Ustawienia wzbogacania - Frigate",
|
||||
"cameraManagement": "Zarządzanie kamerami – Frigate",
|
||||
"cameraReview": "Ustawienia przeglądu kamer - Frigate",
|
||||
"globalConfig": "Konfiguracja globalna - Frigate",
|
||||
"cameraConfig": "Konfiguracja kamery - Frigate",
|
||||
"maintenance": "Konserwacja – Frigate",
|
||||
"profiles": "Profile - Frigate"
|
||||
"cameraReview": "Ustawienia przeglądu kamer - Frigate"
|
||||
},
|
||||
"classification": {
|
||||
"title": "Ustawienia Klasyfikacji",
|
||||
|
||||
@ -7,8 +7,7 @@
|
||||
"logs": {
|
||||
"frigate": "Logi Frigate - Frigate",
|
||||
"go2rtc": "Logi Go2RTC - Frigate",
|
||||
"nginx": "Logi Nginx - Frigate",
|
||||
"websocket": "Logi Websocket - Frigate"
|
||||
"nginx": "Logi Nginx - Frigate"
|
||||
}
|
||||
},
|
||||
"general": {
|
||||
@ -164,16 +163,6 @@
|
||||
"fetchingLogsFailed": "Błąd pobierania logów: {{errorMessage}}",
|
||||
"whileStreamingLogs": "Błąd podczas strumieniowania logów: {{errorMessage}}"
|
||||
}
|
||||
},
|
||||
"websocket": {
|
||||
"label": "Wiadomości",
|
||||
"pause": "Pauza",
|
||||
"resume": "Wznów",
|
||||
"clear": "Wyczyść",
|
||||
"filter": {
|
||||
"all": "Wszystkie tematy",
|
||||
"topics": "Tematy"
|
||||
}
|
||||
}
|
||||
},
|
||||
"title": "System",
|
||||
|
||||
@ -391,8 +391,10 @@ export default function MobileReviewSettingsDrawer({
|
||||
className="flex w-full items-center justify-center gap-2"
|
||||
aria-label={t("title", { ns: "views/replay" })}
|
||||
onClick={() => {
|
||||
const now = new Date(latestTime * 1000);
|
||||
now.setHours(now.getHours() - 1);
|
||||
setDebugReplayRange({
|
||||
after: latestTime - 60,
|
||||
after: now.getTime() / 1000,
|
||||
before: latestTime,
|
||||
});
|
||||
setSelectedReplayOption("1");
|
||||
@ -539,9 +541,11 @@ export default function MobileReviewSettingsDrawer({
|
||||
return;
|
||||
}
|
||||
|
||||
const minutes = parseInt(option, 10);
|
||||
const hours = parseInt(option);
|
||||
const end = latestTime;
|
||||
setDebugReplayRange({ after: end - minutes * 60, before: end });
|
||||
const now = new Date(end * 1000);
|
||||
now.setHours(now.getHours() - hours);
|
||||
setDebugReplayRange({ after: now.getTime() / 1000, before: end });
|
||||
};
|
||||
|
||||
content = (
|
||||
|
||||
@ -396,6 +396,7 @@ export default function HlsVideoPlayer({
|
||||
}}
|
||||
>
|
||||
<ObjectTrackOverlay
|
||||
key={`overlay-${currentTime}`}
|
||||
camera={camera}
|
||||
showBoundingBoxes={!isPlaying}
|
||||
currentTime={currentTime}
|
||||
|
||||
@ -728,8 +728,10 @@ export function RecordingView({
|
||||
setShareTimestampOpen(true);
|
||||
}}
|
||||
onDebugReplayClick={() => {
|
||||
const now = new Date(timeRange.before * 1000);
|
||||
now.setHours(now.getHours() - 1);
|
||||
setDebugReplayRange({
|
||||
after: timeRange.before - 60,
|
||||
after: now.getTime() / 1000,
|
||||
before: timeRange.before,
|
||||
});
|
||||
setDebugReplayMode("select");
|
||||
|
||||
@ -10,16 +10,13 @@ import axios from "axios";
|
||||
import { toast } from "sonner";
|
||||
import { useJobStatus } from "@/api/ws";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { LuCheck, LuExternalLink, LuX } from "react-icons/lu";
|
||||
import { LuCheck, LuX } from "react-icons/lu";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { formatUnixTimestampToDateTime } from "@/utils/dateUtil";
|
||||
import { MediaSyncResults, MediaSyncStats } from "@/types/ws";
|
||||
import { useDocDomain } from "@/hooks/use-doc-domain";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
export default function MediaSyncSettingsView() {
|
||||
const { t } = useTranslation("views/settings");
|
||||
const { getLocaleDocUrl } = useDocDomain();
|
||||
const [selectedMediaTypes, setSelectedMediaTypes] = useState<string[]>([
|
||||
"all",
|
||||
]);
|
||||
@ -112,25 +109,13 @@ export default function MediaSyncSettingsView() {
|
||||
<Heading as="h4" className="mb-2 hidden md:block">
|
||||
{t("maintenance.sync.title")}
|
||||
</Heading>
|
||||
|
||||
<div className="max-w-6xl">
|
||||
<div className="mb-5 mt-2 flex max-w-5xl flex-col gap-2 text-sm text-muted-foreground">
|
||||
<p>{t("maintenance.sync.desc")}</p>
|
||||
|
||||
<div className="flex items-center text-primary-variant">
|
||||
<Link
|
||||
to={getLocaleDocUrl(
|
||||
"configuration/record#syncing-media-files-with-disk",
|
||||
)}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline"
|
||||
>
|
||||
{t("readTheDocumentation", { ns: "common" })}
|
||||
<LuExternalLink className="ml-2 inline-flex size-3" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
{/* Media Types Selection */}
|
||||
<div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user