Compare commits

..

1 Commits

Author SHA1 Message Date
dependabot[bot]
f2460d871a Bump diff from 5.2.0 to 5.2.2 in /docs
Bumps [diff](https://github.com/kpdecker/jsdiff) from 5.2.0 to 5.2.2.
- [Changelog](https://github.com/kpdecker/jsdiff/blob/master/release-notes.md)
- [Commits](https://github.com/kpdecker/jsdiff/compare/v5.2.0...v5.2.2)

---
updated-dependencies:
- dependency-name: diff
  dependency-version: 5.2.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-20 18:50:23 +00:00
375 changed files with 1345 additions and 8674 deletions

View File

@@ -1,385 +1,3 @@
# GitHub Copilot Instructions for Frigate NVR
This document provides coding guidelines and best practices for contributing to Frigate NVR, a complete and local NVR designed for Home Assistant with AI object detection.
## Project Overview
Frigate NVR is a realtime object detection system for IP cameras that uses:
- **Backend**: Python 3.13+ with FastAPI, OpenCV, TensorFlow/ONNX
- **Frontend**: React with TypeScript, Vite, TailwindCSS
- **Architecture**: Multiprocessing design with ZMQ and MQTT communication
- **Focus**: Minimal resource usage with maximum performance
## Code Review Guidelines
When reviewing code, do NOT comment on:
- Missing imports - Static analysis tooling catches these
- Code formatting - Ruff (Python) and Prettier (TypeScript/React) handle formatting
- Minor style inconsistencies already enforced by linters
## Python Backend Standards
### Python Requirements
- **Compatibility**: Python 3.13+
- **Language Features**: Use modern Python features:
- Pattern matching
- Type hints (comprehensive typing preferred)
- f-strings (preferred over `%` or `.format()`)
- Dataclasses
- Async/await patterns
### Code Quality Standards
- **Formatting**: Ruff (configured in `pyproject.toml`)
- **Linting**: Ruff with rules defined in project config
- **Type Checking**: Use type hints consistently
- **Testing**: unittest framework - use `python3 -u -m unittest` to run tests
- **Language**: American English for all code, comments, and documentation
### Logging Standards
- **Logger Pattern**: Use module-level logger
```python
import logging
logger = logging.getLogger(__name__)
```
- **Format Guidelines**:
- No periods at end of log messages
- No sensitive data (keys, tokens, passwords)
- Use lazy logging: `logger.debug("Message with %s", variable)`
- **Log Levels**:
- `debug`: Development and troubleshooting information
- `info`: Important runtime events (startup, shutdown, state changes)
- `warning`: Recoverable issues that should be addressed
- `error`: Errors that affect functionality but don't crash the app
- `exception`: Use in except blocks to include traceback
### Error Handling
- **Exception Types**: Choose most specific exception available
- **Try/Catch Best Practices**:
- Only wrap code that can throw exceptions
- Keep try blocks minimal - process data after the try/except
- Avoid bare exceptions except in background tasks
Bad pattern:
```python
try:
data = await device.get_data() # Can throw
# ❌ Don't process data inside try block
processed = data.get("value", 0) * 100
result = processed
except DeviceError:
logger.error("Failed to get data")
```
Good pattern:
```python
try:
data = await device.get_data() # Can throw
except DeviceError:
logger.error("Failed to get data")
return
# ✅ Process data outside try block
processed = data.get("value", 0) * 100
result = processed
```
### Async Programming
- **External I/O**: All external I/O operations must be async
- **Best Practices**:
- Avoid sleeping in loops - use `asyncio.sleep()` not `time.sleep()`
- Avoid awaiting in loops - use `asyncio.gather()` instead
- No blocking calls in async functions
- Use `asyncio.create_task()` for background operations
- **Thread Safety**: Use proper synchronization for shared state
### Documentation Standards
- **Module Docstrings**: Concise descriptions at top of files
```python
"""Utilities for motion detection and analysis."""
```
- **Function Docstrings**: Required for public functions and methods
```python
async def process_frame(frame: ndarray, config: Config) -> Detection:
"""Process a video frame for object detection.
Args:
frame: The video frame as numpy array
config: Detection configuration
Returns:
Detection results with bounding boxes
"""
```
- **Comment Style**:
- Explain the "why" not just the "what"
- Keep lines under 88 characters when possible
- Use clear, descriptive comments
### File Organization
- **API Endpoints**: `frigate/api/` - FastAPI route handlers
- **Configuration**: `frigate/config/` - Configuration parsing and validation
- **Detectors**: `frigate/detectors/` - Object detection backends
- **Events**: `frigate/events/` - Event management and storage
- **Utilities**: `frigate/util/` - Shared utility functions
## Frontend (React/TypeScript) Standards
### Internationalization (i18n)
- **CRITICAL**: Never write user-facing strings directly in components
- **Always use react-i18next**: Import and use the `t()` function
```tsx
import { useTranslation } from "react-i18next";
function MyComponent() {
const { t } = useTranslation(["views/live"]);
return <div>{t("camera_not_found")}</div>;
}
```
- **Translation Files**: Add English strings to the appropriate json files in `web/public/locales/en`
- **Namespaces**: Organize translations by feature/view (e.g., `views/live`, `common`, `views/system`)
### Code Quality
- **Linting**: ESLint (see `web/.eslintrc.cjs`)
- **Formatting**: Prettier with Tailwind CSS plugin
- **Type Safety**: TypeScript strict mode enabled
- **Testing**: Vitest for unit tests
### Component Patterns
- **UI Components**: Use Radix UI primitives (in `web/src/components/ui/`)
- **Styling**: TailwindCSS with `cn()` utility for class merging
- **State Management**: React hooks (useState, useEffect, useCallback, useMemo)
- **Data Fetching**: Custom hooks with proper loading and error states
### ESLint Rules
Key rules enforced:
- `react-hooks/rules-of-hooks`: error
- `react-hooks/exhaustive-deps`: error
- `no-console`: error (use proper logging or remove)
- `@typescript-eslint/no-explicit-any`: warn (always use proper types instead of `any`)
- Unused variables must be prefixed with `_`
- Comma dangles required for multiline objects/arrays
### File Organization
- **Pages**: `web/src/pages/` - Route components
- **Views**: `web/src/views/` - Complex view components
- **Components**: `web/src/components/` - Reusable components
- **Hooks**: `web/src/hooks/` - Custom React hooks
- **API**: `web/src/api/` - API client functions
- **Types**: `web/src/types/` - TypeScript type definitions
## Testing Requirements
### Backend Testing
- **Framework**: Python unittest
- **Run Command**: `python3 -u -m unittest`
- **Location**: `frigate/test/`
- **Coverage**: Aim for comprehensive test coverage of core functionality
- **Pattern**: Use `TestCase` classes with descriptive test method names
```python
class TestMotionDetection(unittest.TestCase):
def test_detects_motion_above_threshold(self):
# Test implementation
```
### Test Best Practices
- Always have a way to test your work and confirm your changes
- Write tests for bug fixes to prevent regressions
- Test edge cases and error conditions
- Mock external dependencies (cameras, APIs, hardware)
- Use fixtures for test data
## Development Commands
### Python Backend
```bash
# Run all tests
python3 -u -m unittest
# Run specific test file
python3 -u -m unittest frigate.test.test_ffmpeg_presets
# Check formatting (Ruff)
ruff format --check frigate/
# Apply formatting
ruff format frigate/
# Run linter
ruff check frigate/
```
### Frontend (from web/ directory)
```bash
# Start dev server (AI agents should never run this directly unless asked)
npm run dev
# Build for production
npm run build
# Run linter
npm run lint
# Fix linting issues
npm run lint:fix
# Format code
npm run prettier:write
```
### Docker Development
AI agents should never run these commands directly unless instructed.
```bash
# Build local image
make local
# Build debug image
make debug
```
## Common Patterns
### API Endpoint Pattern
```python
from fastapi import APIRouter, Request
from frigate.api.defs.tags import Tags
router = APIRouter(tags=[Tags.Events])
@router.get("/events")
async def get_events(request: Request, limit: int = 100):
"""Retrieve events from the database."""
# Implementation
```
### Configuration Access
```python
# Access Frigate configuration
config: FrigateConfig = request.app.frigate_config
camera_config = config.cameras["front_door"]
```
### Database Queries
```python
from frigate.models import Event
# Use Peewee ORM for database access
events = (
Event.select()
.where(Event.camera == camera_name)
.order_by(Event.start_time.desc())
.limit(limit)
)
```
## Common Anti-Patterns to Avoid
### ❌ Avoid These
```python
# Blocking operations in async functions
data = requests.get(url) # ❌ Use async HTTP client
time.sleep(5) # ❌ Use asyncio.sleep()
# Hardcoded strings in React components
<div>Camera not found</div> # ❌ Use t("camera_not_found")
# Missing error handling
data = await api.get_data() # ❌ No exception handling
# Bare exceptions in regular code
try:
value = await sensor.read()
except Exception: # ❌ Too broad
logger.error("Failed")
```
### ✅ Use These Instead
```python
# Async operations
import aiohttp
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
data = await response.json()
await asyncio.sleep(5) # ✅ Non-blocking
# Translatable strings in React
const { t } = useTranslation();
<div>{t("camera_not_found")}</div> # ✅ Translatable
# Proper error handling
try:
data = await api.get_data()
except ApiException as err:
logger.error("API error: %s", err)
raise
# Specific exceptions
try:
value = await sensor.read()
except SensorException as err: # ✅ Specific
logger.exception("Failed to read sensor")
```
## Project-Specific Conventions
### Configuration Files
- Main config: `config/config.yml`
### Directory Structure
- Backend code: `frigate/`
- Frontend code: `web/`
- Docker files: `docker/`
- Documentation: `docs/`
- Database migrations: `migrations/`
### Code Style Conformance
Always conform new and refactored code to the existing coding style in the project:
- Follow established patterns in similar files
- Match indentation and formatting of surrounding code
- Use consistent naming conventions (snake_case for Python, camelCase for TypeScript)
- Maintain the same level of verbosity in comments and docstrings
## Additional Resources
- Documentation: https://docs.frigate.video
- Main Repository: https://github.com/blakeblackshear/frigate
- Home Assistant Integration: https://github.com/blakeblackshear/frigate-hass-integration
- For Frigate NVR, never write strings in the frontend directly. Since the project uses `react-i18next`, use `t()` and write the English string in the relevant translations file in `web/public/locales/en`.
- Always conform new and refactored code to the existing coding style in the project.
- Always have a way to test your work and confirm your changes. When running backend tests, use `python3 -u -m unittest`.

View File

@@ -2,19 +2,15 @@
# Update package list and install dependencies
sudo apt-get update
sudo apt-get install -y build-essential cmake git wget linux-headers-$(uname -r)
sudo apt-get install -y build-essential cmake git wget
hailo_version="4.21.0"
arch=$(uname -m)
if [[ $arch == "aarch64" ]]; then
source /etc/os-release
os_codename=$VERSION_CODENAME
echo "Detected OS codename: $os_codename"
fi
if [ "$os_codename" = "trixie" ]; then
sudo apt install -y dkms
if [[ $arch == "x86_64" ]]; then
sudo apt install -y linux-headers-$(uname -r);
else
sudo apt install -y linux-modules-extra-$(uname -r);
fi
# Clone the HailoRT driver repository
@@ -51,4 +47,3 @@ sudo udevadm control --reload-rules && sudo udevadm trigger
echo "HailoRT driver installation complete."
echo "reboot your system to load the firmware!"
echo "Driver version: $(modinfo -F version hailo_pci)"

View File

@@ -29,10 +29,6 @@ auth:
reset_admin_password: true
```
## Password guidance
Constructing secure passwords and managing them properly is important. Frigate requires a minimum length of 12 characters. For guidance on password standards see [NIST SP 800-63B](https://pages.nist.gov/800-63-3/sp800-63b.html). To learn what makes a password truly secure, read this [article](https://medium.com/peerio/how-to-build-a-billion-dollar-password-3d92568d9277).
## Login failure rate limiting
In order to limit the risk of brute force attacks, rate limiting is available for login failures. This is implemented with SlowApi, and the string notation for valid values is available in [the documentation](https://limits.readthedocs.io/en/stable/quickstart.html#examples).
@@ -166,10 +162,6 @@ In this example:
- If no mapping matches, Frigate falls back to `default_role` if configured.
- If `role_map` is not defined, Frigate assumes the role header directly contains `admin`, `viewer`, or a custom role name.
**Note on matching semantics:**
- Admin precedence: if the `admin` mapping matches, Frigate resolves the session to `admin` to avoid accidental downgrade when a user belongs to multiple groups (for example both `admin` and `viewer` groups).
#### Port Considerations
**Authenticated Port (8971)**

View File

@@ -0,0 +1,247 @@
---
id: genai
title: Generative AI
---
Generative AI can be used to automatically generate descriptive text based on the thumbnails of your tracked objects. This helps with [Semantic Search](/configuration/semantic_search) in Frigate to provide more context about your tracked objects. Descriptions are accessed via the _Explore_ view in the Frigate UI by clicking on a tracked object's thumbnail.
Requests for a description are sent off automatically to your AI provider at the end of the tracked object's lifecycle, or can optionally be sent earlier after a number of significantly changed frames, for example in use in more real-time notifications. Descriptions can also be regenerated manually via the Frigate UI. Note that if you are manually entering a description for tracked objects prior to its end, this will be overwritten by the generated response.
## Configuration
Generative AI can be enabled for all cameras or only for specific cameras. If GenAI is disabled for a camera, you can still manually generate descriptions for events using the HTTP API. There are currently 3 native providers available to integrate with Frigate. Other providers that support the OpenAI standard API can also be used. See the OpenAI section below.
To use Generative AI, you must define a single provider at the global level of your Frigate configuration. If the provider you choose requires an API key, you may either directly paste it in your configuration, or store it in an environment variable prefixed with `FRIGATE_`.
```yaml
genai:
provider: gemini
api_key: "{FRIGATE_GEMINI_API_KEY}"
model: gemini-2.0-flash
cameras:
front_camera:
genai:
enabled: True # <- enable GenAI for your front camera
use_snapshot: True
objects:
- person
required_zones:
- steps
indoor_camera:
objects:
genai:
enabled: False # <- disable GenAI for your indoor camera
```
By default, descriptions will be generated for all tracked objects and all zones. But you can also optionally specify `objects` and `required_zones` to only generate descriptions for certain tracked objects or zones.
Optionally, you can generate the description using a snapshot (if enabled) by setting `use_snapshot` to `True`. By default, this is set to `False`, which sends the uncompressed images from the `detect` stream collected over the object's lifetime to the model. Once the object lifecycle ends, only a single compressed and cropped thumbnail is saved with the tracked object. Using a snapshot might be useful when you want to _regenerate_ a tracked object's description as it will provide the AI with a higher-quality image (typically downscaled by the AI itself) than the cropped/compressed thumbnail. Using a snapshot otherwise has a trade-off in that only a single image is sent to your provider, which will limit the model's ability to determine object movement or direction.
Generative AI can also be toggled dynamically for a camera via MQTT with the topic `frigate/<camera_name>/object_descriptions/set`. See the [MQTT documentation](/integrations/mqtt/#frigatecamera_nameobjectdescriptionsset).
## Ollama
:::warning
Using Ollama on CPU is not recommended, high inference times make using Generative AI impractical.
:::
[Ollama](https://ollama.com/) allows you to self-host large language models and keep everything running locally. It is highly recommended to host this server on a machine with an Nvidia graphics card, or on a Apple silicon Mac for best performance.
Most of the 7b parameter 4-bit vision models will fit inside 8GB of VRAM. There is also a [Docker container](https://hub.docker.com/r/ollama/ollama) available.
Parallel requests also come with some caveats. You will need to set `OLLAMA_NUM_PARALLEL=1` and choose a `OLLAMA_MAX_QUEUE` and `OLLAMA_MAX_LOADED_MODELS` values that are appropriate for your hardware and preferences. See the [Ollama documentation](https://docs.ollama.com/faq#how-does-ollama-handle-concurrent-requests).
### Model Types: Instruct vs Thinking
Most vision-language models are available as **instruct** models, which are fine-tuned to follow instructions and respond concisely to prompts. However, some models (such as certain Qwen-VL or minigpt variants) offer both **instruct** and **thinking** versions.
- **Instruct models** are always recommended for use with Frigate. These models generate direct, relevant, actionable descriptions that best fit Frigate's object and event summary use case.
- **Thinking models** are fine-tuned for more free-form, open-ended, and speculative outputs, which are typically not concise and may not provide the practical summaries Frigate expects. For this reason, Frigate does **not** recommend or support using thinking models.
Some models are labeled as **hybrid** (capable of both thinking and instruct tasks). In these cases, Frigate will always use instruct-style prompts and specifically disables thinking-mode behaviors to ensure concise, useful responses.
**Recommendation:**
Always select the `-instruct` or documented instruct/tagged variant of any model you use in your Frigate configuration. If in doubt, refer to your model providers documentation or model library for guidance on the correct model variant to use.
### Supported Models
You must use a vision capable model with Frigate. Current model variants can be found [in their model library](https://ollama.com/search?c=vision). Note that Frigate will not automatically download the model you specify in your config, you must download the model to your local instance of Ollama first i.e. by running `ollama pull qwen3-vl:2b-instruct` on your Ollama server/Docker container. Note that the model specified in Frigate's config must match the downloaded model tag.
:::note
You should have at least 8 GB of RAM available (or VRAM if running on GPU) to run the 7B models, 16 GB to run the 13B models, and 32 GB to run the 33B models.
:::
#### Ollama Cloud models
Ollama also supports [cloud models](https://ollama.com/cloud), where your local Ollama instance handles requests from Frigate, but model inference is performed in the cloud. Set up Ollama locally, sign in with your Ollama account, and specify the cloud model name in your Frigate config. For more details, see the Ollama cloud model [docs](https://docs.ollama.com/cloud).
### Configuration
```yaml
genai:
provider: ollama
base_url: http://localhost:11434
model: qwen3-vl:4b
```
## Google Gemini
Google Gemini has a [free tier](https://ai.google.dev/pricing) for the API, however the limits may not be sufficient for standard Frigate usage. Choose a plan appropriate for your installation.
### Supported Models
You must use a vision capable model with Frigate. Current model variants can be found [in their documentation](https://ai.google.dev/gemini-api/docs/models/gemini).
### Get API Key
To start using Gemini, you must first get an API key from [Google AI Studio](https://aistudio.google.com).
1. Accept the Terms of Service
2. Click "Get API Key" from the right hand navigation
3. Click "Create API key in new project"
4. Copy the API key for use in your config
### Configuration
```yaml
genai:
provider: gemini
api_key: "{FRIGATE_GEMINI_API_KEY}"
model: gemini-2.5-flash
```
:::note
To use a different Gemini-compatible API endpoint, set the `GEMINI_BASE_URL` environment variable to your provider's API URL.
:::
## OpenAI
OpenAI does not have a free tier for their API. With the release of gpt-4o, pricing has been reduced and each generation should cost fractions of a cent if you choose to go this route.
### Supported Models
You must use a vision capable model with Frigate. Current model variants can be found [in their documentation](https://platform.openai.com/docs/models).
### Get API Key
To start using OpenAI, you must first [create an API key](https://platform.openai.com/api-keys) and [configure billing](https://platform.openai.com/settings/organization/billing/overview).
### Configuration
```yaml
genai:
provider: openai
api_key: "{FRIGATE_OPENAI_API_KEY}"
model: gpt-4o
```
:::note
To use a different OpenAI-compatible API endpoint, set the `OPENAI_BASE_URL` environment variable to your provider's API URL.
:::
## Azure OpenAI
Microsoft offers several vision models through Azure OpenAI. A subscription is required.
### Supported Models
You must use a vision capable model with Frigate. Current model variants can be found [in their documentation](https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models).
### Create Resource and Get API Key
To start using Azure OpenAI, you must first [create a resource](https://learn.microsoft.com/azure/cognitive-services/openai/how-to/create-resource?pivots=web-portal#create-a-resource). You'll need your API key, model name, and resource URL, which must include the `api-version` parameter (see the example below).
### Configuration
```yaml
genai:
provider: azure_openai
base_url: https://instance.cognitiveservices.azure.com/openai/responses?api-version=2025-04-01-preview
model: gpt-5-mini
api_key: "{FRIGATE_OPENAI_API_KEY}"
```
## Usage and Best Practices
Frigate's thumbnail search excels at identifying specific details about tracked objects for example, using an "image caption" approach to find a "person wearing a yellow vest," "a white dog running across the lawn," or "a red car on a residential street." To enhance this further, Frigates default prompts are designed to ask your AI provider about the intent behind the object's actions, rather than just describing its appearance.
While generating simple descriptions of detected objects is useful, understanding intent provides a deeper layer of insight. Instead of just recognizing "what" is in a scene, Frigates default prompts aim to infer "why" it might be there or "what" it could do next. Descriptions tell you whats happening, but intent gives context. For instance, a person walking toward a door might seem like a visitor, but if theyre moving quickly after hours, you can infer a potential break-in attempt. Detecting a person loitering near a door at night can trigger an alert sooner than simply noting "a person standing by the door," helping you respond based on the situations context.
### Using GenAI for notifications
Frigate provides an [MQTT topic](/integrations/mqtt), `frigate/tracked_object_update`, that is updated with a JSON payload containing `event_id` and `description` when your AI provider returns a description for a tracked object. This description could be used directly in notifications, such as sending alerts to your phone or making audio announcements. If additional details from the tracked object are needed, you can query the [HTTP API](/integrations/api/event-events-event-id-get) using the `event_id`, eg: `http://frigate_ip:5000/api/events/<event_id>`.
If looking to get notifications earlier than when an object ceases to be tracked, an additional send trigger can be configured of `after_significant_updates`.
```yaml
genai:
send_triggers:
tracked_object_end: true # default
after_significant_updates: 3 # how many updates to a tracked object before we should send an image
```
## Custom Prompts
Frigate sends multiple frames from the tracked object along with a prompt to your Generative AI provider asking it to generate a description. The default prompt is as follows:
```
Analyze the sequence of images containing the {label}. Focus on the likely intent or behavior of the {label} based on its actions and movement, rather than describing its appearance or the surroundings. Consider what the {label} is doing, why, and what it might do next.
```
:::tip
Prompts can use variable replacements `{label}`, `{sub_label}`, and `{camera}` to substitute information from the tracked object as part of the prompt.
:::
You are also able to define custom prompts in your configuration.
```yaml
genai:
provider: ollama
base_url: http://localhost:11434
model: qwen3-vl:8b-instruct
objects:
prompt: "Analyze the {label} in these images from the {camera} security camera. Focus on the actions, behavior, and potential intent of the {label}, rather than just describing its appearance."
object_prompts:
person: "Examine the main person in these images. What are they doing and what might their actions suggest about their intent (e.g., approaching a door, leaving an area, standing still)? Do not describe the surroundings or static details."
car: "Observe the primary vehicle in these images. Focus on its movement, direction, or purpose (e.g., parking, approaching, circling). If it's a delivery vehicle, mention the company."
```
Prompts can also be overridden at the camera level to provide a more detailed prompt to the model about your specific camera, if you desire.
```yaml
cameras:
front_door:
objects:
genai:
enabled: True
use_snapshot: True
prompt: "Analyze the {label} in these images from the {camera} security camera at the front door. Focus on the actions and potential intent of the {label}."
object_prompts:
person: "Examine the person in these images. What are they doing, and how might their actions suggest their purpose (e.g., delivering something, approaching, leaving)? If they are carrying or interacting with a package, include details about its source or destination."
cat: "Observe the cat in these images. Focus on its movement and intent (e.g., wandering, hunting, interacting with objects). If the cat is near the flower pots or engaging in any specific actions, mention it."
objects:
- person
- cat
required_zones:
- steps
```
### Experiment with prompts
Many providers also have a public facing chat interface for their models. Download a couple of different thumbnails or snapshots from Frigate and try new things in the playground to get descriptions to your liking before updating the prompt in Frigate.
- OpenAI - [ChatGPT](https://chatgpt.com)
- Gemini - [Google AI Studio](https://aistudio.google.com)
- Ollama - [Open WebUI](https://docs.openwebui.com/)

View File

@@ -17,23 +17,11 @@ Using Ollama on CPU is not recommended, high inference times make using Generati
:::
[Ollama](https://ollama.com/) allows you to self-host large language models and keep everything running locally. It is highly recommended to host this server on a machine with an Nvidia graphics card, or on a Apple silicon Mac for best performance.
[Ollama](https://ollama.com/) allows you to self-host large language models and keep everything running locally. It provides a nice API over [llama.cpp](https://github.com/ggerganov/llama.cpp). It is highly recommended to host this server on a machine with an Nvidia graphics card, or on a Apple silicon Mac for best performance.
Most of the 7b parameter 4-bit vision models will fit inside 8GB of VRAM. There is also a [Docker container](https://hub.docker.com/r/ollama/ollama) available.
Parallel requests also come with some caveats. You will need to set `OLLAMA_NUM_PARALLEL=1` and choose a `OLLAMA_MAX_QUEUE` and `OLLAMA_MAX_LOADED_MODELS` values that are appropriate for your hardware and preferences. See the [Ollama documentation](https://docs.ollama.com/faq#how-does-ollama-handle-concurrent-requests).
### Model Types: Instruct vs Thinking
Most vision-language models are available as **instruct** models, which are fine-tuned to follow instructions and respond concisely to prompts. However, some models (such as certain Qwen-VL or minigpt variants) offer both **instruct** and **thinking** versions.
- **Instruct models** are always recommended for use with Frigate. These models generate direct, relevant, actionable descriptions that best fit Frigate's object and event summary use case.
- **Thinking models** are fine-tuned for more free-form, open-ended, and speculative outputs, which are typically not concise and may not provide the practical summaries Frigate expects. For this reason, Frigate does **not** recommend or support using thinking models.
Some models are labeled as **hybrid** (capable of both thinking and instruct tasks). In these cases, Frigate will always use instruct-style prompts and specifically disables thinking-mode behaviors to ensure concise, useful responses.
**Recommendation:**
Always select the `-instruct` or documented instruct/tagged variant of any model you use in your Frigate configuration. If in doubt, refer to your model providers documentation or model library for guidance on the correct model variant to use.
Parallel requests also come with some caveats. You will need to set `OLLAMA_NUM_PARALLEL=1` and choose a `OLLAMA_MAX_QUEUE` and `OLLAMA_MAX_LOADED_MODELS` values that are appropriate for your hardware and preferences. See the [Ollama documentation](https://github.com/ollama/ollama/blob/main/docs/faq.md#how-does-ollama-handle-concurrent-requests).
### Supported Models
@@ -66,26 +54,26 @@ You should have at least 8 GB of RAM available (or VRAM if running on GPU) to ru
:::
#### Ollama Cloud models
Ollama also supports [cloud models](https://ollama.com/cloud), where your local Ollama instance handles requests from Frigate, but model inference is performed in the cloud. Set up Ollama locally, sign in with your Ollama account, and specify the cloud model name in your Frigate config. For more details, see the Ollama cloud model [docs](https://docs.ollama.com/cloud).
### Configuration
```yaml
genai:
provider: ollama
base_url: http://localhost:11434
model: qwen3-vl:4b
model: minicpm-v:8b
provider_options: # other Ollama client options can be defined
keep_alive: -1
options:
num_ctx: 8192 # make sure the context matches other services that are using ollama
```
## Google Gemini
Google Gemini has a [free tier](https://ai.google.dev/pricing) for the API, however the limits may not be sufficient for standard Frigate usage. Choose a plan appropriate for your installation.
Google Gemini has a free tier allowing [15 queries per minute](https://ai.google.dev/pricing) to the API, which is more than sufficient for standard Frigate usage.
### Supported Models
You must use a vision capable model with Frigate. Current model variants can be found [in their documentation](https://ai.google.dev/gemini-api/docs/models/gemini).
You must use a vision capable model with Frigate. Current model variants can be found [in their documentation](https://ai.google.dev/gemini-api/docs/models/gemini). At the time of writing, this includes `gemini-1.5-pro` and `gemini-1.5-flash`.
### Get API Key
@@ -102,32 +90,16 @@ To start using Gemini, you must first get an API key from [Google AI Studio](htt
genai:
provider: gemini
api_key: "{FRIGATE_GEMINI_API_KEY}"
model: gemini-2.5-flash
model: gemini-1.5-flash
```
:::note
To use a different Gemini-compatible API endpoint, set the `provider_options` with the `base_url` key to your provider's API URL. For example:
```
genai:
provider: gemini
...
provider_options:
base_url: https://...
```
Other HTTP options are available, see the [python-genai documentation](https://github.com/googleapis/python-genai).
:::
## OpenAI
OpenAI does not have a free tier for their API. With the release of gpt-4o, pricing has been reduced and each generation should cost fractions of a cent if you choose to go this route.
### Supported Models
You must use a vision capable model with Frigate. Current model variants can be found [in their documentation](https://platform.openai.com/docs/models).
You must use a vision capable model with Frigate. Current model variants can be found [in their documentation](https://platform.openai.com/docs/models). At the time of writing, this includes `gpt-4o` and `gpt-4-turbo`.
### Get API Key
@@ -171,18 +143,17 @@ Microsoft offers several vision models through Azure OpenAI. A subscription is r
### Supported Models
You must use a vision capable model with Frigate. Current model variants can be found [in their documentation](https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models).
You must use a vision capable model with Frigate. Current model variants can be found [in their documentation](https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models). At the time of writing, this includes `gpt-4o` and `gpt-4-turbo`.
### Create Resource and Get API Key
To start using Azure OpenAI, you must first [create a resource](https://learn.microsoft.com/azure/cognitive-services/openai/how-to/create-resource?pivots=web-portal#create-a-resource). You'll need your API key, model name, and resource URL, which must include the `api-version` parameter (see the example below).
To start using Azure OpenAI, you must first [create a resource](https://learn.microsoft.com/azure/cognitive-services/openai/how-to/create-resource?pivots=web-portal#create-a-resource). You'll need your API key and resource URL, which must include the `api-version` parameter (see the example below). The model field is not required in your configuration as the model is part of the deployment name you chose when deploying the resource.
### Configuration
```yaml
genai:
provider: azure_openai
base_url: https://instance.cognitiveservices.azure.com/openai/responses?api-version=2025-04-01-preview
model: gpt-5-mini
base_url: https://example-endpoint.openai.azure.com/openai/deployments/gpt-4o/chat/completions?api-version=2023-03-15-preview
api_key: "{FRIGATE_OPENAI_API_KEY}"
```

View File

@@ -381,7 +381,6 @@ Start with ["Why isn't my license plate being detected and recognized?"](#why-is
```yaml
lpr:
enabled: true
device: CPU
debug_save_plates: true
```

View File

@@ -1514,11 +1514,11 @@ RF-DETR can be exported as ONNX by running the command below. You can copy and p
```sh
docker build . --build-arg MODEL_SIZE=Nano --rm --output . -f- <<'EOF'
FROM python:3.12 AS build
FROM python:3.11 AS build
RUN apt-get update && apt-get install --no-install-recommends -y libgl1 && rm -rf /var/lib/apt/lists/*
COPY --from=ghcr.io/astral-sh/uv:0.10.4 /uv /bin/
COPY --from=ghcr.io/astral-sh/uv:0.8.0 /uv /bin/
WORKDIR /rfdetr
RUN uv pip install --system rfdetr[onnxexport] torch==2.8.0 onnx==1.19.1 transformers==4.57.6 onnxscript
RUN uv pip install --system rfdetr[onnxexport] torch==2.8.0 onnx==1.19.1 onnxscript
ARG MODEL_SIZE
RUN python3 -c "from rfdetr import RFDETR${MODEL_SIZE}; x = RFDETR${MODEL_SIZE}(resolution=320); x.export(simplify=True)"
FROM scratch

View File

@@ -214,12 +214,6 @@ The `exec:`, `echo:`, and `expr:` sources are disabled by default for security.
:::
:::warning
The `exec:`, `echo:`, and `expr:` sources are disabled by default for security. You must set `GO2RTC_ALLOW_ARBITRARY_EXEC=true` to use them. See [Security: Restricted Stream Sources](#security-restricted-stream-sources) for more information.
:::
NOTE: The output will need to be passed with two curly braces `{{output}}`
```yaml

View File

@@ -167,7 +167,7 @@ Inference speeds vary greatly depending on the CPU or GPU used, some known examp
| Intel N100 | ~ 15 ms | s-320: 30 ms | 320: ~ 25 ms | | Can only run one detector instance |
| Intel N150 | ~ 15 ms | t-320: 16 ms s-320: 24 ms | | | |
| Intel Iris XE | ~ 10 ms | t-320: 6 ms t-640: 14 ms s-320: 8 ms s-640: 16 ms | 320: ~ 10 ms 640: ~ 20 ms | 320-n: 33 ms | |
| Intel NPU | ~ 6 ms | s-320: 11 ms s-640: 30 ms | 320: ~ 14 ms 640: ~ 34 ms | 320-n: 40 ms | |
| Intel NPU | ~ 6 ms | s-320: 11 ms | 320: ~ 14 ms 640: ~ 34 ms | 320-n: 40 ms | |
| Intel Arc A310 | ~ 5 ms | t-320: 7 ms t-640: 11 ms s-320: 8 ms s-640: 15 ms | 320: ~ 8 ms 640: ~ 14 ms | | |
| Intel Arc A380 | ~ 6 ms | | 320: ~ 10 ms 640: ~ 22 ms | 336: 20 ms 448: 27 ms | |
| Intel Arc A750 | ~ 4 ms | | 320: ~ 8 ms | | |
@@ -186,7 +186,7 @@ There are improved capabilities in newer GPU architectures that TensorRT can ben
#### Compatibility References:
[NVIDIA TensorRT Support Matrix](https://docs.nvidia.com/deeplearning/tensorrt-rtx/latest/getting-started/support-matrix.html)
[NVIDIA TensorRT Support Matrix](https://docs.nvidia.com/deeplearning/tensorrt/archives/tensorrt-841/support-matrix/index.html)
[NVIDIA CUDA Compatibility](https://docs.nvidia.com/deploy/cuda-compatibility/index.html)

View File

@@ -112,65 +112,42 @@ The Hailo-8 and Hailo-8L AI accelerators are available in both M.2 and HAT form
:::warning
On Raspberry Pi OS **Bookworm**, the kernel includes an older version of the Hailo driver that is incompatible with Frigate. You **must** follow the installation steps below to install the correct driver version, and you **must** disable the built-in kernel driver as described in step 1.
On Raspberry Pi OS **Trixie**, the Hailo driver is no longer shipped with the kernel. It is installed via DKMS, and the conflict described below does not apply. You can simply run the installation script.
The Raspberry Pi kernel includes an older version of the Hailo driver that is incompatible with Frigate. You **must** follow the installation steps below to install the correct driver version, and you **must** disable the built-in kernel driver as described in step 1.
:::
1. **Disable the built-in Hailo driver (Raspberry Pi Bookworm OS only)**:
1. **Disable the built-in Hailo driver (Raspberry Pi only)**:
:::note
If you are **not** using a Raspberry Pi with **Bookworm OS**, skip this step and proceed directly to step 2.
If you are using Raspberry Pi with **Trixie OS**, also skip this step and proceed directly to step 2.
If you are **not** using a Raspberry Pi, skip this step and proceed directly to step 2.
:::
First, check if the driver is currently loaded:
If you are using a Raspberry Pi, you need to blacklist the built-in kernel Hailo driver to prevent conflicts. First, check if the driver is currently loaded:
```bash
lsmod | grep hailo
```
If it shows `hailo_pci`, unload it:
```bash
sudo modprobe -r hailo_pci
```
Then locate the built-in kernel driver and rename it so it cannot be loaded.
Renaming allows the original driver to be restored later if needed.
First, locate the currently installed kernel module:
```bash
modinfo -n hailo_pci
sudo rmmod hailo_pci
```
Example output:
```
/lib/modules/6.6.31+rpt-rpi-2712/kernel/drivers/media/pci/hailo/hailo_pci.ko.xz
```
Save the module path to a variable:
Now blacklist the driver to prevent it from loading on boot:
```bash
BUILTIN=$(modinfo -n hailo_pci)
echo "blacklist hailo_pci" | sudo tee /etc/modprobe.d/blacklist-hailo_pci.conf
```
And rename the module by appending .bak:
Update initramfs to ensure the blacklist takes effect:
```bash
sudo mv "$BUILTIN" "${BUILTIN}.bak"
sudo update-initramfs -u
```
Now refresh the kernel module map so the system recognizes the change:
```bash
sudo depmod -a
```
Reboot your Raspberry Pi:
```bash
@@ -183,7 +160,7 @@ On Raspberry Pi OS **Trixie**, the Hailo driver is no longer shipped with the ke
lsmod | grep hailo
```
This command should return no results.
This command should return no results. If it still shows `hailo_pci`, the blacklist did not take effect properly and you may need to check for other Hailo packages installed via apt that are loading the driver.
2. **Run the installation script**:
@@ -235,38 +212,6 @@ On Raspberry Pi OS **Trixie**, the Hailo driver is no longer shipped with the ke
lsmod | grep hailo_pci
```
Verify the driver version:
```bash
cat /sys/module/hailo_pci/version
```
Verify that the firmware was installed correctly:
```bash
ls -l /lib/firmware/hailo/hailo8_fw.bin
```
**Optional: Fix PCIe descriptor page size error**
If you encounter the following error:
```
[HailoRT] [error] CHECK failed - max_desc_page_size given 16384 is bigger than hw max desc page size 4096
```
Create a configuration file to force the correct descriptor page size:
```bash
echo 'options hailo_pci force_desc_page_size=4096' | sudo tee /etc/modprobe.d/hailo_pci.conf
```
and reboot:
```bash
sudo reboot
```
#### Setup
To set up Frigate, follow the default installation instructions, for example: `ghcr.io/blakeblackshear/frigate:stable`
@@ -689,42 +634,3 @@ docker run \
```
Log into QNAP, open Container Station. Frigate docker container should be listed under 'Overview' and running. Visit Frigate Web UI by clicking Frigate docker, and then clicking the URL shown at the top of the detail page.
## macOS - Apple Silicon
:::warning
macOS uses port 5000 for its Airplay Receiver service. If you want to expose port 5000 in Frigate for local app and API access the port will need to be mapped to another port on the host e.g. 5001
Failure to remap port 5000 on the host will result in the WebUI and all API endpoints on port 5000 being unreachable, even if port 5000 is exposed correctly in Docker.
:::
Docker containers on macOS can be orchestrated by either [Docker Desktop](https://docs.docker.com/desktop/setup/install/mac-install/) or [OrbStack](https://orbstack.dev) (native swift app). The difference in inference speeds is negligable, however CPU, power consumption and container start times will be lower on OrbStack because it is a native Swift application.
To allow Frigate to use the Apple Silicon Neural Engine / Processing Unit (NPU) the host must be running [Apple Silicon Detector](../configuration/object_detectors.md#apple-silicon-detector) on the host (outside Docker)
#### Docker Compose example
```yaml
services:
frigate:
container_name: frigate
image: ghcr.io/blakeblackshear/frigate:stable-standard-arm64
restart: unless-stopped
shm_size: "512mb" # update for your cameras based on calculation above
volumes:
- /etc/localtime:/etc/localtime:ro
- /path/to/your/config:/config
- /path/to/your/recordings:/recordings
ports:
- "8971:8971"
# If exposing on macOS map to a diffent host port like 5001 or any orher port with no conflicts
# - "5001:5000" # Internal unauthenticated access. Expose carefully.
- "8554:8554" # RTSP feeds
extra_hosts:
# This is very important
# It allows frigate access to the NPU on Apple Silicon via Apple Silicon Detector
- "host.docker.internal:host-gateway" # Required to talk to the NPU detector
environment:
- FRIGATE_RTSP_PASSWORD: "password"
```

View File

@@ -9064,9 +9064,9 @@
}
},
"node_modules/diff": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz",
"integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==",
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-5.2.2.tgz",
"integrity": "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.3.1"

View File

@@ -350,15 +350,21 @@ def validate_password_strength(password: str) -> tuple[bool, Optional[str]]:
Validate password strength.
Returns a tuple of (is_valid, error_message).
Longer passwords are harder to crack than shorter complex ones.
https://pages.nist.gov/800-63-3/sp800-63b.html
"""
if not password:
return False, "Password cannot be empty"
if len(password) < 12:
return False, "Password must be at least 12 characters long"
if len(password) < 8:
return False, "Password must be at least 8 characters long"
if not any(c.isupper() for c in password):
return False, "Password must contain at least one uppercase letter"
if not any(c.isdigit() for c in password):
return False, "Password must contain at least one digit"
if not any(c in '!@#$%^&*(),.?":{}|<>' for c in password):
return False, "Password must contain at least one special character"
return True, None
@@ -439,11 +445,10 @@ def resolve_role(
Determine the effective role for a request based on proxy headers and configuration.
Order of resolution:
1. If a role header is defined in proxy_config.header_map.role:
- If a role_map is configured, treat the header as group claims
(split by proxy_config.separator) and map to roles.
Admin matches short-circuit to admin.
- If no role_map is configured, treat the header as role names directly.
1. If a role header is defined in proxy_config.header_map.role:
- If a role_map is configured, treat the header as group claims
(split by proxy_config.separator) and map to roles.
- If no role_map is configured, treat the header as role names directly.
2. If no valid role is found, return proxy_config.default_role if it's valid in config_roles, else 'viewer'.
Args:
@@ -493,12 +498,6 @@ def resolve_role(
}
logger.debug("Matched roles from role_map: %s", matched_roles)
# If admin matches, prioritize it to avoid accidental downgrade when
# users belong to both admin and lower-privilege groups.
if "admin" in matched_roles and "admin" in config_roles:
logger.debug("Resolved role (with role_map) to 'admin'.")
return "admin"
if matched_roles:
resolved = next(
(r for r in config_roles if r in matched_roles), validated_default
@@ -801,7 +800,7 @@ def get_users():
"/users",
dependencies=[Depends(require_role(["admin"]))],
summary="Create new user",
description="Creates a new user with the specified username, password, and role. Requires admin role. Password must be at least 12 characters long.",
description='Creates a new user with the specified username, password, and role. Requires admin role. Password must meet strength requirements: minimum 8 characters, at least one uppercase letter, at least one digit, and at least one special character (!@#$%^&*(),.?":{} |<>).',
)
def create_user(
request: Request,
@@ -818,15 +817,6 @@ def create_user(
content={"message": f"Role must be one of: {', '.join(config_roles)}"},
status_code=400,
)
# Validate password strength
is_valid, error_message = validate_password_strength(body.password)
if not is_valid:
return JSONResponse(
content={"message": error_message},
status_code=400,
)
role = body.role or "viewer"
password_hash = hash_password(body.password, iterations=HASH_ITERATIONS)
User.insert(
@@ -861,7 +851,7 @@ def delete_user(request: Request, username: str):
"/users/{username}/password",
dependencies=[Depends(allow_any_authenticated())],
summary="Update user password",
description="Updates a user's password. Users can only change their own password unless they have admin role. Requires the current password to verify identity for non-admin users. Password must be at least 12 characters long. If user changes their own password, a new JWT cookie is automatically issued.",
description="Updates a user's password. Users can only change their own password unless they have admin role. Requires the current password to verify identity for non-admin users. Password must meet strength requirements: minimum 8 characters, at least one uppercase letter, at least one digit, and at least one special character (!@#$%^&*(),.?\":{} |<>). If user changes their own password, a new JWT cookie is automatically issued.",
)
async def update_password(
request: Request,
@@ -986,16 +976,7 @@ async def require_camera_access(
current_user = await get_current_user(request)
if isinstance(current_user, JSONResponse):
detail = "Authentication required"
try:
error_payload = json.loads(current_user.body)
detail = (
error_payload.get("message") or error_payload.get("detail") or detail
)
except Exception:
pass
raise HTTPException(status_code=current_user.status_code, detail=detail)
return current_user
role = current_user["role"]
all_camera_names = set(request.app.frigate_config.cameras.keys())
@@ -1013,61 +994,6 @@ async def require_camera_access(
)
def _get_stream_owner_cameras(request: Request, stream_name: str) -> set[str]:
owner_cameras: set[str] = set()
for camera_name, camera in request.app.frigate_config.cameras.items():
if stream_name == camera_name:
owner_cameras.add(camera_name)
continue
if stream_name in camera.live.streams.values():
owner_cameras.add(camera_name)
return owner_cameras
async def require_go2rtc_stream_access(
stream_name: Optional[str] = None,
request: Request = None,
):
"""Dependency to enforce go2rtc stream access based on owning camera access."""
if stream_name is None:
return
current_user = await get_current_user(request)
if isinstance(current_user, JSONResponse):
detail = "Authentication required"
try:
error_payload = json.loads(current_user.body)
detail = (
error_payload.get("message") or error_payload.get("detail") or detail
)
except Exception:
pass
raise HTTPException(status_code=current_user.status_code, detail=detail)
role = current_user["role"]
all_camera_names = set(request.app.frigate_config.cameras.keys())
roles_dict = request.app.frigate_config.auth.roles
allowed_cameras = User.get_allowed_cameras(role, roles_dict, all_camera_names)
# Admin or full access bypasses
if role == "admin" or not roles_dict.get(role):
return
owner_cameras = _get_stream_owner_cameras(request, stream_name)
if owner_cameras & set(allowed_cameras):
return
raise HTTPException(
status_code=403,
detail=f"Access denied to camera '{stream_name}'. Allowed: {allowed_cameras}",
)
async def get_allowed_cameras_for_filter(request: Request):
"""Dependency to get allowed_cameras for filtering lists."""
current_user = await get_current_user(request)

View File

@@ -17,7 +17,7 @@ from zeep.transports import AsyncTransport
from frigate.api.auth import (
allow_any_authenticated,
require_go2rtc_stream_access,
require_camera_access,
require_role,
)
from frigate.api.defs.tags import Tags
@@ -71,27 +71,14 @@ def go2rtc_streams():
@router.get(
"/go2rtc/streams/{stream_name}",
dependencies=[Depends(require_go2rtc_stream_access)],
"/go2rtc/streams/{camera_name}", dependencies=[Depends(require_camera_access)]
)
def go2rtc_camera_stream(request: Request, stream_name: str):
def go2rtc_camera_stream(request: Request, camera_name: str):
r = requests.get(
"http://127.0.0.1:1984/api/streams",
params={
"src": stream_name,
"video": "all",
"audio": "all",
"microphone": "",
},
f"http://127.0.0.1:1984/api/streams?src={camera_name}&video=all&audio=all&microphone"
)
if not r.ok:
camera_config = request.app.frigate_config.cameras.get(stream_name)
if camera_config is None:
for camera_name, camera in request.app.frigate_config.cameras.items():
if stream_name in camera.live.streams.values():
camera_config = request.app.frigate_config.cameras.get(camera_name)
break
camera_config = request.app.frigate_config.cameras.get(camera_name)
if camera_config and camera_config.enabled:
logger.error("Failed to fetch streams from go2rtc")

View File

@@ -73,7 +73,7 @@ def get_faces():
face_dict[name] = []
for file in filter(
lambda f: f.lower().endswith((".webp", ".png", ".jpg", ".jpeg")),
lambda f: (f.lower().endswith((".webp", ".png", ".jpg", ".jpeg"))),
os.listdir(face_dir),
):
face_dict[name].append(file)
@@ -582,7 +582,7 @@ def get_classification_dataset(name: str):
dataset_dict[category_name] = []
for file in filter(
lambda f: f.lower().endswith((".webp", ".png", ".jpg", ".jpeg")),
lambda f: (f.lower().endswith((".webp", ".png", ".jpg", ".jpeg"))),
os.listdir(category_dir),
):
dataset_dict[category_name].append(file)
@@ -693,7 +693,7 @@ def get_classification_images(name: str):
status_code=200,
content=list(
filter(
lambda f: f.lower().endswith((".webp", ".png", ".jpg", ".jpeg")),
lambda f: (f.lower().endswith((".webp", ".png", ".jpg", ".jpeg"))),
os.listdir(train_dir),
)
),
@@ -759,28 +759,15 @@ def delete_classification_dataset_images(
CLIPS_DIR, sanitize_filename(name), "dataset", sanitize_filename(category)
)
deleted_count = 0
for id in list_of_ids:
file_path = os.path.join(folder, sanitize_filename(id))
if os.path.isfile(file_path):
os.unlink(file_path)
deleted_count += 1
if os.path.exists(folder) and not os.listdir(folder) and category.lower() != "none":
os.rmdir(folder)
# Update training metadata to reflect deleted images
# This ensures the dataset is marked as changed after deletion
# (even if the total count happens to be the same after adding and deleting)
if deleted_count > 0:
sanitized_name = sanitize_filename(name)
metadata = read_training_metadata(sanitized_name)
if metadata:
last_count = metadata.get("last_training_image_count", 0)
updated_count = max(0, last_count - deleted_count)
write_training_metadata(sanitized_name, updated_count)
return JSONResponse(
content=({"success": True, "message": "Successfully deleted images."}),
status_code=200,

View File

@@ -69,25 +69,6 @@ logger = logging.getLogger(__name__)
router = APIRouter(tags=[Tags.events])
def _build_attribute_filter_clause(attributes: str):
filtered_attributes = [
attr.strip() for attr in attributes.split(",") if attr.strip()
]
attribute_clauses = []
for attr in filtered_attributes:
attribute_clauses.append(Event.data.cast("text") % f'*:"{attr}"*')
escaped_attr = json.dumps(attr, ensure_ascii=True)[1:-1]
if escaped_attr != attr:
attribute_clauses.append(Event.data.cast("text") % f'*:"{escaped_attr}"*')
if not attribute_clauses:
return None
return reduce(operator.or_, attribute_clauses)
@router.get(
"/events",
response_model=list[EventResponse],
@@ -212,9 +193,14 @@ def events(
if attributes != "all":
# Custom classification results are stored as data[model_name] = result_value
attribute_clause = _build_attribute_filter_clause(attributes)
if attribute_clause is not None:
clauses.append(attribute_clause)
filtered_attributes = attributes.split(",")
attribute_clauses = []
for attr in filtered_attributes:
attribute_clauses.append(Event.data.cast("text") % f'*:"{attr}"*')
attribute_clause = reduce(operator.or_, attribute_clauses)
clauses.append(attribute_clause)
if recognized_license_plate != "all":
filtered_recognized_license_plates = recognized_license_plate.split(",")
@@ -522,7 +508,7 @@ def events_search(
cameras = params.cameras
labels = params.labels
sub_labels = params.sub_labels
attributes = unquote(params.attributes)
attributes = params.attributes
zones = params.zones
after = params.after
before = params.before
@@ -621,9 +607,13 @@ def events_search(
if attributes != "all":
# Custom classification results are stored as data[model_name] = result_value
attribute_clause = _build_attribute_filter_clause(attributes)
if attribute_clause is not None:
event_filters.append(attribute_clause)
filtered_attributes = attributes.split(",")
attribute_clauses = []
for attr in filtered_attributes:
attribute_clauses.append(Event.data.cast("text") % f'*:"{attr}"*')
event_filters.append(reduce(operator.or_, attribute_clauses))
if zones != "all":
zone_clauses = []

View File

@@ -108,13 +108,12 @@ class GenAIReviewConfig(FrigateBaseModel):
default="""### Normal Activity Indicators (Level 0)
- Known/verified people in any zone at any time
- People with pets in residential areas
- Routine residential vehicle access during daytime/evening (6 AM - 10 PM): entering, exiting, loading/unloading items — normal commute and travel patterns
- Deliveries or services during daytime/evening (6 AM - 10 PM): carrying packages to doors/porches, placing items, leaving
- Services/maintenance workers with visible tools, uniforms, or service vehicles during daytime
- Activity confined to public areas only (sidewalks, streets) without entering property at any time
### Suspicious Activity Indicators (Level 1)
- **Checking or probing vehicle/building access**: trying handles without entering, peering through windows, examining multiple vehicles, or possessing break-in tools — Level 1
- **Testing or attempting to open doors/windows/handles on vehicles or buildings** — ALWAYS Level 1 regardless of time or duration
- **Unidentified person in private areas (driveways, near vehicles/buildings) during late night/early morning (11 PM - 5 AM)** — ALWAYS Level 1 regardless of activity or duration
- Taking items that don't belong to them (packages, objects from porches/driveways)
- Climbing or jumping fences/barriers to access property
@@ -134,8 +133,8 @@ Evaluate in this order:
1. **If person is verified/known** → Level 0 regardless of time or activity
2. **If person is unidentified:**
- Check time: If late night/early morning (11 PM - 5 AM) AND in private areas (driveways, near vehicles/buildings) → Level 1
- Check actions: If probing access (trying handles without entering, checking multiple vehicles), taking items, climbing → Level 1
- Otherwise, if daytime/evening (6 AM - 10 PM) with clear legitimate purpose (delivery, service, routine vehicle access) → Level 0
- Check actions: If testing doors/handles, taking items, climbing → Level 1
- Otherwise, if daytime/evening (6 AM - 10 PM) with clear legitimate purpose (delivery, service worker) → Level 0
3. **Escalate to Level 2 if:** Weapons, break-in tools, forced entry in progress, violence, or active property damage visible (escalates from Level 0 or 1)
The mere presence of an unidentified person in private areas during late night hours is inherently suspicious and warrants human review, regardless of what activity they appear to be doing or how brief the sequence is.""",

View File

@@ -97,7 +97,7 @@ class CustomStateClassificationProcessor(RealTimeProcessorApi):
self.interpreter.allocate_tensors()
self.tensor_input_details = self.interpreter.get_input_details()
self.tensor_output_details = self.interpreter.get_output_details()
self.labelmap = load_labels(labelmap_path, prefill=0, indexed=False)
self.labelmap = load_labels(labelmap_path, prefill=0)
self.classifications_per_second.start()
def __update_metrics(self, duration: float) -> None:
@@ -398,7 +398,7 @@ class CustomObjectClassificationProcessor(RealTimeProcessorApi):
self.interpreter.allocate_tensors()
self.tensor_input_details = self.interpreter.get_input_details()
self.tensor_output_details = self.interpreter.get_output_details()
self.labelmap = load_labels(labelmap_path, prefill=0, indexed=False)
self.labelmap = load_labels(labelmap_path, prefill=0)
def __update_metrics(self, duration: float) -> None:
self.classifications_per_second.update()
@@ -419,21 +419,14 @@ class CustomObjectClassificationProcessor(RealTimeProcessorApi):
"""
if object_id not in self.classification_history:
self.classification_history[object_id] = []
logger.debug(f"Created new classification history for {object_id}")
self.classification_history[object_id].append(
(current_label, current_score, current_time)
)
history = self.classification_history[object_id]
logger.debug(
f"History for {object_id}: {len(history)} entries, latest=({current_label}, {current_score})"
)
if len(history) < 3:
logger.debug(
f"History for {object_id} has {len(history)} entries, need at least 3"
)
return None, 0.0
label_counts = {}
@@ -452,27 +445,14 @@ class CustomObjectClassificationProcessor(RealTimeProcessorApi):
best_count = label_counts[best_label]
consensus_threshold = total_attempts * 0.6
logger.debug(
f"Consensus calc for {object_id}: label_counts={label_counts}, "
f"best_label={best_label}, best_count={best_count}, "
f"total={total_attempts}, threshold={consensus_threshold}"
)
if best_count < consensus_threshold:
logger.debug(
f"No consensus for {object_id}: {best_count} < {consensus_threshold}"
)
return None, 0.0
avg_score = sum(label_scores[best_label]) / len(label_scores[best_label])
if best_label == "none":
logger.debug(f"Filtering 'none' label for {object_id}")
return None, 0.0
logger.debug(
f"Consensus reached for {object_id}: {best_label} with avg_score={avg_score}"
)
return best_label, avg_score
def process_frame(self, obj_data, frame):
@@ -580,30 +560,17 @@ class CustomObjectClassificationProcessor(RealTimeProcessorApi):
)
if score < self.model_config.threshold:
logger.debug(
f"{self.model_config.name}: Score {score} < threshold {self.model_config.threshold} for {object_id}, skipping"
)
logger.debug(f"Score {score} is less than threshold.")
return
sub_label = self.labelmap[best_id]
logger.debug(
f"{self.model_config.name}: Object {object_id} (label={obj_data['label']}) passed threshold with sub_label={sub_label}, score={score}"
)
consensus_label, consensus_score = self.get_weighted_score(
object_id, sub_label, score, now
)
logger.debug(
f"{self.model_config.name}: get_weighted_score returned consensus_label={consensus_label}, consensus_score={consensus_score} for {object_id}"
)
if consensus_label is not None:
camera = obj_data["camera"]
logger.debug(
f"{self.model_config.name}: Publishing sub_label={consensus_label} for {obj_data['label']} object {object_id} on {camera}"
)
if (
self.model_config.object_config.classification_type
@@ -658,7 +625,6 @@ class CustomObjectClassificationProcessor(RealTimeProcessorApi):
def handle_request(self, topic, request_data):
if topic == EmbeddingsRequestEnum.reload_classification_model.value:
if request_data.get("model_name") == self.model_config.name:
self.__build_detector()
logger.info(
f"Successfully loaded updated model for {self.model_config.name}"
)
@@ -696,7 +662,7 @@ def write_classification_attempt(
# delete oldest face image if maximum is reached
try:
files = sorted(
filter(lambda f: f.endswith(".webp"), os.listdir(folder)),
filter(lambda f: (f.endswith(".webp")), os.listdir(folder)),
key=lambda f: os.path.getctime(os.path.join(folder, f)),
reverse=True,
)

View File

@@ -539,7 +539,7 @@ class FaceRealTimeProcessor(RealTimeProcessorApi):
cv2.imwrite(file, frame)
files = sorted(
filter(lambda f: f.endswith(".webp"), os.listdir(folder)),
filter(lambda f: (f.endswith(".webp")), os.listdir(folder)),
key=lambda f: os.path.getctime(os.path.join(folder, f)),
reverse=True,
)

View File

@@ -633,7 +633,7 @@ class EmbeddingMaintainer(threading.Thread):
camera, frame_name, _, _, motion_boxes, _ = data
if not camera or camera not in self.config.cameras:
if not camera or len(motion_boxes) == 0 or camera not in self.config.cameras:
return
camera_config = self.config.cameras[camera]
@@ -660,10 +660,8 @@ class EmbeddingMaintainer(threading.Thread):
return
for processor in self.realtime_processors:
if (
dedicated_lpr_enabled
and len(motion_boxes) > 0
and isinstance(processor, LicensePlateRealTimeProcessor)
if dedicated_lpr_enabled and isinstance(
processor, LicensePlateRealTimeProcessor
):
processor.process_frame(camera, yuv_frame, True)

View File

@@ -6,7 +6,6 @@ from typing import Dict
from frigate.comms.events_updater import EventEndPublisher, EventUpdateSubscriber
from frigate.config import FrigateConfig
from frigate.config.classification import ObjectClassificationType
from frigate.events.types import EventStateEnum, EventTypeEnum
from frigate.models import Event
from frigate.util.builtin import to_relative_box
@@ -16,16 +15,6 @@ logger = logging.getLogger(__name__)
def should_update_db(prev_event: Event, current_event: Event) -> bool:
"""If current_event has updated fields and (clip or snapshot)."""
# If event is ending and was previously saved, always update to set end_time
# This ensures events are properly ended even when alerts/detections are disabled
# mid-event (which can cause has_clip/has_snapshot to become False)
if (
prev_event["end_time"] is None
and current_event["end_time"] is not None
and (prev_event["has_clip"] or prev_event["has_snapshot"])
):
return True
if current_event["has_clip"] or current_event["has_snapshot"]:
# if this is the first time has_clip or has_snapshot turned true
if not prev_event["has_clip"] and not prev_event["has_snapshot"]:
@@ -248,18 +237,6 @@ class EventProcessor(threading.Thread):
"recognized_license_plate"
][1]
# only overwrite attribute-type custom model fields in the database if they're set
for name, model_config in self.config.classification.custom.items():
if (
model_config.object_config
and model_config.object_config.classification_type
== ObjectClassificationType.attribute
):
value = event_data.get(name)
if value is not None:
event[Event.data][name] = value[0]
event[Event.data][f"{name}_score"] = value[1]
(
Event.insert(event)
.on_conflict(

View File

@@ -99,8 +99,8 @@ When forming your description:
## Response Format
Your response MUST be a flat JSON object with:
- `title` (string): A concise, direct title that describes the primary action or event in the sequence, not just what you literally see. Use spatial context when available to make titles more meaningful. When multiple objects/actions are present, prioritize whichever is most prominent or occurs first. Use names from "Objects in Scene" based on what you visually observe. If you see both a name and an unidentified object of the same type but visually observe only one person/object, use ONLY the name. Examples: "Joe walking dog", "Person taking out trash", "Vehicle arriving in driveway", "Joe accessing vehicle", "Person leaving porch for driveway".
- `scene` (string): A narrative description of what happens across the sequence from start to finish, in chronological order. Start by describing how the sequence begins, then describe the progression of events. **Describe all significant movements and actions in the order they occur.** For example, if a vehicle arrives and then a person exits, describe both actions sequentially. **Only describe actions you can actually observe happening in the frames provided.** Do not infer or assume actions that aren't visible (e.g., if you see someone walking but never see them sit, don't say they sat down). Include setting, detected objects, and their observable actions. Avoid speculation or filling in assumed behaviors. Your description should align with and support the threat level you assign.
- `title` (string): A concise, grammatically complete title in the format "[Subject] [action verb] [context]" that matches your scene description. Use names from "Objects in Scene" when you visually observe them.
- `shortSummary` (string): A brief 2-sentence summary of the scene, suitable for notifications. Should capture the key activity and context without full detail. This should be a condensed version of the scene description above.
- `confidence` (float): 0-1 confidence in your analysis. Higher confidence when objects/actions are clearly visible and context is unambiguous. Lower confidence when the sequence is unclear, objects are partially obscured, or context is ambiguous.
- `potential_threat_level` (integer): 0, 1, or 2 as defined in "Normal Activity Patterns for This Property" above. Your threat level must be consistent with your scene description and the guidance above.

View File

@@ -22,6 +22,7 @@ class GeminiClient(GenAIClient):
"""Initialize the client."""
# Merge provider_options into HttpOptions
http_options_dict = {
"api_version": "v1",
"timeout": int(self.timeout * 1000), # requires milliseconds
"retry_options": types.HttpRetryOptions(
attempts=3,

View File

@@ -26,16 +26,15 @@ LOG_HANDLER.setFormatter(
# filter out norfair warning
LOG_HANDLER.addFilter(
lambda record: (
not record.getMessage().startswith("You are using a scalar distance function")
lambda record: not record.getMessage().startswith(
"You are using a scalar distance function"
)
)
# filter out tflite logging
LOG_HANDLER.addFilter(
lambda record: (
"Created TensorFlow Lite XNNPACK delegate for CPU." not in record.getMessage()
)
lambda record: "Created TensorFlow Lite XNNPACK delegate for CPU."
not in record.getMessage()
)

View File

@@ -208,10 +208,8 @@ class RecordingMaintainer(threading.Thread):
processed_segment_count = len(
list(
filter(
lambda r: (
r["start_time"].timestamp()
< most_recently_processed_frame_time
),
lambda r: r["start_time"].timestamp()
< most_recently_processed_frame_time,
grouped_recordings[camera],
)
)

View File

@@ -1,7 +1,6 @@
from unittest.mock import patch
from fastapi import HTTPException, Request
from fastapi.testclient import TestClient
from frigate.api.auth import (
get_allowed_cameras_for_filter,
@@ -10,33 +9,6 @@ from frigate.api.auth import (
from frigate.models import Event, Recordings, ReviewSegment
from frigate.test.http_api.base_http_test import AuthTestClient, BaseTestHttp
# Minimal multi-camera config used by go2rtc stream access tests.
# front_door has a stream alias "front_door_main"; back_door uses its own name.
# The "limited_user" role is restricted to front_door only.
_MULTI_CAMERA_CONFIG = {
"mqtt": {"host": "mqtt"},
"auth": {
"roles": {
"limited_user": ["front_door"],
}
},
"cameras": {
"front_door": {
"ffmpeg": {
"inputs": [{"path": "rtsp://10.0.0.1:554/video", "roles": ["detect"]}]
},
"detect": {"height": 1080, "width": 1920, "fps": 5},
"live": {"streams": {"default": "front_door_main"}},
},
"back_door": {
"ffmpeg": {
"inputs": [{"path": "rtsp://10.0.0.2:554/video", "roles": ["detect"]}]
},
"detect": {"height": 1080, "width": 1920, "fps": 5},
},
},
}
class TestCameraAccessEventReview(BaseTestHttp):
def setUp(self):
@@ -218,179 +190,3 @@ class TestCameraAccessEventReview(BaseTestHttp):
resp = client.get("/events/summary")
summary_list = resp.json()
assert len(summary_list) == 2
class TestGo2rtcStreamAccess(BaseTestHttp):
"""Tests for require_go2rtc_stream_access — the auth dependency on
GET /go2rtc/streams/{stream_name}.
go2rtc is not running in unit tests, so an authorized request returns
500 (the proxy call fails), while an unauthorized request returns 401/403
before the proxy is ever reached.
"""
def _make_app(self, config_override: dict | None = None):
"""Build a test app, optionally replacing self.minimal_config."""
if config_override is not None:
self.minimal_config = config_override
app = super().create_app()
# Allow tests to control the current user via request headers.
async def mock_get_current_user(request: Request):
username = request.headers.get("remote-user")
role = request.headers.get("remote-role")
if not username or not role:
from fastapi.responses import JSONResponse
return JSONResponse(
content={"message": "No authorization headers."},
status_code=401,
)
return {"username": username, "role": role}
app.dependency_overrides[get_current_user] = mock_get_current_user
return app
def setUp(self):
super().setUp([Event, ReviewSegment, Recordings])
def tearDown(self):
super().tearDown()
# ------------------------------------------------------------------
# Helpers
# ------------------------------------------------------------------
def _get_stream(
self, app, stream_name: str, role: str = "admin", user: str = "test"
):
"""Issue GET /go2rtc/streams/{stream_name} with the given role."""
with AuthTestClient(app) as client:
return client.get(
f"/go2rtc/streams/{stream_name}",
headers={"remote-user": user, "remote-role": role},
)
# ------------------------------------------------------------------
# Tests
# ------------------------------------------------------------------
def test_admin_can_access_any_stream(self):
"""Admin role bypasses camera restrictions."""
app = self._make_app(_MULTI_CAMERA_CONFIG)
# front_door stream — go2rtc is not running so expect 500, not 401/403
resp = self._get_stream(app, "front_door", role="admin")
assert resp.status_code not in (401, 403), (
f"Admin should not be blocked; got {resp.status_code}"
)
# back_door stream
resp = self._get_stream(app, "back_door", role="admin")
assert resp.status_code not in (401, 403)
def test_missing_auth_headers_returns_401(self):
"""Requests without auth headers must be rejected with 401."""
app = self._make_app(_MULTI_CAMERA_CONFIG)
# Use plain TestClient (not AuthTestClient) so no headers are injected.
with TestClient(app, raise_server_exceptions=False) as client:
resp = client.get("/go2rtc/streams/front_door")
assert resp.status_code == 401, f"Expected 401, got {resp.status_code}"
def test_unconfigured_role_can_access_any_stream(self):
"""When no camera restrictions are configured for a role the user
should have access to all streams (no roles_dict entry ⇒ no restriction)."""
no_roles_config = {
"mqtt": {"host": "mqtt"},
"cameras": {
"front_door": {
"ffmpeg": {
"inputs": [
{"path": "rtsp://10.0.0.1:554/video", "roles": ["detect"]}
]
},
"detect": {"height": 1080, "width": 1920, "fps": 5},
},
"back_door": {
"ffmpeg": {
"inputs": [
{"path": "rtsp://10.0.0.2:554/video", "roles": ["detect"]}
]
},
"detect": {"height": 1080, "width": 1920, "fps": 5},
},
},
}
app = self._make_app(no_roles_config)
# "myuser" role is not listed in roles_dict — should be allowed everywhere
for stream in ("front_door", "back_door"):
resp = self._get_stream(app, stream, role="myuser")
assert resp.status_code not in (401, 403), (
f"Unconfigured role should not be blocked on '{stream}'; "
f"got {resp.status_code}"
)
def test_restricted_role_can_access_allowed_camera(self):
"""limited_user role (restricted to front_door) can access front_door stream."""
app = self._make_app(_MULTI_CAMERA_CONFIG)
resp = self._get_stream(app, "front_door", role="limited_user")
assert resp.status_code not in (401, 403), (
f"limited_user should be allowed on front_door; got {resp.status_code}"
)
def test_restricted_role_blocked_from_disallowed_camera(self):
"""limited_user role (restricted to front_door) cannot access back_door stream."""
app = self._make_app(_MULTI_CAMERA_CONFIG)
resp = self._get_stream(app, "back_door", role="limited_user")
assert resp.status_code == 403, (
f"limited_user should be denied on back_door; got {resp.status_code}"
)
def test_stream_alias_allowed_for_owning_camera(self):
"""Stream alias 'front_door_main' is owned by front_door; limited_user (who
is allowed front_door) should be permitted."""
app = self._make_app(_MULTI_CAMERA_CONFIG)
# front_door_main is the alias defined in live.streams for front_door
resp = self._get_stream(app, "front_door_main", role="limited_user")
assert resp.status_code not in (401, 403), (
f"limited_user should be allowed on alias front_door_main; "
f"got {resp.status_code}"
)
def test_stream_alias_blocked_when_owning_camera_disallowed(self):
"""limited_user cannot access a stream alias that belongs to a camera they
are not allowed to see."""
# Give back_door a stream alias and restrict limited_user to front_door only
config = {
"mqtt": {"host": "mqtt"},
"auth": {
"roles": {
"limited_user": ["front_door"],
}
},
"cameras": {
"front_door": {
"ffmpeg": {
"inputs": [
{"path": "rtsp://10.0.0.1:554/video", "roles": ["detect"]}
]
},
"detect": {"height": 1080, "width": 1920, "fps": 5},
},
"back_door": {
"ffmpeg": {
"inputs": [
{"path": "rtsp://10.0.0.2:554/video", "roles": ["detect"]}
]
},
"detect": {"height": 1080, "width": 1920, "fps": 5},
"live": {"streams": {"default": "back_door_main"}},
},
},
}
app = self._make_app(config)
resp = self._get_stream(app, "back_door_main", role="limited_user")
assert resp.status_code == 403, (
f"limited_user should be denied on alias back_door_main; "
f"got {resp.status_code}"
)

View File

@@ -168,57 +168,6 @@ class TestHttpApp(BaseTestHttp):
assert events[0]["id"] == id
assert events[1]["id"] == id2
def test_get_event_list_match_multilingual_attribute(self):
event_id = "123456.zh"
attribute = "中文标签"
with AuthTestClient(self.app) as client:
super().insert_mock_event(event_id, data={"custom_attr": attribute})
events = client.get("/events", params={"attributes": attribute}).json()
assert len(events) == 1
assert events[0]["id"] == event_id
events = client.get(
"/events", params={"attributes": "%E4%B8%AD%E6%96%87%E6%A0%87%E7%AD%BE"}
).json()
assert len(events) == 1
assert events[0]["id"] == event_id
def test_events_search_match_multilingual_attribute(self):
event_id = "123456.zh.search"
attribute = "中文标签"
mock_embeddings = Mock()
mock_embeddings.search_thumbnail.return_value = [(event_id, 0.05)]
self.app.frigate_config.semantic_search.enabled = True
self.app.embeddings = mock_embeddings
with AuthTestClient(self.app) as client:
super().insert_mock_event(event_id, data={"custom_attr": attribute})
events = client.get(
"/events/search",
params={
"search_type": "similarity",
"event_id": event_id,
"attributes": attribute,
},
).json()
assert len(events) == 1
assert events[0]["id"] == event_id
events = client.get(
"/events/search",
params={
"search_type": "similarity",
"event_id": event_id,
"attributes": "%E4%B8%AD%E6%96%87%E6%A0%87%E7%AD%BE",
},
).json()
assert len(events) == 1
assert events[0]["id"] == event_id
def test_get_good_event(self):
id = "123456.random"

View File

@@ -31,21 +31,6 @@ class TestProxyRoleResolution(unittest.TestCase):
role = resolve_role(headers, self.proxy_config, self.config_roles)
self.assertEqual(role, "admin")
def test_role_map_or_matching(self):
config = self.proxy_config
config.header_map.role_map = {
"admin": ["group_admin", "group_privileged"],
}
# OR semantics: a single matching group should map to the role
headers = {"x-remote-role": "group_admin"}
role = resolve_role(headers, config, self.config_roles)
self.assertEqual(role, "admin")
headers = {"x-remote-role": "group_admin|group_privileged"}
role = resolve_role(headers, config, self.config_roles)
self.assertEqual(role, "admin")
def test_direct_role_header_with_separator(self):
config = self.proxy_config
config.header_map.role_map = None # disable role_map

View File

@@ -377,14 +377,7 @@ class TrackedObject:
return (thumb_update, significant_change, path_update, autotracker_update)
def to_dict(self) -> dict[str, Any]:
# Tracking internals excluded from output (centroid, estimate, estimate_velocity)
_EXCLUDED_OBJ_DATA_KEYS = {
"centroid",
"estimate",
"estimate_velocity",
}
event: dict[str, Any] = {
event = {
"id": self.obj_data["id"],
"camera": self.camera_config.name,
"frame_time": self.obj_data["frame_time"],
@@ -419,11 +412,6 @@ class TrackedObject:
"recognized_license_plate": self.obj_data.get("recognized_license_plate"),
}
# Add any other obj_data keys (e.g. custom attribute fields) not yet included
for key, value in self.obj_data.items():
if key not in _EXCLUDED_OBJ_DATA_KEYS and key not in event:
event[key] = value
return event
def is_active(self) -> bool:

View File

@@ -129,9 +129,7 @@ def get_ffmpeg_arg_list(arg: Any) -> list:
return arg if isinstance(arg, list) else shlex.split(arg)
def load_labels(
path: Optional[str], encoding="utf-8", prefill=91, indexed: bool | None = None
):
def load_labels(path: Optional[str], encoding="utf-8", prefill=91):
"""Loads labels from file (with or without index numbers).
Args:
path: path to label file.
@@ -148,12 +146,11 @@ def load_labels(
if not lines:
return {}
if indexed != False and lines[0].split(" ", maxsplit=1)[0].isdigit():
if lines[0].split(" ", maxsplit=1)[0].isdigit():
pairs = [line.split(" ", maxsplit=1) for line in lines]
labels.update({int(index): label.strip() for index, label in pairs})
else:
labels.update({index: line.strip() for index, line in enumerate(lines)})
return labels

View File

@@ -214,7 +214,6 @@ class CameraWatchdog(threading.Thread):
self.latest_valid_segment_time: float = 0
self.latest_invalid_segment_time: float = 0
self.latest_cache_segment_time: float = 0
self.record_enable_time: datetime | None = None
def _update_enabled_state(self) -> bool:
"""Fetch the latest config and update enabled state."""
@@ -262,9 +261,6 @@ class CameraWatchdog(threading.Thread):
def run(self) -> None:
if self._update_enabled_state():
self.start_all_ffmpeg()
# If recording is enabled at startup, set the grace period timer
if self.config.record.enabled:
self.record_enable_time = datetime.now().astimezone(timezone.utc)
time.sleep(self.sleeptime)
while not self.stop_event.wait(self.sleeptime):
@@ -274,15 +270,13 @@ class CameraWatchdog(threading.Thread):
self.logger.debug(f"Enabling camera {self.config.name}")
self.start_all_ffmpeg()
# reset all timestamps and record the enable time for grace period
# reset all timestamps
self.latest_valid_segment_time = 0
self.latest_invalid_segment_time = 0
self.latest_cache_segment_time = 0
self.record_enable_time = datetime.now().astimezone(timezone.utc)
else:
self.logger.debug(f"Disabling camera {self.config.name}")
self.stop_all_ffmpeg()
self.record_enable_time = None
# update camera status
self.requestor.send_data(
@@ -367,12 +361,6 @@ class CameraWatchdog(threading.Thread):
if self.config.record.enabled and "record" in p["roles"]:
now_utc = datetime.now().astimezone(timezone.utc)
# Check if we're within the grace period after enabling recording
# Grace period: 90 seconds allows time for ffmpeg to start and create first segment
in_grace_period = self.record_enable_time is not None and (
now_utc - self.record_enable_time
) < timedelta(seconds=90)
latest_cache_dt = (
datetime.fromtimestamp(
self.latest_cache_segment_time, tz=timezone.utc
@@ -398,16 +386,10 @@ 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
cache_stale = not in_grace_period and now_utc > (
latest_cache_dt + timedelta(seconds=120)
)
valid_stale = not in_grace_period and now_utc > (
latest_valid_dt + timedelta(seconds=120)
)
cache_stale = now_utc > (latest_cache_dt + timedelta(seconds=120))
valid_stale = now_utc > (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 + timedelta(seconds=120))
and self.latest_valid_segment_time
<= self.latest_invalid_segment_time

9
web/package-lock.json generated
View File

@@ -48,7 +48,7 @@
"idb-keyval": "^6.2.1",
"immer": "^10.1.1",
"konva": "^9.3.18",
"lodash": "^4.17.23",
"lodash": "^4.17.21",
"lucide-react": "^0.477.0",
"monaco-yaml": "^5.3.1",
"next-themes": "^0.3.0",
@@ -7210,10 +7210,9 @@
}
},
"node_modules/lodash": {
"version": "4.17.23",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
"integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
"license": "MIT"
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/lodash.merge": {
"version": "4.6.2",

View File

@@ -54,7 +54,7 @@
"idb-keyval": "^6.2.1",
"immer": "^10.1.1",
"konva": "^9.3.18",
"lodash": "^4.17.23",
"lodash": "^4.17.21",
"lucide-react": "^0.477.0",
"monaco-yaml": "^5.3.1",
"next-themes": "^0.3.0",

View File

@@ -1,6 +1,5 @@
{
"train": {
"titleShort": "الأخيرة"
},
"documentTitle": "تصنيف النماذج - Frigate"
}
}

View File

@@ -60,7 +60,7 @@
"cough": "Tos",
"throat_clearing": "Carraspeig",
"sneeze": "Esternut",
"sniff": "olorar",
"sniff": "Fregit nasal",
"run": "Córrer",
"shuffle": "Passos arrossegats",
"footsteps": "Passos",
@@ -97,7 +97,7 @@
"moo": "Mugir",
"cowbell": "Esquellot",
"pig": "Porc",
"oink": "Oinc",
"oink": "Oink",
"bleat": "Brama",
"fowl": "Au de corral",
"chicken": "Pollastre",
@@ -439,37 +439,37 @@
"inside": "Interior",
"pulse": "Pols",
"outside": "Fora",
"chirp_tone": "Gisclada",
"chirp_tone": "To de grinyol",
"harmonic": "Harmònic",
"sine_wave": "Ona sinus",
"crunch": "Cruixit",
"hum": "Zunzum",
"plop": "Xip-xap",
"hum": "Taral·lejar",
"plop": "Chof",
"clickety_clack": "Clic-Clac",
"clicking": "Clicant",
"clatter": "Rebombori",
"clatter": "Soroll",
"chird": "Piular",
"liquid": "Líquid",
"splash": "Esquitx",
"slosh": "Xipolleig",
"boing": "Rebot",
"zing": "Zunzum agut",
"rumble": "Retombori",
"sizzle": "Crepitació",
"splash": "Xof",
"slosh": "Xip-xap",
"boing": "Boing",
"zing": "Fiu",
"rumble": "Bum-bum",
"sizzle": "Xiu-xiu",
"whir": "Brrrm",
"rustle": "Frec",
"creak": "Rascada",
"clang": "Soroll metàl·lic",
"rustle": "Fru-Fru",
"creak": "Clic-clac",
"clang": "Clang",
"squish": "Xaf",
"drip": "Goteig",
"pour": "Abocament",
"trickle": "Raig fi",
"gush": "Raig fort",
"fill": "Ompliment",
"ding": "Ting",
"ping": "Ressò",
"beep": "Pitit",
"squeal": "Chirrit",
"drip": "Plic-plic",
"pour": "Glug-glug",
"trickle": "Xiulet",
"gush": "Xuuuix",
"fill": "Glug-glug",
"ding": "Ding",
"ping": "Ping",
"beep": "Bip",
"squeal": "Xiscle",
"crumpling": "Arrugant-se",
"rub": "Fregar",
"scrape": "Raspar",
@@ -480,13 +480,13 @@
"smash": "Aixafar",
"whack": "Cop",
"slap": "Bufetada",
"bang": "Cop fort",
"bang": "Bang",
"basketball_bounce": "Rebot de bàsquet",
"chorus_effect": "Efecte de cor",
"effects_unit": "Unitat d'Efectes",
"electronic_tuner": "Afinador electrònic",
"thunk": "Bruix",
"thump": "Soroll sord",
"thump": "Cop fort",
"whoosh": "Xiuxiueig",
"arrow": "Fletxa",
"sonar": "Sonar",

View File

@@ -48,8 +48,7 @@
"bg": "Български (Búlgar)",
"gl": "Galego (Gallec)",
"id": "Bahasa Indonesia (Indonesi)",
"ur": "اردو (Urdú)",
"hr": "Hrvatski (croat)"
"ur": "اردو (Urdú)"
},
"system": "Sistema",
"systemMetrics": "Mètriques del sistema",
@@ -202,8 +201,7 @@
},
"inProgress": "En curs",
"invalidStartTime": "Hora d'inici no vàlida",
"invalidEndTime": "Hora de finalització no vàlida",
"never": "Mai"
"invalidEndTime": "Hora de finalització no vàlida"
},
"unit": {
"speed": {

View File

@@ -6,8 +6,7 @@
"title": "Frigate s'està reiniciant",
"content": "Aquesta pàgina es tornarà a carregar d'aquí a {{countdown}} segons.",
"button": "Forçar la recàrrega ara"
},
"description": "Això aturarà breument Frigate mentre es reinicia."
}
},
"explore": {
"plus": {

View File

@@ -19,8 +19,7 @@
"description": {
"addFace": "Afegiu una col·lecció nova a la biblioteca de cares pujant la vostra primera imatge.",
"placeholder": "Introduïu un nom per a aquesta col·lecció",
"invalidName": "Nom no vàlid. Els noms només poden incloure lletres, números, espais, apòstrofs, guions baixos i guions.",
"nameCannotContainHash": "El nom no pot contenir #."
"invalidName": "Nom no vàlid. Els noms només poden incloure lletres, números, espais, apòstrofs, guions baixos i guions."
},
"documentTitle": "Biblioteca de rostres - Frigate",
"uploadFaceImage": {

View File

@@ -184,16 +184,6 @@
"restricted": {
"title": "No hi ha càmeres disponibles",
"description": "No teniu permís per veure cap càmera en aquest grup."
},
"default": {
"title": "No s'ha configurat cap càmera",
"description": "Comenceu connectant una càmera a Frigate.",
"buttonText": "Afegeix una càmera"
},
"group": {
"title": "No hi ha càmeres al grup",
"description": "Aquest grup de càmeres no té càmeres assignades o habilitades.",
"buttonText": "Gestiona els grups"
}
}
}

View File

@@ -114,11 +114,6 @@
},
"error": {
"mustBeFinished": "El dibuix del polígon s'ha d'acabar abans de desar."
},
"type": {
"zone": "zona",
"motion_mask": "màscara de moviment",
"object_mask": "màscara d'objecte"
}
},
"zoneName": {
@@ -537,7 +532,7 @@
"hide": "Amaga contrasenya",
"requirements": {
"title": "Requisits contrasenya:",
"length": "Com a mínim 12 carácters",
"length": "Com a mínim 8 carácters",
"uppercase": "Com a mínim una majúscula",
"digit": "Com a mínim un digit",
"special": "Com a mínim un carácter especial (!@#$%^&*(),.?\":{}|<>)"
@@ -959,7 +954,7 @@
"useDigestAuthDescription": "Usa l'autenticació de resum HTTP per a ONVIF. Algunes càmeres poden requerir un nom d'usuari/contrasenya ONVIF dedicat en lloc de l'usuari administrador estàndard."
},
"save": {
"failure": "S'ha produït un error en desar {{cameraName}}.",
"failure": "SS'ha produït un error en desar {{cameraName}}.",
"success": "S'ha desat correctament la càmera nova {{cameraName}}."
},
"testResultLabels": {
@@ -1216,11 +1211,11 @@
"cameraReview": {
"object_descriptions": {
"title": "Descripcions d'objectes generadors d'IA",
"desc": "Activa/desactiva temporalment les descripcions d'objectes generatius d'IA per a aquesta càmera fins que es reiniciï Frigate. Quan està desactivat, les descripcions generades per IA no se sol·licitaran per als objectes rastrejats en aquesta càmera."
"desc": "Activa/desactiva temporalment les descripcions d'objectes generatius d'IA per a aquesta càmera. Quan està desactivat, les descripcions generades per IA no se sol·licitaran per als objectes rastrejats en aquesta càmera."
},
"review_descriptions": {
"title": "Descripcions de la IA generativa",
"desc": "Activa/desactiva temporalment les descripcions de la IA Generativa per a aquesta càmera fins que es reiniciï Frigate. Quan està desactivat, les descripcions generades per IA no se sol·licitaran per als elements de revisió d'aquesta càmera."
"desc": "Activa/desactiva temporalment les descripcions de revisió de la IA generativa per a aquesta càmera. Quan està desactivat, les descripcions generades per IA no se sol·licitaran per als elements de revisió d'aquesta càmera."
},
"review": {
"title": "Revisió",

View File

@@ -78,11 +78,7 @@
"formattedTimestampFilename": {
"24hour": "dd-MM-yy-HH-mm-ss",
"12hour": "dd-MM.yy-h-mm-ss-a"
},
"never": "Nikdy",
"inProgress": "Zpracovává se",
"invalidStartTime": "Neplatný čas začátku",
"invalidEndTime": "Neplatný čas konce"
}
},
"button": {
"twoWayTalk": "Obousměrná komunikace",
@@ -119,17 +115,10 @@
"unselect": "Zrušit výběr",
"deleteNow": "Smazat hned",
"next": "Další",
"export": "Exportovat",
"continue": "Pokračovat"
"export": "Exportovat"
},
"label": {
"back": "Jdi zpět",
"hide": "Skrýt {{item}}",
"show": "Zobrazit {{item}}",
"ID": "ID",
"none": "Nic",
"all": "Vše",
"other": "Ostatní"
"back": "Jdi zpět"
},
"unit": {
"speed": {
@@ -139,14 +128,6 @@
"length": {
"feet": "stopa",
"meters": "metry"
},
"data": {
"kbps": "kB/s",
"mbps": "MB/s",
"gbps": "GB/s",
"kbph": "kB/hodinu",
"mbph": "MB/hodinu",
"gbph": "GB/hodinu"
}
},
"selectItem": "Vybrat {{item}}",
@@ -249,8 +230,7 @@
"uiPlayground": "UI hřiště",
"faceLibrary": "Knihovna Obličejů",
"configurationEditor": "Editor Konfigurace",
"withSystem": "Systém",
"classification": "Klasifikace"
"withSystem": "Systém"
},
"pagination": {
"previous": {
@@ -290,17 +270,5 @@
"viewer": "Divák",
"desc": "Správci mají plný přístup ke všem funkcím v uživatelském rozhraní Frigate. Diváci jsou omezeni na sledování kamer, položek přehledu a historických záznamů v UI."
},
"readTheDocumentation": "Přečtěte si dokumentaci",
"list": {
"two": "{{0}} a {{1}}",
"many": "{{items}}, a {{last}}",
"separatorWithSpace": ", "
},
"field": {
"optional": "Volitelné",
"internalID": "Interní ID Frigate používá v konfiguraci a databázi"
},
"information": {
"pixels": "{{area}}px"
}
"readTheDocumentation": "Přečtěte si dokumentaci"
}

View File

@@ -44,8 +44,7 @@
"button": {
"markAsReviewed": "Označit jako zkontrolované",
"deleteNow": "Smazat hned",
"export": "Exportovat",
"markAsUnreviewed": "Označit jako nezkontrolované"
"export": "Exportovat"
}
},
"export": {
@@ -68,13 +67,12 @@
"export": "Exportovat",
"selectOrExport": "Vybrat pro Export",
"toast": {
"success": "Export úspěšně spuštěn. Soubor najdete na stránce exportů.",
"success": "Export úspěšně spuštěn. Soubor najdete v adresáři /exports.",
"error": {
"failed": "Chyba spuštění exportu: {{error}}",
"endTimeMustAfterStartTime": "Čas konce musí být po čase začátku",
"noVaildTimeSelected": "Není vybráno žádné platné časové období"
},
"view": "Zobrazení"
}
},
"fromTimeline": {
"saveExport": "Uložit export",
@@ -118,7 +116,6 @@
"search": {
"placeholder": "Hledej pomocí štítku nebo podštítku..."
},
"noImages": "Nebyly nalezeny žádné náhledy pro tuto kameru",
"unknownLabel": "Uložený obrázek Spouštěče"
"noImages": "Nebyly nalezeny žádné náhledy pro tuto kameru"
}
}

View File

@@ -132,9 +132,5 @@
},
"count_one": "Třída {{count}}",
"count_other": "Třídy {{count}}"
},
"attributes": {
"label": "Atributy Klasifikace",
"all": "Všechny Atributy"
}
}

View File

@@ -38,65 +38,10 @@
"deleteImageFailed": "Chyba při mazání: {{errorMessage}}",
"deleteCategoryFailed": "Chyba při mazání třídy: {{errorMessage}}",
"deleteModelFailed": "Chyba při mazání modelu: {{errorMessage}}",
"categorizeFailed": "Chyba při mazání obrázku: {{errorMessage}}",
"trainingFailed": "Trénování modelu selhalo. Zkontrolujte logy Frigate pro zjištění detailů.",
"trainingFailedToStart": "Chyba spuštění trénování modelu: {{errorMessage}}",
"updateModelFailed": "Chyba aktualizace modelu: {{errorMessage}}",
"renameCategoryFailed": "Chyba přejmenování třídy: {{errorMessage}}"
"categorizeFailed": "Chyba při mazání obrázku: {{errorMessage}}"
}
},
"train": {
"titleShort": "Nedávný",
"title": "Předchozí klasifikace",
"aria": "Vybrat předchozí Klasifikace"
},
"deleteModel": {
"desc_one": "Jste si jistí, že chcete odstranit {{count}} model? Tím trvale odstraníte všechny související data včetně obrázků a tréninkových dat. Tato akce je nevratná.",
"desc_few": "Jste si jistí, že chcete odstranit {{count}} modely? Tím trvale odstraníte všechny související data včetně obrázků a tréninkových dat. Tato akce je nevratná.",
"desc_other": "Jste si jistí, že chcete odstranit {{count}} modelů? Tím trvale odstraníte všechny související data včetně obrázků a tréninkových dat. Tato akce je nevratná."
},
"deleteDatasetImages": {
"desc_one": "Opravdu chcete odstranit {{count}} obrázek z {{dataset}}? Tato akce je nevratná a vyžaduje přetrénování modelu.",
"desc_few": "Opravdu chcete odstranit {{count}} obrázky z {{dataset}}? Tato akce je nevratná a vyžaduje přetrénování modelu.",
"desc_other": "Opravdu chcete odstranit {{count}} obrázků z {{dataset}}? Tato akce je nevratná a vyžaduje přetrénování modelu.",
"title": "Smazat obrázky datové sady"
},
"deleteTrainImages": {
"desc_one": "Opravdu chcete odstranit {{count}} obrázek? Tato akce je nevratná.",
"desc_few": "Opravdu chcete odstranit {{count}} obrázky? Tato akce je nevratná.",
"desc_other": "Opravdu chcete odstranit {{count}} obrázků? Tato akce je nevratná.",
"title": "Odstranit tréninkové obrázky"
},
"wizard": {
"step3": {
"allImagesRequired_one": "Prosím, zařaďte všechny obrázky. Zbývá {{count}} obrázek.",
"allImagesRequired_few": "Prosím, zařaďte všechny obrázky. Zbývají {{count}} obrázky.",
"allImagesRequired_other": "Prosím, zařaďte všechny obrázky. Zbývá {{count}} obrázků.",
"trainingStarted": "Trénování úspěšně spuštěno",
"generateSuccess": "Vzorové obrázky byly úspěšně vytvořeny"
}
},
"deleteCategory": {
"title": "Smazat Třídu",
"desc": "Opravdu chcete odstranit třídu {{name}}? Tím se na trvalo odstraní všechny související obrázky a bude potřeba přetrénovat model.",
"minClassesTitle": "Nemůžete smazat třídu",
"minClassesDesc": "Klasifikační model musí mít alespoň 2 třídy. Než tuto třídu odstraníte přidejte další třídu."
},
"edit": {
"descriptionObject": "Upravte typ objektu a typ klasifikace pro tento model klasifikace.",
"stateClassesInfo": "Poznámka: Změna tříd stavů vyžaduje přetrénování modelu s aktualizovanými třídami."
},
"renameCategory": {
"title": "Přejmenovat třídu",
"desc": "Vložte nové jméno pro {{name}}. Aby se změna názvu projevila, bude nutné model znovu natrénovat."
},
"description": {
"invalidName": "Neplatné jméno. Jméno můžou obsahovat pouze písmena, čísla, mezery, apostrofy, podtržítka a spojovníky."
},
"categories": "Třídy",
"createCategory": {
"new": "Vytvořit novou Třídu"
},
"categorizeImageAs": "Klasifikovat obrázek jako:",
"categorizeImage": "Klasifikovat obrázek"
"titleShort": "Nedávný"
}
}

View File

@@ -9,18 +9,14 @@
"empty": {
"alert": "Nejsou žádné výstrahy na kontrolu",
"detection": "Nejsou žádné detekce na kontrolu",
"motion": "Nenalezena žádná data o pohybu",
"recordingsDisabled": {
"title": "Nahrávání musí být povoleno",
"description": "Položky revize lze pro kameru vytvořit pouze tehdy, je-li pro ni povoleno nahrávání."
}
"motion": "Nenalezena žádná data o pohybu"
},
"timeline": "Časová osa",
"timeline.aria": "Zvolit časovou osu",
"events": {
"label": "Události",
"aria": "Zvolit události",
"noFoundForTimePeriod": "Pro toto časové období nebyly nalezeny žádné události."
"noFoundForTimePeriod": "Pro toto období nebyly nalezeny žádné události."
},
"documentTitle": "Revize - Frigate",
"camera": "Kamera",
@@ -30,8 +26,8 @@
"markAsReviewed": "Označit jako zkontrolované",
"markTheseItemsAsReviewed": "Označit tyto položky jako zkontrolované",
"newReviewItems": {
"label": "Zobrazit nové položky revize",
"button": "Nové položky revize"
"label": "Zobrazit nové položky na kontrolu",
"button": "Nové položky na kontrolu"
},
"recordings": {
"documentTitle": "Záznamy - Frigate"
@@ -46,22 +42,8 @@
"detail": {
"label": "Detail",
"noDataFound": "Žádná detailní data k prohlédnutí",
"aria": "Přepnout zobrazení detailů",
"aria": "Přepnout detailní zobrazení",
"trackedObject_other": "{{count}} objektů",
"trackedObject_one": "{{count}} objekt",
"noObjectDetailData": "Nejsou k dispozici žádné podrobné údaje o objektu.",
"settings": "Nastavení Detailního Zobrazení",
"alwaysExpandActive": {
"title": "Vždy rozbalit aktivní",
"desc": "Vždy zobrazit podrobnosti objektu aktivní položky revize, pokud jsou k dispozici."
}
},
"objectTrack": {
"trackedPoint": "Sledovaný bod",
"clickToSeek": "Kliknutím přeskočte na tento čas"
},
"select_all": "Vše",
"normalActivity": "Normální",
"needsReview": "Potřebuje revizi",
"securityConcern": "Obava o bezpečnost"
"trackedObject_one": "{{count}} objektů"
}
}

View File

@@ -24,8 +24,7 @@
"regenerate": "Od {{provider}} byl vyžádán nový popis. V závislosti na rychlosti vašeho poskytovatele může obnovení nového popisu nějakou dobu trvat.",
"updatedSublabel": "Úspěšně aktualizovaný podružný štítek.",
"updatedLPR": "Úspěšně aktualizovaná SPZ.",
"audioTranscription": "Požádání o přepis zvuku bylo úspěšné. V závislosti na rychlosti Vašeho Frigate serveru může přepis trvat nějaký čas než bude dokončen.",
"updatedAttributes": "Atributy byly úspěšně aktualizovány."
"audioTranscription": "Požádání o přepis zvuku bylo úspěšné."
},
"error": {
"regenerate": "Chyba volání {{provider}} pro nový popis: {{errorMessage}}",
@@ -207,7 +206,7 @@
"dialog": {
"confirmDelete": {
"title": "Potvrdit smazání",
"desc": "Odstraněním tohoto sledovaného objektu se odstraní snímek, všechna uložená vložení a všechny související položky s podrobnostmi o sledování. Zaznamenaný záznam tohoto sledovaného objektu v zobrazení Historie <em>NEBUDE</em> smazán.<br /><br />Opravdu chcete pokračovat?"
"desc": "Odstraněním tohoto sledovaného objektu se odstraní snímek, všechna uložená vložení a všechny související položky životního cyklu objektu. Zaznamenaný záznam tohoto sledovaného objektu v zobrazení Historie <em>NEBUDE</em> smazán.<br /><br />Opravdu chcete pokračovat?"
}
},
"trackedObjectDetails": "Detaily sledovaných objektů",
@@ -215,9 +214,7 @@
"details": "detaily",
"snapshot": "snímek",
"video": "video",
"object_lifecycle": "životní cyklus objektu",
"thumbnail": "Náhled",
"tracking_details": "detaily sledování"
"object_lifecycle": "životní cyklus objektu"
},
"noTrackedObjects": "Žádné sledované objekty nebyly nalezeny",
"fetchingTrackedObjectsFailed": "Chyba při načítání sledovaných objektů: {{errorMessage}}",
@@ -227,49 +224,5 @@
},
"concerns": {
"label": "Obavy"
},
"trackingDetails": {
"title": "Detaily Sledování",
"noImageFound": "Nebyl nalezen obrázek pro tuto časovou značku.",
"createObjectMask": "Vytvořit Masku Objektu",
"adjustAnnotationSettings": "Upravte nastavení poznámek",
"scrollViewTips": "Klikněte pro zobrazení významných okamžiků z životního cyklu tohoto objektu.",
"autoTrackingTips": "Pozice ohraničujících rámečků budou nepřesné pro kamery s automatickým sledováním.",
"count": "{{first}} z {{second}}",
"trackedPoint": "Sledovaný Bod",
"lifecycleItemDesc": {
"visible": "Detekován {{label}}",
"entered_zone": "{{label}} vstoupil do {{zones}}",
"active": "{{label}} se stal aktivním",
"stationary": "{{label}} se zastavil",
"attribute": {
"faceOrLicense_plate": "Pro {{label}} zjištěn {{attribute}}"
},
"header": {
"ratio": "Poměr",
"area": "Oblast",
"score": "Skóre"
}
},
"annotationSettings": {
"title": "Nastavení anotací",
"showAllZones": {
"title": "Zobrazit všechny zóny",
"desc": "Vždy zobrazovat zóny na snímcích, na kterých objekty vstoupili do zóny."
},
"offset": {
"label": "Odsazení anotace",
"desc": "Tato data pocházejí z detekčního kanálu vaší kamery, ale překrývají se s obrázky ze záznamového kanálu. Je nepravděpodobné, že by oba streamy byly dokonale synchronizované. V důsledku toho se ohraničovací rámeček a záznam nebudou dokonale srovnávat. Toto nastavení můžete použít k časovému posunutí anotací dopředu nebo dozadu, abyste je lépe zarovnali se zaznamenaným záznamem.",
"millisecondsToOffset": "Milisekundy na posunutí detekce anotací. <em>Výchozí: 0</em>",
"tips": "Snižte hodnotu, pokud je přehrávané video před ohraničením a body cesty, nebo zvyšte hodnotu, pokud je přehrávané video za nimi. Hodnota může být i záporná.",
"toast": {
"success": "Odsazení anotací pro {{camera}} bylo uloženo do konfiguračního souboru."
}
}
},
"carousel": {
"previous": "Předcházející snímek",
"next": "Další snímek"
}
}
}

View File

@@ -1,6 +1,6 @@
{
"imageEntry": {
"dropInstructions": "Přetáhněte obrázek sem, nebo klikněte na výběr",
"dropInstructions": "Přetáhněte obrázek zde, nebo klikněte na výběr",
"maxSize": "Maximální velikost: {{size}}MB",
"dropActive": "Přetáhněte obrázek zde…",
"validation": {
@@ -10,7 +10,7 @@
"createFaceLibrary": {
"new": "Vytvořit nový obličej",
"desc": "Vytvořit novou kolekci",
"nextSteps": "Chcete-li vybudovat pevný základ:<li>Použijte kartu Nedávná Rozpoznání k výběru a trénování na snímcích pro každou detekovanou osobu.</li><li>Pro nejlepší výsledky se zaměřte na přímé snímky; vyhněte se trénování snímků, které zachycují obličeje pod úhlem.</li></ul>",
"nextSteps": "Chcete-li vybudovat pevný základ:<li>Použijte kartu Trénování k výběru a trénování na snímcích pro každou detekovanou osobu.</li><li>Pro nejlepší výsledky se zaměřte na přímé snímky; vyhněte se trénování snímků, které zachycují obličeje pod úhlem.</li></ul>",
"title": "Vytvořit kolekci"
},
"details": {
@@ -44,7 +44,7 @@
"description": {
"addFace": "Přidejte novou kolekci do Knihovny obličejů nahráním prvního obrázku.",
"placeholder": "Zadejte název pro tuto kolekci",
"invalidName": "Neplatné jméno. Jméno můžou obsahovat pouze písmena, čísla, mezery, apostrofy, podtržítka a spojovníky."
"invalidName": "Neplatný název. Názvy mohou obsahovat pouze písmena, čísla, mezery, apostrofy, podtržítka a pomlčky."
},
"documentTitle": "Knihovna obličejů - Frigate",
"uploadFaceImage": {

View File

@@ -86,7 +86,7 @@
"enable": "Ukázat statistiky streamu"
},
"manualRecording": {
"title": "Na požádání",
"title": "Nahrávání na vyžádání",
"playInBackground": {
"label": "Přehrát na pozadí",
"desc": "Povolte tuto volbu pro pokračování streamování i když je přehrávač skrytý."
@@ -103,7 +103,7 @@
"started": "Manuálně spuštěno nahrávání na požádání.",
"ended": "Ukončeno manuální nahrávání na vyžádání.",
"recordDisabledTips": "Protože je v konfiguraci této kamery nahrávání zakázáno nebo omezeno, bude uložen pouze snímek.",
"tips": "Stáhněte si aktuální snímek nebo spusťte ruční událost na základě nastavení uchování záznamu této kamery."
"tips": "Spustit ruční událost na základě nastavení uchování záznamů této kamery."
},
"streamingSettings": "Nastavení Streamování",
"audio": "Zvuk",
@@ -167,11 +167,5 @@
"transcription": {
"enable": "Povolit živý přepis zvuku",
"disable": "Zakázat živý přepis zvuku"
},
"snapshot": {
"takeSnapshot": "Stáhnout aktuální snímek",
"noVideoSource": "Pro snímek není k dispozici žádné video.",
"captureFailed": "Zachycení snímku selhalo.",
"downloadStarted": "Stažení snímku spuštěno."
}
}

View File

@@ -134,7 +134,7 @@
"name": {
"inputPlaceHolder": "Zadejte jméno…",
"title": "Jméno",
"tips": "Název musí mít alespoň 2 znaky, musí obsahovat alespoň jedno písmeno a nesmí být shodný s názvem kamery nebo jiné zóny této kamery."
"tips": "Název musí mít alespoň 2 znaky a nesmí být shodný s názvem kamery nebo jiné zóny."
},
"inertia": {
"title": "Setrvačnost",
@@ -160,7 +160,7 @@
}
},
"toast": {
"success": "Zóna {{zoneName}} byla uložena."
"success": "Zóna {{zoneName}} byla uložena. Restartujte Frigate pro aplikování změn."
},
"label": "Zóny",
"desc": {
@@ -199,8 +199,8 @@
"clickDrawPolygon": "Kliknutím nakreslíte polygon do obrázku.",
"toast": {
"success": {
"title": "{{polygonName}} byl uložen.",
"noName": "Maska Detekce pohybu byla uložena."
"title": "{{polygonName}} byl uložen. Restartujte Frigate pro aplikování změn.",
"noName": "Maska Detekce pohybu byla uložena. Restartujte Frigate pro aplikování změn."
}
}
},
@@ -284,8 +284,8 @@
"clickDrawPolygon": "Kliknutím nakreslete polygon do obrázku.",
"toast": {
"success": {
"title": "{{polygonName}} byl uložen.",
"noName": "Maska Objektu byla uložena."
"title": "{{polygonName}} byl uložen. Restartujte Frigate pro aplikování změn.",
"noName": "Maska Objektu byla uložena. Restartujte Frigate pro aplikování změn."
}
},
"point_one": "{{count}} bod",
@@ -322,7 +322,7 @@
"noCamera": "Žádná Kamera"
},
"general": {
"title": "Nastaverozhraní",
"title": "Hlavní nastavení",
"liveDashboard": {
"title": "Živý dashboard",
"automaticLiveView": {
@@ -332,13 +332,6 @@
"playAlertVideos": {
"label": "Přehrát videa s výstrahou",
"desc": "Ve výchozím nastavení se nedávná upozornění na ovládacím panelu Živě přehrávají jako malá opakující se videa. Vypněte tuto možnost, chcete-li na tomto zařízení/prohlížeči zobrazovat pouze statický obrázek nedávných výstrah."
},
"displayCameraNames": {
"label": "Vždy zobrazit názvy kamer",
"desc": "Vždy zobrazit názvy kamer v čipu na ovládacím panelu živého náhledu s více kamerami."
},
"liveFallbackTimeout": {
"label": "Časový limit pádu živého přehrávání"
}
},
"storedLayouts": {
@@ -636,11 +629,11 @@
"actions": "Akce",
"noUsers": "Žádní uživatelé nebyli nalezeni.",
"changeRole": "Změnit roli uživatele",
"password": "Resetovat Heslo",
"password": "Heslo",
"deleteUser": "Smazat uživatele",
"role": "Role"
},
"updatePassword": "Resetovat heslo",
"updatePassword": "Aktualizovat heslo",
"toast": {
"success": {
"createUser": "Uživatel {{user}} úspěšně vytvořen",
@@ -750,7 +743,7 @@
"triggers": {
"documentTitle": "Spouštěče",
"management": {
"title": "Spouštěče",
"title": "Správa spouštěčů",
"desc": "Spravovat spouštěče pro {{camera}}. Použít typ miniatury ke spuštění u miniatur podobných vybranému sledovanému objektu a typ popisu ke spuštění u popisů podobných zadanému textu."
},
"addTrigger": "Přidat spouštěč",
@@ -789,10 +782,10 @@
"form": {
"name": {
"title": "Název",
"placeholder": "Pojmenujte tento spouštěč",
"placeholder": "Zadejte název spouštěče",
"error": {
"minLength": "Pole musí mít alespoň 2 znaky.",
"invalidCharacters": "Pole může obsahovat pouze písmena, číslice, podtržítka a pomlčky.",
"minLength": "Název musí mít alespoň 2 znaky.",
"invalidCharacters": "Jméno může obsahovat pouze písmena, číslice, podtržítka a pomlčky.",
"alreadyExists": "Spouštěč s tímto názvem již pro tuto kameru existuje."
}
},
@@ -805,9 +798,9 @@
},
"content": {
"title": "Obsah",
"imagePlaceholder": "Vyberte miniaturu",
"imagePlaceholder": "Vybrat obrázek",
"textPlaceholder": "Zadat textový obsah",
"imageDesc": "Je zobrazeno pouze posledních 100 miniatur. Pokud nemůžete najít požadovanou miniaturu, prosím zkontrolujte dřívější objekty v Prozkoumat a nastavte spouštěč ze tamějšího menu.",
"imageDesc": "Vybrat obrázek, který spustí tuto akci, když bude detekován podobný obrázek.",
"textDesc": "Zadejte text, který spustí tuto akci, když bude zjištěn podobný popis sledovaného objektu.",
"error": {
"required": "Obsah je povinný."
@@ -815,7 +808,7 @@
},
"actions": {
"title": "Akce",
"desc": "Ve výchozím nastavení Frigate odesílá MQTT zprávu pro všechny spouštěče. Podřazené popisky přidávají název spouštěče k popisku objektu. Atributy jsou prohledávatelná metadata uložená samostatně v metadatech sledovaného objektu.",
"desc": "Ve výchozím nastavení Frigate odesílá MQTT zprávu pro všechny spouštěče. Zvolte dodatečnou akci, která se má provést, když se tento spouštěč aktivuje.",
"error": {
"min": "Musí být vybrána alespoň jedna akce."
}
@@ -857,9 +850,9 @@
"createRole": "Role {{role}} byla úspěšně vytvořena",
"updateCameras": "Kamery byly aktualizovány pro roli {{role}}",
"deleteRole": "Role {{role}} byla úspěšně smazána",
"userRolesUpdated_one": "{{count}} uživatel přiřazený k této roli byl aktualizován na „diváka“, který má přístup ke všem kamerám.",
"userRolesUpdated_few": "{{count}} uživatelé přiřazení k této roli bylo aktualizováno na „diváky“, kteří mají přístup ke všem kamerám.",
"userRolesUpdated_other": "{{count}} uživatelů přiřazených k této roli bylo aktualizováno na „diváky“, kteří mají přístup ke všem kamerám."
"userRolesUpdated_one": "{{count}} uživatel(ů) přiřazených k této roli bylo aktualizováno na „Divák“, který má přístup ke všem kamerám.",
"userRolesUpdated_few": "",
"userRolesUpdated_other": ""
},
"error": {
"createRoleFailed": "Nepodařilo se vytvořit roli: {{errorMessage}}",
@@ -903,36 +896,5 @@
"title": "Správa role diváka",
"desc": "Spravujte vlastní role diváků a jejich oprávnění k přístupu ke kamerám pro tuto instanci Frigate."
}
},
"cameraWizard": {
"save": {
"success": "Nová kamera {{cameraName}} úspěšně uložena."
},
"step2": {
"testSuccess": "Test připojení v pořádku!",
"probeSuccessful": "Sonda úspěšná",
"probeNoSuccess": "Sonda neúspěšná"
},
"step3": {
"testSuccess": "Test streamu v pořádku!"
},
"step4": {
"reconnectionSuccess": "Opakované připojení úspěšné.",
"streamValidated": "Stream {{number}} úspěšně ověřený"
}
},
"cameraManagement": {
"cameraConfig": {
"toast": {
"success": "Kamera {{cameraName}} úspěšně uložena"
}
}
},
"cameraReview": {
"reviewClassification": {
"toast": {
"success": "Konfigurace Klasifikací Revizí byla uložena. Restartujte Frigate pro aplikování změn."
}
}
}
}

View File

@@ -112,23 +112,12 @@
"gpuUsage": "Využití CPU",
"gpuMemory": "Paměť GPU",
"gpuEncoder": "GPU kodér",
"gpuDecoder": "GPU Dekodér",
"intelGpuWarning": {
"title": "Upozornění Intel GPU Stats",
"message": "Statistiky GPU nedostupné",
"description": "Toto je známá chyba v nástrojích Intel pro hlášení statistik GPU (intel_gpu_top), která selhává a opakovaně vrací využití GPU 0 %, a to i v případech, kdy na (i)GPU správně běží hardwarová akcelerace a detekce objektů. Nejedná se o chybu Frigate. Můžete restartovat hostitele, abyste problém dočasně vyřešili a potvrdili, že GPU funguje správně. Toto neovlivňuje výkon."
}
"gpuDecoder": "GPU Dekodér"
},
"otherProcesses": {
"title": "Ostatní procesy",
"processCpuUsage": "Využití CPU procesy",
"processMemoryUsage": "Využití paměti procesy",
"series": {
"go2rtc": "go2rtc",
"recording": "nahrávání",
"review_segment": "revidovat segment",
"embeddings": "vložení"
}
"processMemoryUsage": "Využití paměti procesy"
},
"title": "Hlavní"
},

View File

@@ -27,7 +27,7 @@
"harp": "Harpe",
"bell": "Klokke",
"harmonica": "Harmonika",
"bagpipes": "Sækkepiber",
"bagpipes": "Sækkepibe",
"didgeridoo": "Didgeridoo",
"jazz": "Jazz",
"opera": "Opera",
@@ -78,122 +78,11 @@
"camera": "Kamera",
"tools": "Værktøj",
"hammer": "Hammer",
"drill": "Boremaskine",
"drill": "Bore",
"explosion": "Eksplosion",
"fireworks": "Nytårskrudt",
"babbling": "Pludren",
"yell": "Råb",
"whoop": "Jubel",
"snicker": "Smålatter",
"bird": "Fugl",
"cat": "Kat",
"dog": "Hund",
"horse": "Hest",
"sheep": "Får",
"mouse": "Mus",
"keyboard": "Tastatur",
"blender": "Mixer",
"hair_dryer": "Føntørrer",
"animal": "Dyr",
"bark": "Gø",
"goat": "Gæd",
"sigh": "Suk",
"singing": "Synger",
"choir": "Kor",
"yodeling": "Jodlen",
"chant": "Messe",
"mantra": "Meditationsmantra",
"child_singing": "Barn Synger",
"synthetic_singing": "Syntetisk Sang",
"rapping": "Rapper",
"humming": "Nynner",
"groan": "Støn",
"grunt": "Grynt",
"whistling": "Fløjter",
"breathing": "Vejrtrækning",
"wheeze": "Hæsende vejrtrækning",
"snoring": "Snorker",
"gasp": "Gisp",
"pant": "Anstrengende vejrtrækning",
"snort": "Fnyse",
"cough": "Hoster",
"throat_clearing": "Rømmer sig",
"sneeze": "Nyser",
"sniff": "Snøfter",
"run": "Løb",
"shuffle": "Trække fødderne",
"footsteps": "Fodtrin",
"chewing": "Tygger",
"biting": "Bider",
"gargling": "Gurgler",
"stomach_rumble": "Maverumlen",
"burping": "Bøvser",
"hiccup": "Hikke",
"fart": "Prut",
"hands": "Hænder",
"finger_snapping": "Knipse fingere",
"clapping": "Klapper",
"heartbeat": "Hjertebanken",
"heart_murmur": "Hjertemislyd",
"cheering": "Hujen",
"applause": "Bifald",
"chatter": "Snak",
"crowd": "Forsamling",
"children_playing": "Børn leger",
"pets": "Kæledyr",
"yip": "Jubel",
"howl": "Hyl",
"bow_wow": "Vov vov",
"growling": "Knurren",
"whimper_dog": "Hunde­klynk",
"purr": "Spinde",
"meow": "Meaw",
"hiss": "Hvæser",
"caterwaul": "Kattejammer",
"livestock": "Husdyr",
"oink": "Nøf",
"bleat": "Brægen",
"vibration": "Vibration",
"fowl": "Fjerkræ",
"chicken": "Kylling",
"cluck": "Kagle",
"cock_a_doodle_doo": "Kykeliky",
"turkey": "Kalkun",
"gobble": "Kalkunlyd",
"duck": "And",
"quack": "Rap",
"goose": "Gås",
"honk": "Dyt",
"wild_animals": "Vilde dyr",
"roaring_cats": "Brølende katte",
"roar": "Brøl",
"chirp": "Pip",
"squawk": "Skræppen",
"pigeon": "Due",
"coo": "Kurre",
"crow": "Krage",
"caw": "Kragelyd",
"owl": "Ugle",
"hoot": "Uglehyl",
"flapping_wings": "Vingeslag",
"dogs": "Hunde",
"rats": "Rotter",
"patter": "Dråbelyd",
"insect": "Insekt",
"cricket": "Cricket",
"guitar": "Guitar",
"electric_guitar": "Elektrisk Guitar",
"bass_guitar": "Basguitar",
"acoustic_guitar": "Akustisk Guitar",
"steel_guitar": "Stål Guitar",
"tapping": "Tapping på guitar",
"strum": "Slå an",
"banjo": "Banjo",
"sitar": "Sitar",
"mandolin": "Mandolin",
"snare_drum": "Lilletromme",
"rimshot": "Kantslag",
"drum_roll": "Trommehvirvel",
"bass_drum": "Stortromme",
"techno": "Techno"
"snicker": "Smålatter"
}

View File

@@ -24,13 +24,13 @@
"am": "am",
"year_one": "{{time}} år",
"year_other": "{{time}} år",
"mo": "{{time}}må",
"mo": "{{time}}mo",
"month_one": "{{time}} måned",
"month_other": "{{time}} måneder",
"d": "{{time}}d",
"day_one": "{{time}} dag",
"day_other": "{{time}} dage",
"h": "{{time}}t",
"h": "{{time}}h",
"yr": "{{time}}yr",
"hour_one": "{{time}} time",
"hour_other": "{{time}} timer",
@@ -41,11 +41,11 @@
"second_one": "{{time}} sekund",
"second_other": "{{time}} sekunder",
"formattedTimestamp": {
"12hour": "d MMM, h:mm:ss aaa",
"24hour": "d. MMM, HH:mm:ss"
"12hour": "MMM d, h:mm:ss aaa",
"24hour": "MMM d, HH:mm:ss"
},
"formattedTimestamp2": {
"12hour": "dd/MM h:mm:ss",
"12hour": "MM/dd h:mm:ssa",
"24hour": "d MMM HH:mm:ss"
},
"formattedTimestampHourMinute": {
@@ -57,26 +57,22 @@
"24hour": "HH:mm:ss"
},
"formattedTimestampMonthDayHourMinute": {
"12hour": "d MMM, h:mm aaa",
"24hour": "d MMM, HH:mm"
"12hour": "MMM d, h:mm aaa",
"24hour": "MMM d, HH:mm"
},
"formattedTimestampMonthDayYear": {
"12hour": "d MMM, yyyy",
"24hour": "d MMM, yyyy"
"12hour": "MMM d, yyyy",
"24hour": "MMM d, yyyy"
},
"formattedTimestampMonthDayYearHourMinute": {
"12hour": "d MMM yyyy, h:mm aaa",
"24hour": "d MMM yyyy, HH:mm"
"12hour": "MMM d yyyy, h:mm aaa",
"24hour": "MMM d yyyy, HH:mm"
},
"formattedTimestampMonthDay": "d MMM",
"formattedTimestampMonthDay": "MMM d",
"formattedTimestampFilename": {
"12hour": "dd-MM-yy-h-mm-ss-a",
"24hour": "dd-MM-yy-HH-mm-ss"
},
"never": "Aldrig",
"inProgress": "Under behandling",
"invalidStartTime": "Ugyldig starttid",
"invalidEndTime": "Ugyldig sluttid"
"12hour": "MM-dd-yy-h-mm-ss-a",
"24hour": "MM-dd-yy-HH-mm-ss"
}
},
"unit": {
"speed": {
@@ -86,28 +82,14 @@
"length": {
"feet": "fod",
"meters": "meter"
},
"data": {
"kbps": "kB/s",
"mbps": "MB/s",
"gbps": "GB/s",
"kbph": "kB/time",
"mbph": "MB/time",
"gbph": "GB/time"
}
},
"label": {
"back": "Gå tilbage",
"hide": "Skjul {{item}}",
"show": "Vis {{item}}",
"ID": "ID",
"none": "Ingen",
"all": "Alle",
"other": "Andet"
"back": "Gå tilbage"
},
"button": {
"apply": "Anvend",
"reset": "Nulstil",
"reset": "Reset",
"done": "Udført",
"enabled": "Aktiveret",
"enable": "Aktiver",
@@ -134,22 +116,21 @@
"no": "Nej",
"download": "Download",
"info": "Info",
"suspended": "Sat på pause",
"unsuspended": "Genoptag",
"suspended": "Suspenderet",
"unsuspended": "Ophæv suspendering",
"play": "Afspil",
"unselect": "Fravælg",
"export": "Eksporter",
"deleteNow": "Slet nu",
"next": "Næste",
"continue": "Fortsæt"
"next": "Næste"
},
"menu": {
"system": "System",
"systemMetrics": "Systemstatistik",
"systemMetrics": "System metrics",
"configuration": "Konfiguration",
"systemLogs": "Systemlogfiler",
"systemLogs": "System logs",
"settings": "Indstillinger",
"configurationEditor": "Konfigurationsværktøj",
"configurationEditor": "Konfiguratons Editor",
"languages": "Sprog",
"language": {
"en": "English (Engelsk)",
@@ -184,17 +165,8 @@
"th": "ไทย (Thai)",
"ca": "Català (Katalansk)",
"withSystem": {
"label": "Brug systemindstillinger for sprog"
},
"ptBR": "Português brasileiro (Brasiliansk Portugisisk)",
"sr": "Српски (Serbisk)",
"sl": "Slovenščina (Slovensk)",
"lt": "Lietuvių (Litauisk)",
"bg": "Български (Bulgarsk)",
"gl": "Galego (Galisisk)",
"id": "Bahasa Indonesia (Indonesisk)",
"ur": "اردو (Urdu)",
"hr": "Hrvatski (Kroatisk)"
"label": "Brug system indstillinger for sprog"
}
},
"appearance": "Udseende",
"darkMode": {
@@ -213,7 +185,7 @@
"nord": "Nord",
"red": "Rød",
"highcontrast": "Høj Kontrast",
"default": "Standard"
"default": "Default"
},
"help": "Hjælp",
"documentation": {
@@ -222,7 +194,7 @@
},
"restart": "Genstart Frigate",
"live": {
"title": "Direkte",
"title": "Live",
"allCameras": "Alle kameraer",
"cameras": {
"title": "Kameraer",
@@ -230,28 +202,27 @@
"count_other": "{{count}} Kameraer"
}
},
"review": "Gennemse",
"review": "Review",
"explore": "Udforsk",
"export": "Eksporter",
"uiPlayground": "UI sandkasse",
"faceLibrary": "Ansigtsarkiv",
"faceLibrary": "Face Library",
"user": {
"title": "Bruger",
"account": "Konto",
"current": "Aktiv bruger: {{user}}",
"anonymous": "anonym",
"logout": "Log ud",
"setPassword": "Vælg kodeord"
},
"classification": "Kategorisering"
"logout": "Logout",
"setPassword": "Set Password"
}
},
"toast": {
"copyUrlToClipboard": "Kopieret URL til udklipsholder.",
"copyUrlToClipboard": "Kopieret URL til klippebord.",
"save": {
"title": "Gem",
"error": {
"title": "Ændringer kunne ikke gemmes: {{errorMessage}}",
"noMessage": "Kunne ikke gemme konfigurationsændringer"
"title": "Ændringer kan ikke gemmes: {{errorMessage}}",
"noMessage": "Kan ikke gemme konfigurationsændringer"
}
}
},
@@ -262,7 +233,7 @@
"desc": "Admins har fuld adgang til Frigate UI. Viewers er begrænset til at se kameraer, gennemse items, og historik i UI."
},
"pagination": {
"label": "sideinddeling",
"label": "paginering",
"previous": {
"title": "Forrige",
"label": "Gå til forrige side"
@@ -274,27 +245,15 @@
"more": "Flere sider"
},
"accessDenied": {
"documentTitle": "Adgang nægtet - Frigate",
"title": "Adgang nægtet",
"desc": "Du har ikke rettigheder til at se denne side."
"documentTitle": "Adgang forbudt - Frigate",
"title": "Adgang forbudt",
"desc": "Du har ikke tiiladelse til at se denne side."
},
"notFound": {
"documentTitle": "Ikke fundet - Frigate",
"title": "404",
"desc": "Siden blev ikke fundet"
"desc": "Side ikke fundet"
},
"selectItem": "Vælg {{item}}",
"readTheDocumentation": "Læs dokumentationen",
"list": {
"two": "{{0}} og {{1}}",
"many": "{{items}}, og {{last}}",
"separatorWithSpace": ", "
},
"field": {
"optional": "Valgfrit",
"internalID": "Det interne ID som Frigate bruger i konfigurationen og databasen"
},
"information": {
"pixels": "{{area}}px"
}
"readTheDocumentation": "Læs dokumentationen"
}

View File

@@ -8,8 +8,7 @@
"passwordRequired": "Kodeord kræves",
"loginFailed": "Login fejlede",
"unknownError": "Ukendt fejl. Tjek logs.",
"rateLimit": "Grænsen for forespørgsler er overskredet. Prøv igen senere.",
"webUnknownError": "Ukendt fejl. Tjek konsollogs."
"rateLimit": "Grænsen for forespørgsler er overskredet. Prøv igen senere."
},
"firstTimeLogin": "Forsøger du at logge ind for første gang? Loginoplysningerne står i Frigate-loggene."
}

View File

@@ -14,73 +14,8 @@
"label": "Navn",
"placeholder": "Indtast et navn…",
"errorMessage": {
"mustLeastCharacters": "Kameragruppens navn skal være mindst 2 tegn.",
"exists": "Kameragruppenavn findes allerede.",
"nameMustNotPeriod": "Kameragruppenavn må ikke indeholde en periode.",
"invalid": "Ugyldigt kamera gruppenavn."
}
},
"cameras": {
"label": "Kameraer",
"desc": "Vælg kameraer til denne gruppe."
},
"icon": "Ikon",
"success": "Kameragruppe ({{name}}) er blevet gemt.",
"camera": {
"birdseye": "Fugleøje",
"setting": {
"label": "Kamera Streaming Indstillinger",
"title": "{{cameraName}} Streaming Indstillinger",
"desc": "Skift de live streaming muligheder for denne kameragruppes dashboard. <em> Disse indstillinger er enheds- og browserspecifikke.</em>",
"audioIsAvailable": "Lyd er tilgængelig for denne stream",
"audioIsUnavailable": "Lyd er ikke tilgængelig for denne strøm",
"audio": {
"tips": {
"title": "Lyd skal komme fra dit kamera og konfigureret i go2rtc til denne stream."
}
},
"stream": "Stream",
"placeholder": "Vælg en stream",
"streamMethod": {
"label": "Streaming Metode",
"placeholder": "Vælg en streaming metode",
"method": {
"noStreaming": {
"label": "Ingen Streaming",
"desc": "Kamerabilleder vil kun opdatere én gang i minuttet og ingen live streaming vil forekomme."
},
"smartStreaming": {
"label": "Smart Streaming (anbefalet)",
"desc": "Smart streaming vil opdatere dit kamerabillede én gang i minuttet, når der ikke sker noget, for at spare båndbredde og ressourcer. Når der registreres aktivitet, skifter billedet problemfrit til en live stream."
},
"continuousStreaming": {
"label": "Kontinuerlig Streaming",
"desc": {
"title": "Kamerabillede vil altid være en live stream, når det er synligt på instrumentbrættet, selv om der ikke registreres nogen aktivitet.",
"warning": "Kontinuerlig streaming kan forårsage højt båndbreddeforbrug og ydelsesproblemer. Brug med omtanke."
}
}
}
},
"compatibilityMode": {
"label": "Kompatibilitetstilstand",
"desc": "Aktivér kun denne mulighed, hvis kameraets live stream viser farve artefakter og har en diagonal linje på højre side af billedet."
}
"mustLeastCharacters": "Kameragruppens navn skal være mindst 2 tegn."
}
}
},
"debug": {
"options": {
"label": "Indstillinger",
"title": "Valgmuligheder",
"showOptions": "Vis muligheder",
"hideOptions": "Skjul muligheder"
},
"boundingBox": "Afgrænsningsfelt",
"timestamp": "Tidsstempel",
"zones": "Zoner",
"mask": "Maske",
"motion": "Bevægelse",
"regions": "Regioner"
}
}

View File

@@ -6,8 +6,7 @@
"title": "Frigate genstarter",
"button": "Gennemtving genindlæsning nu",
"content": "Denne side genindlæses om {{countdown}} sekunder."
},
"description": "Dette vil kortvarigt stoppe Frigate under genstart."
}
},
"explore": {
"plus": {
@@ -18,9 +17,7 @@
"review": {
"question": {
"label": "Bekræft denne etiket til Frigate Plus",
"ask_a": "Er dette objekt et <code>{{label}}</code>?",
"ask_an": "Er dette objekt en <code>{{label}}</code>?",
"ask_full": "Er dette objekt en <code>{{untranslatedLabel}}</code> ({{translatedLabel}})?"
"ask_a": "Er dette objekt et <code>{{label}}</code>?"
}
}
}

View File

@@ -1,5 +1,5 @@
{
"filter": "Filtrer",
"filter": "Filter",
"classes": {
"label": "Klasser",
"all": {

View File

@@ -2,7 +2,7 @@
"iconPicker": {
"selectIcon": "Vælg et ikon",
"search": {
"placeholder": "Søg efter et ikon…"
"placeholder": "Søg efter ikoner…"
}
}
}

View File

@@ -4,48 +4,12 @@
"cameraDisabled": "Kamera er deaktiveret",
"noPreviewFoundFor": "Ingen forhåndsvisning fundet for {{cameraName}}",
"submitFrigatePlus": {
"title": "Indsend dette billede til Frigate+?",
"title": "Indsend denne frame til Frigate+?",
"submit": "Indsend"
},
"livePlayerRequiredIOSVersion": "iOS 17.1 eller nyere kræves for denne type livestream.",
"streamOffline": {
"title": "Stream offline",
"desc": "Der er ikke modtaget nogen billeder på {{cameraName}}-<code>detect</code>-streamen, tjek fejllogs."
},
"stats": {
"streamType": {
"title": "Stream type:",
"short": "Type"
},
"bandwidth": {
"title": "Båndbredde:",
"short": "Båndbredde"
},
"latency": {
"title": "Latenstid:",
"value": "{{seconds}} sekunder",
"short": {
"title": "Latenstid",
"value": "{{seconds}} sek"
}
},
"droppedFrames": {
"short": {
"title": "Tabt",
"value": "{{droppedFrames}} billeder"
},
"title": "Tabte billeder:"
},
"totalFrames": "Antal billeder i alt:",
"decodedFrames": "Dekodede billeder:",
"droppedFrameRate": "Rate for tabte billeder:"
},
"toast": {
"success": {
"submittedFrigatePlus": "Billede sendt til Frigate+"
},
"error": {
"submitFrigatePlusFailed": "Kunne ikke sende billede til Frigate+"
}
"desc": "Der er ikke modtaget nogen frames på {{cameraName}}-<code>detect</code>-streamen, tjek fejlloggene."
}
}

View File

@@ -14,107 +14,5 @@
"sink": "Håndvask",
"toothbrush": "Tandbørste",
"scissors": "Saks",
"clock": "Ur",
"fire_hydrant": "Brandhane",
"street_sign": "Gadeskilt",
"stop_sign": "Stopskilt",
"parking_meter": "Parkeringsautomat",
"bench": "Bænk",
"bird": "Fugl",
"cat": "Kat",
"dog": "Hund",
"horse": "Hest",
"sheep": "Får",
"cow": "Ko",
"elephant": "Elefant",
"bear": "Bjørn",
"zebra": "Zebra",
"giraffe": "Giraf",
"hat": "Hat",
"backpack": "Rygsæk",
"umbrella": "Paraply",
"shoe": "Sko",
"eye_glasses": "Briller",
"handbag": "Håndtaske",
"tie": "Slips",
"suitcase": "Kuffert",
"frisbee": "Frisbee",
"skis": "Ski",
"snowboard": "Snowboard",
"sports_ball": "Bold",
"kite": "Drage",
"baseball_bat": "Baseball Bat",
"baseball_glove": "Baseball hanske",
"surfboard": "Surfbræt",
"tennis_racket": "Tennis ketcher",
"bottle": "Flaske",
"plate": "Tallerken",
"wine_glass": "Vinglas",
"cup": "Kop",
"fork": "Gaffel",
"knife": "Kniv",
"spoon": "Ske",
"bowl": "Skål",
"banana": "Banan",
"apple": "Æble",
"sandwich": "Sandwich",
"orange": "Appelsin",
"broccoli": "Broccoli",
"carrot": "Gulerod",
"hot_dog": "Hotdog",
"pizza": "Pizza",
"donut": "Donut",
"cake": "Kage",
"chair": "Stol",
"couch": "Sofa",
"potted_plant": "Potteplante",
"bed": "Seng",
"mirror": "Spejl",
"dining_table": "Spisebord",
"window": "Vindue",
"desk": "Bord",
"toilet": "Toilet",
"tv": "Fjernsyn",
"laptop": "Bærebar computer",
"mouse": "Mus",
"remote": "Fjernbetjening",
"keyboard": "Tastatur",
"cell_phone": "Mobiltelefon",
"microwave": "Mikrobølgeovn",
"oven": "Ovn",
"toaster": "Brødrister",
"refrigerator": "Køleskab",
"blender": "Mixer",
"book": "Bog",
"vase": "Vase",
"teddy_bear": "Bamse",
"hair_dryer": "Føntørrer",
"hair_brush": "Hårbørste",
"squirrel": "Egern",
"deer": "Hjort",
"animal": "Dyr",
"bark": "Gø",
"fox": "Ræv",
"goat": "Gæd",
"rabbit": "Kanin",
"raccoon": "Vaskebjørn",
"robot_lawnmower": "Robotplæneklipper",
"waste_bin": "Affaldsspand",
"on_demand": "Manuel optagelse",
"face": "Ansigt",
"license_plate": "Nummerplade",
"package": "Pakke",
"bbq_grill": "Grill",
"amazon": "Amazon levering",
"usps": "USPS levering",
"ups": "UPS levering",
"fedex": "FedEx levering",
"dhl": "DHL levering",
"an_post": "An Post levering",
"purolator": "Purolator levering",
"postnl": "PostNL levering",
"nzpost": "NZPost levering",
"postnord": "PostNord levering",
"gls": "GLS levering",
"dpd": "DPD levering"
"clock": "Ur"
}

View File

@@ -1,187 +1,18 @@
{
"documentTitle": "Kategoriseringsmodeller - Frigate",
"documentTitle": "Klassifikationsmodeller",
"details": {
"scoreInfo": "Scoren viser den gennemsnitlige sikkerhed for kategoriseringen på tværs af alle registreringer af dette objekt.",
"unknown": "Ukendt",
"none": "Ingen"
"scoreInfo": "Scoren repræsenterer den gennemsnitlige klassifikationssikkerhed på tværs af alle registreringer af dette objekt.",
"unknown": "Ukendt"
},
"description": {
"invalidName": "Ugyldigt navn. Navne må kun indeholde bogstaver, tal, mellemrum, apostroffer, understregninger og bindestreger."
},
"button": {
"deleteClassificationAttempts": "Slet kategoriseringsbilleder",
"deleteClassificationAttempts": "Slet klassifikationsbilleder",
"renameCategory": "Omdøb klasse",
"deleteCategory": "Slet klasse",
"deleteImages": "Slet billeder",
"trainModel": "Træn model",
"addClassification": "Tilføj Kategori",
"deleteModels": "Slet modeller",
"editModel": "Rediger model"
},
"tooltip": {
"trainingInProgress": "Modellen er ved at blive trænet",
"noNewImages": "Der er ingen nye billeder at lære af. Kategorisér flere billeder i datasættet først.",
"noChanges": "Ingen ændringer i datasættet siden sidste træning.",
"modelNotReady": "Modellen er ikke klar til træning"
},
"toast": {
"success": {
"deletedCategory": "Slettet kategori",
"deletedImage": "Slettede billeder",
"deletedModel_one": "{{count}} model er nu slettet",
"deletedModel_other": "{{count}} modeller er nu slettet",
"categorizedImage": "Billedet er nu kategoriseret",
"trainedModel": "Modellen er klar.",
"trainingModel": "Modeltræning er started.",
"updatedModel": "Modellens indstillinger er opdateret",
"renamedCategory": "Kategorien er omdøbt til {{name}}"
},
"error": {
"deleteImageFailed": "Fejl under sletning: {{errorMessage}}",
"deleteCategoryFailed": "Sletning af kategori fejlede: {{errorMessage}}",
"deleteModelFailed": "Sletning af model fejlede: {{errorMessage}}",
"categorizeFailed": "Kategorisering af billedet fejlede: {{errorMessage}}",
"trainingFailed": "Træning af modellen fejlede. Check Frigate loggen.",
"trainingFailedToStart": "Opstart af modeltræning fejlede: {{errorMessage}}",
"updateModelFailed": "Ændring af modellen fejlede: {{errorMessage}}",
"renameCategoryFailed": "Kan ikke omdøbe kategorien: {{errorMessage}}"
}
},
"deleteCategory": {
"title": "Slet kategori",
"desc": "Er du sikker på at du vil slette kategorien {{name}}? Dette kan ikke fortrydes og sletter alle tilhørende billeder samt træning af modellen.",
"minClassesTitle": "Kan ikke slette Kategori",
"minClassesDesc": "Modellen skal have mindst 2 kategorier. Tilføj en kategori, før du sletter denne."
},
"deleteModel": {
"title": "Slet Kategoriseringsmodellen",
"desc_one": "Er du sikker på, at du vil slette {{count}} model? Dette vil permanent slette alle tilknyttede data, inkl. billeder og træningsdata. Denne handling kan ikke fortrydes.",
"desc_other": "Er du sikker på, at du vil slette {{count}} modeller? Dette vil permanent slette alle tilknyttede data, inkl. billeder og træningsdata. Denne handling kan ikke fortrydes.",
"single": "Er du sikker på, at du vil slette {{name}}? Dette vil permanent slette alle tilknyttede data, inklusive billeder og træningsdata. Denne handling kan ikke fortrydes."
},
"train": {
"title": "Nyeste kategorier",
"titleShort": "Nyeste",
"aria": "Vælg de nyeste kategorier"
},
"categories": "Kategorier",
"createCategory": {
"new": "Opret en ny kategori"
},
"categorizeImageAs": "Kategoriser billedet som:",
"categorizeImage": "Kategoriser billedet",
"menu": {
"objects": "Genstande",
"states": "Statestik"
},
"noModels": {
"object": {
"title": "Ingen kategoriseringsmodeller for genstande",
"description": "Opret en model, der kan kategorisere genstande.",
"buttonText": "Opret Genstands Model"
},
"state": {
"title": "Ingen modeller til genstandstilstande",
"description": "Opret en brugerdefineret model til at overvåge og kategorisere tilstandsændringer i specifikke kamerområder.",
"buttonText": "Opret tilstandsmodel"
}
},
"wizard": {
"step1": {
"type": "Type",
"typeState": "Tilstand",
"typeObject": "Genstand",
"objectLabel": "Genstands mærkat",
"objectLabelPlaceholder": "Vælg genstands type...",
"classificationType": "Kategoriseringstype",
"classificationTypeTip": "Udforsk kategoriseringstyper",
"errors": {
"nameLength": "Modellens navn må højst være 64 tegn",
"nameOnlyNumbers": "Modellens navn skal indeholde bogstaver",
"classRequired": "Der mangler en kategori",
"classesUnique": "Kategorinavne skal være unikke",
"noneNotAllowed": "Kategorinavnet 'none' er ikke tilladt",
"stateRequiresTwoClasses": "Tilstandsmodeller har brug for 2 kategorier",
"objectLabelRequired": "Vælg genstands mærkat",
"objectTypeRequired": "Vælg kategoriseringstype",
"nameRequired": "Modelnavn er påkrævet"
},
"description": "Tilstandsmodeller overvåger faste kameraområder for ændringer (f.eks. dør åben/lukket). Genstandsmodeller tilføjer kategoriseringer til detekterede genstande (f.eks. kendte dyr, leveringspersoner osv.).",
"name": "Navn",
"namePlaceholder": "Skriv modelnavn...",
"classificationTypeDesc": "Underetiketter tilføjer ekstra tekst til genstandens etiket (f.eks. 'Person: UPS'). Attributter er søgbare metadata, der opbevares separat i genstandens metadata.",
"classificationSubLabel": "Underetiketter",
"classificationAttribute": "Attribut",
"classes": "Kategori",
"states": "Tilstande",
"classesTip": "Lær om kategorier",
"classesStateDesc": "Definér de forskellige tilstande, dit kameraområde kan være i. For eksempel: 'åben' og 'lukket' for en garageport.",
"classesObjectDesc": "Definér de forskellige kategorier, som detekterede genstande skal kategoriseres i. For eksempel: 'leveringsperson', 'beboer', 'fremmed' til kategorisering af personer.",
"classPlaceholder": "Skriv kategorinavn..."
},
"step2": {
"description": "Vælg kameraer, og definer det område, der skal overvåges for hvert kamera. Modellen vil kategorisere tilstanden i disse områder.",
"cameras": "Kameraer",
"selectCamera": "Vælg Kamera",
"noCameras": "Klik + for at tilføje kamera",
"selectCameraPrompt": "Vælg et kamera fra listen for at definere dets overvågningsområde"
},
"step3": {
"selectImagesPrompt": "Vælg alle billeder med: {{className}}",
"selectImagesDescription": "Klik på billederne for at vælge dem. Klik på Fortsæt, når du er færdig med denne kategori.",
"allImagesRequired_one": "Venligst kategoriser alle billeder. {{count}} billede tilbage.",
"allImagesRequired_other": "Venligst kategoriser alle billeder. {{count}} billeder tilbage.",
"generating": {
"title": "Genererer testbilleder",
"description": "Frigate henter repræsentative billeder fra dine optagelser. Det kan tage et øjeblik..."
},
"training": {
"title": "Træningsmodel",
"description": "Din model trænes i baggrunden. Luk denne dialog, og din model vil begynde at køre, så snart træningen er færdig."
},
"retryGenerate": "Forsøg at generere igen",
"noImages": "Ingen prøvebilleder blev genereret",
"classifying": "Kategoriserer og træner...",
"trainingStarted": "Træningen er startet",
"modelCreated": "Model er oprettet. Brug visningen af nylige kategoriseringer til at tilføje billeder for de manglende tilstande, og træn modellen derefter.",
"errors": {
"noCameras": "Ingen kamera konfigureret",
"noObjectLabel": "Ingen genstandsmærkat valgt",
"generateFailed": "Kunne ikke generere eksempler: {{error}}",
"generationFailed": "Der opstod en fejl under genereringen. Prøv igen.",
"classifyFailed": "Kunne ikke kategorisere billederne: {{error}}"
},
"generateSuccess": "Eksempelbilleder er nu genereret",
"missingStatesWarning": {
"title": "Manglende tilstandseksempler",
"description": "Det anbefales at vælge eksempler for alle tilstande for at opnå de bedste resultater. Du kan fortsætte uden at vælge alle tilstande, men modellen bliver ikke trænet, før alle tilstande har billeder. Efter du fortsætter, kan du bruge visningen Seneste kategoriseringer til at kategorisere billeder for de manglende tilstande og derefter træne modellen."
}
},
"title": "Opret ny kategorisering",
"steps": {
"nameAndDefine": "Navn og definition",
"stateArea": "Tilstandsområde",
"chooseExamples": "Vælg Eksempler"
}
},
"edit": {
"title": "Rediger kategoriseringsmodel",
"descriptionState": "Rediger kategorierne for denne model til genstandstilstande. Ændringer kræver, at modellen trænes igen.",
"descriptionObject": "Rediger genstandstypen og kategoriseringstypen for denne genstandskategoriseringsmodel.",
"stateClassesInfo": "Bemærk: Ændring af tilstandskategorier kræver, at modellen trænes igen med de opdaterede kategorier."
},
"deleteDatasetImages": {
"title": "Slet billeder i datasættet",
"desc_one": "Er du sikker på, at du vil slette {{count}} billede fra {{dataset}}? Denne handling kan ikke fortrydes og kræver, at modellen trænes igen.",
"desc_other": "Er du sikker på, at du vil slette {{count}} billeder fra {{dataset}}? Denne handling kan ikke fortrydes og kræver, at modellen trænes igen."
},
"deleteTrainImages": {
"title": "Slet trænings billeder",
"desc_one": "Er du sikker på, at du vil slette {{count}} billede? Denne handling kan ikke fortrydes.",
"desc_other": "Er du sikker på, at du vil slette {{count}} billeder? Denne handling kan ikke fortrydes."
},
"renameCategory": {
"title": "Omdøb Kategori",
"desc": "Indtast et nyt navn til {{name}}. Modellen skal trænes igen, før navneændringen træder i kraft."
"addClassification": "Tilføj klassifikation"
}
}

View File

@@ -3,16 +3,8 @@
"copyConfig": "Kopiér konfiguration",
"saveAndRestart": "Gem & Genstart",
"saveOnly": "Kun gem",
"configEditor": "Konfigurationsværktøj",
"safeConfigEditor": "Konfigurationsværktøj (Sikker tilstand)",
"safeModeDescription": "Frigate er i sikker tilstand på grund af valideringsfejl af konfigurationen.",
"confirm": "Afslut uden at gemme?",
"toast": {
"success": {
"copyToClipboard": "Konfigurationen er kopieret."
},
"error": {
"savingError": "Kan ikke gemme konfigurationen"
}
}
"configEditor": "Konfigurationseditor",
"safeConfigEditor": "Konfigurationseditor (Sikker tilstand)",
"safeModeDescription": "Frigate er i sikker tilstand på grund af en fejl ved validering af konfigurationen.",
"confirm": "Afslut uden at gemme?"
}

View File

@@ -11,22 +11,6 @@
"empty": {
"alert": "Der er ingen advarsler at gennemgå",
"detection": "Der er ingen registreringer at gennemgå",
"motion": "Ingen bevægelsesdata fundet",
"recordingsDisabled": {
"title": "Optagelser skal være aktiveret"
}
},
"documentTitle": "Gennemse - Frigate",
"recordings": {
"documentTitle": "Optagelser - Frigate"
},
"calendarFilter": {
"last24Hours": "Sidste 24 timer"
},
"markAsReviewed": "Marker som gennemset",
"markTheseItemsAsReviewed": "Marker disse som gennemset",
"detail": {
"aria": "Skift til detaljevisning"
},
"timeline.aria": "Vælg tidslinje"
"motion": "Ingen bevægelsesdata fundet"
}
}

View File

@@ -17,15 +17,10 @@
"context": "Udforsk kan bruges, når genindekseringen af de sporede objektindlejringer er fuldført.",
"finishingShortly": "Afsluttes om lidt",
"step": {
"thumbnailsEmbedded": "Miniaturer indlejret: ",
"descriptionsEmbedded": "Beskrivelser indlejrede: ",
"trackedObjectsProcessed": "Sporede objekter behandlede: "
"thumbnailsEmbedded": "Miniaturer indlejret: "
}
},
"title": "Udforsk er ikke tilgængelig",
"downloadingModels": {
"context": "Frigate henter de nødvendige indlejringsmodeller for at understøtte semantiske søgninger. Dette kan tage flere minutter, afhængig af hastigheden på din netværksforbindelse."
}
"title": "Udforsk er ikke tilgængelig"
},
"exploreMore": "Udforsk flere {{label}}-objekter",
"details": {

View File

@@ -8,11 +8,5 @@
"desc": "Indtast et nyt navn for denne eksport."
},
"noExports": "Ingen eksporter fundet",
"deleteExport": "Slet eksport",
"tooltip": {
"shareExport": "Del eksport",
"downloadVideo": "Download video",
"editName": "Rediger navn",
"deleteExport": "Slette eksport"
}
"deleteExport": "Slet eksport"
}

View File

@@ -3,8 +3,7 @@
"description": {
"addFace": "Tilføj en ny samling til ansigtsbiblioteket ved at uploade dit første billede.",
"placeholder": "Angiv et navn for bibliotek",
"invalidName": "Ugyldigt navn. Navne må kun indeholde bogstaver, tal, mellemrum, apostroffer, understregninger og bindestreger.",
"nameCannotContainHash": "Navet kan ikke indeholde #."
"invalidName": "Ugyldigt navn. Navne må kun indeholde bogstaver, tal, mellemrum, apostroffer, understregninger og bindestreger."
},
"details": {
"person": "Person",
@@ -16,78 +15,5 @@
"uploadFaceImage": {
"title": "Upload ansigtsbillede",
"desc": "Upload et billede for at scanne efter ansigter og inkludere det for {{pageToggle}}"
},
"train": {
"titleShort": "Nyeste",
"title": "Seneste genkendelser",
"aria": "Vælg seneste genkendelser",
"empty": "Der er ingen nylige ansigtsgenkendelser"
},
"createFaceLibrary": {
"new": "Nyt ansigt",
"nextSteps": "<ul> <li>Brug fanen <strong>Seneste genkendelser</strong> til at udvælge og træne på billeder for hver registreret person.</li> <li>Fokusér på billeder taget lige forfra for de bedste resultater; undgå træningsbilleder, hvor ansigter er fotograferet fra siden eller i vinkel.</li> </ul>"
},
"steps": {
"faceName": "Skriv ansigt navn",
"uploadFace": "Upload ansigt billede",
"nextSteps": "Næste skridt",
"description": {
"uploadFace": "Upload et billede af {{name}}, hvor ansigtet er set forfra. Billedet behøver ikke kun at vise ansigtet og skal ikke beskæres."
}
},
"button": {
"deleteFace": "Slet ansigt",
"deleteFaceAttempts": "Slet ansigter",
"addFace": "Tilføj ansigt",
"renameFace": "Omdøb ansigt",
"uploadImage": "Upload billede",
"reprocessFace": "Genbehandl ansigt"
},
"trainFace": "Lær ansigt",
"renameFace": {
"title": "Omdøb ansigt",
"desc": "Indtast et nyt navn til {{name}}"
},
"toast": {
"success": {
"deletedFace_one": "{{count}} ansigt blev slettet",
"deletedFace_other": "{{count}} ansigter blev slettet",
"deletedName_one": "{{count}} ansigt slettet",
"deletedName_other": "{{count}} ansigter slettet",
"uploadedImage": "Billedet blev uploadet.",
"addFaceLibrary": "{{name}} er blevet tilføjet til ansigtsbiblioteket!",
"renamedFace": "Ansigtet er blevet omdøbt til {{name}}",
"trainedFace": "Ansigtet er blevet trænet.",
"updatedFaceScore": "Ansigtets score er blevet opdateret til {{score}} ({{name}})."
},
"error": {
"uploadingImageFailed": "Kunne ikke uploade billedet: {{errorMessage}}",
"addFaceLibraryFailed": "Kunne ikke angive navn på ansigtet: {{errorMessage}}",
"deleteFaceFailed": "Kunne ikke slette: {{errorMessage}}",
"deleteNameFailed": "Kunne ikke slette navnet: {{errorMessage}}",
"renameFaceFailed": "Kunne ikke omdøbe ansigtet: {{errorMessage}}",
"trainFailed": "Kunne ikke træne: {{errorMessage}}",
"updateFaceScoreFailed": "Kunne ikke opdatere ansigtets score: {{errorMessage}}"
}
},
"deleteFaceAttempts": {
"desc_one": "Er du sikker på, at du vil slette {{count}} ansigt? Denne handling kan ikke fortrydes.",
"desc_other": "Er du sikker på, at du vil slette {{count}} ansigter? Denne handling kan ikke fortrydes.",
"title": "Slet ansigter"
},
"collections": "Samlinger",
"deleteFaceLibrary": {
"title": "Slet navn",
"desc": "Er du sikker på, at du vil slette samlingen {{name}}? Dette vil permanent slette alle tilknyttede ansigter."
},
"imageEntry": {
"maxSize": "Maks. størrelse: {{size}} MB",
"validation": {
"selectImage": "Vælg venligst en billedfil."
},
"dropActive": "Slip billedet her…",
"dropInstructions": "Træk og slip eller indsæt et billede her eller klik for at vælge"
},
"nofaces": "Ingen tilgængelige ansigter",
"trainFaceAs": "Træn ansigt som:"
}
}

View File

@@ -14,104 +14,8 @@
"move": {
"clickMove": {
"label": "Klik i billedrammen for at centrere kameraet",
"enable": "Aktivér klik for at flytte",
"disable": "Deaktiver klik for at flytte"
},
"left": {
"label": "Flyt PTZ-kameraet til venstre"
},
"up": {
"label": "Flyt PTZ kamera op"
},
"down": {
"label": "Flyt PTZ-kameraet ned"
},
"right": {
"label": "Flyt PTZ-kameraet til højre"
"enable": "Aktivér klik for at flytte"
}
},
"zoom": {
"in": {
"label": "Zoom PTZ-kamera ind"
},
"out": {
"label": "Zoom PTZ kamera ud"
}
},
"focus": {
"in": {
"label": "Focus PTZ kamera ind"
},
"out": {
"label": "Focus PTZ kamera ud"
}
},
"frame": {
"center": {
"label": "Klik på billedet for at centrere PTZ-kameraet"
}
},
"presets": "PTZ kamera forudindstillinger"
},
"camera": {
"enable": "Aktivér kamera",
"disable": "Deaktivér kamera"
},
"muteCameras": {
"enable": "Slå lyd på alle kameraer fra",
"disable": "Slå lyd på alle kameraer til"
},
"detect": {
"enable": "Aktiver detektering",
"disable": "Deaktiver detektering"
},
"recording": {
"enable": "Aktivér optagelse",
"disable": "Deaktiver optagelse"
},
"snapshots": {
"enable": "Aktivér Snapshots",
"disable": "Deaktivér Snapshots"
},
"snapshot": {
"takeSnapshot": "Hent instant snapshot",
"noVideoSource": "Ingen videokilde til snapshot.",
"captureFailed": "Kunne ikke tage snapshot.",
"downloadStarted": "Hentning af snapshot startet."
},
"audioDetect": {
"enable": "Aktiver lyddetektor",
"disable": "Deaktiver lyddetektor"
},
"transcription": {
"enable": "Aktiver Live Audio Transkription",
"disable": "Deaktiver Live Audio Transkription"
},
"autotracking": {
"enable": "Aktiver Autotracking",
"disable": "Deaktiver Autotracking"
},
"streamStats": {
"enable": "Vis Stream statistik",
"disable": "Skjul Stream statistik"
},
"manualRecording": {
"title": "Manuel optagelse",
"tips": "Hent et øjebliksbillede eller start en manuel begivenhed baseret på dette kameras indstillinger for optagelse af opbevaring.",
"playInBackground": {
"label": "Afspil i baggrunden",
"desc": "Aktiver denne mulighed for at fortsætte streaming, når afspilleren er skjult."
},
"showStats": {
"label": "Vis statistik",
"desc": "Aktiver denne mulighed for at vise streamstatistikker som en overlejring på kameraets feed."
},
"debugView": "Debug View",
"start": "Start on-demand optagelse",
"started": "Start manuel optagelse.",
"failedToStart": "Manuel optagelse fejlede.",
"recordDisabledTips": "Da optagelsen er deaktiveret eller begrænset i konfig for dette kamera, gemmes der kun et snapshot.",
"end": "Afslut manuel optagelse",
"ended": "Afsluttet manuel optagelse."
}
}
}

View File

@@ -1,5 +1,5 @@
{
"filter": "Filtrer",
"filter": "Filter",
"export": "Eksporter",
"calendar": "Kalender",
"filters": "Filtere",

View File

@@ -8,12 +8,5 @@
"filterInformation": "Filter information",
"filterActive": "Filtre aktiv",
"clear": "Ryd søgning"
},
"trackedObjectId": "Sporet genstands-ID",
"filter": {
"label": {
"cameras": "Kameraer",
"zones": "Områder"
}
}
}

View File

@@ -9,11 +9,6 @@
"enrichments": "Indstillinger for berigelser - Frigate",
"masksAndZones": "Maske- og zoneeditor - Frigate",
"motionTuner": "Bevægelsesjustering - Frigate",
"general": "Brugergrænsefladeindstillinger - Frigate",
"frigatePlus": "Frigate+ Indstillinger - Frigate",
"notifications": "Notifikations indstillinger - Frigate"
},
"menu": {
"ui": "Brugergrænseflade"
"general": "Brugergrænsefladeindstillinger - Frigate"
}
}

View File

@@ -84,8 +84,7 @@
},
"inProgress": "Im Gange",
"invalidStartTime": "Ungültige Startzeit",
"invalidEndTime": "Ungültige Endzeit",
"never": "Nie"
"invalidEndTime": "Ungültige Endzeit"
},
"button": {
"save": "Speichern",
@@ -179,8 +178,7 @@
"lt": "Lietuvių (Litauisch)",
"bg": "Български (bulgarisch)",
"gl": "Galego (Galicisch)",
"id": "Bahasa Indonesia (Indonesisch)",
"hr": "Hrvatski (Kroatisch)"
"id": "Bahasa Indonesia (Indonesisch)"
},
"appearance": "Erscheinung",
"theme": {

View File

@@ -6,8 +6,7 @@
"content": "Diese Seite wird in {{countdown}} Sekunde(n) aktualisiert.",
"button": "Neuladen erzwingen"
},
"button": "Neustarten",
"description": "Dies wird Frigate kurz stoppen, während es neu startet."
"button": "Neustarten"
},
"explore": {
"plus": {

View File

@@ -1,9 +1,8 @@
{
"description": {
"placeholder": "Gib einen Name für diese Kollektion ein",
"addFace": "Füge der Gesichtsbibliothek eine neue Sammlung hinzu, indem du ein erstes Bild hochlädst.",
"invalidName": "Ungültiger Name. Namen dürfen nur Buchstaben, Zahlen, Leerzeichen, Apostrophe, Unterstriche und Bindestriche enthalten.",
"nameCannotContainHash": "Der Name darf keine # enthalten."
"addFace": "Füge der Gesichtsbibliothek eine neue Sammlung hinzu, indem du ein Bild hochlädst.",
"invalidName": "Ungültiger Name. Namen dürfen nur Buchstaben, Zahlen, Leerzeichen, Apostrophe, Unterstriche und Bindestriche enthalten."
},
"details": {
"person": "Person",

View File

@@ -178,16 +178,6 @@
"restricted": {
"title": "Keine Kamera verfügbar",
"description": "Sie haben keine Berechtigung, Kameras in dieser Gruppe anzuzeigen."
},
"default": {
"title": "Keine Kameras konfiguriert",
"description": "Zum Start eine Kamera mit Frigate verbinden.",
"buttonText": "Kamera hinzufügen"
},
"group": {
"title": "Keine Kameras in der Gruppe",
"description": "Diese Kameragruppe hat keine zugewiesenen oder aktiven Kameras.",
"buttonText": "Gruppen verwalten"
}
},
"snapshot": {

View File

@@ -49,12 +49,12 @@
"desc": "Standardmäßig werden die letzten Warnmeldungen auf dem Live-Dashboard als kurze Videoschleifen abgespielt. Deaktiviere diese Option, um nur ein statisches Bild der letzten Warnungen auf diesem Gerät/Browser anzuzeigen."
},
"automaticLiveView": {
"desc": "Automatisch zur Live-Ansicht einer Kamera wechseln, wenn eine Aktivität erkannt wird. Wenn diese Option deaktiviert ist, werden statische Kamerabilder auf dem Live-Dashboard nur einmal pro Minute aktualisiert.",
"desc": "Zeigt automatisch das Live-Bild einer Kamera an, wenn eine Aktivität erkannt wird. Ist diese Option deaktiviert, werden Kamerabilder im Live-Dashboard nur einmal pro Minute aktualisiert.",
"label": "Automatische Live Ansicht"
},
"displayCameraNames": {
"label": "Immer Namen der Kamera anzeigen",
"desc": "Zeige immer die Kameranamen in einem Chip im Dashboard der Mehrkamera-Live-Ansicht an."
"desc": "Kamerabezeichnung immer im einem Chip im Live-View-Dashboard r mehrere Kameras anzeigen."
},
"liveFallbackTimeout": {
"label": "Live Player Ausfallzeitlimit",
@@ -276,11 +276,6 @@
},
"error": {
"mustBeFinished": "Polygonzeichnung muss vor dem Speichern abgeschlossen sein."
},
"type": {
"zone": "Zone",
"motion_mask": "Bewegungsmaske",
"object_mask": "Objektmaske"
}
},
"speed": {
@@ -549,7 +544,7 @@
"placeholder": "Passwort eingeben",
"requirements": {
"title": "Passwort Anforderungen:",
"length": "Mindestens 12 Zeichen",
"length": "Mindestens 8 Zeichen",
"uppercase": "Mindestens ein Großbuchstabe",
"digit": "Mindestens eine Ziffer",
"special": "Mindestens ein Sonderzeichen (!@#$%^&*(),.?\":{}|<>)"
@@ -1215,7 +1210,7 @@
"restreamingWarning": "Die Reduzierung der Verbindungen zur Kamera für den Aufzeichnungsstream kann zu einer geringfügigen Erhöhung der CPU-Auslastung führen.",
"brands": {
"reolink-rtsp": "Reolink RTSP wird nicht empfohlen. Aktivieren Sie HTTP in den Firmware-Einstellungen der Kamera und starten Sie den Assistenten neu.",
"reolink-http": "Reolink-HTTP-Streams sollten für eine bessere Kompatibilität FFmpeg verwenden. Aktivieren Sie für diesen Stream die Option „Stream-Kompatibilitätsmodus verwenden“."
"reolink-http": "Für Reolink-HTTP-Streams sollten sie FFmpeg verwenden, um eine bessere Kompatibilität zu gewährleisten. Aktivieren Sie für diesen Stream die Option „Stream-Kompatibilitätsmodus verwenden“."
},
"dahua": {
"substreamWarning": "Substream 1 ist auf eine niedrige Auflösung festgelegt. Viele Kameras von Dahua / Amcrest / EmpireTech unterstützen zusätzliche Substreams, die in den Kameraeinstellungen aktiviert werden müssen. Es wird empfohlen, diese Streams zu überprüfen und zu nutzen, sofern sie verfügbar sind."
@@ -1270,11 +1265,11 @@
"title": "Kamera-Einstellungen überprüfen",
"object_descriptions": {
"title": "Generative KI Objektbeschreibungen",
"desc": "Aktiviere/deaktiviere vorübergehend die Objektbeschreibungen durch generative KI für diese Kamera. Wenn diese Option deaktiviert ist, werden keine KI-generierten Beschreibungen für verfolgte Objekte dieser Kamera erstellt."
"desc": "Aktiviere/deaktiviere vorübergehend die Objektbeschreibungen durch Generative KI für diese Kamera. Wenn diese Option deaktiviert ist, werden keine KI-generierten Beschreibungen für verfolgte Objekte dieser Kamera erstellt."
},
"review_descriptions": {
"title": "Generative KI Review Beschreibungen",
"desc": "Aktivieren/deaktivieren Sie vorübergehend die generativen KI-Überprüfungsbeschreibungen für diese Kamera, bis Frigate neu gestartet wird. Wenn diese Option deaktiviert ist, werden für Überprüfungselemente auf dieser Kamera keine KI-generierten Beschreibungen angefordert."
"desc": "Generative KI Review Beschreibungen für diese Kamera vorübergehend aktivieren/deaktivieren. Wenn diese Option deaktiviert ist, werden für die Review Elemente dieser Kamera keine KI-generierten Beschreibungen angefordert."
},
"review": {
"title": "Überprüfung",

View File

@@ -75,8 +75,7 @@
"s": "{{time}}δ",
"inProgress": "Σε εξέλιξη",
"invalidStartTime": "Μη έγκυρη ώρα έναρξης",
"invalidEndTime": "Μη έγκυρη ώρα λήξης",
"never": "Ποτέ"
"invalidEndTime": "Μη έγκυρη ώρα λήξης"
},
"menu": {
"live": {

View File

@@ -2,13 +2,6 @@
"documentTitle": "Μοντέλα Ταξινόμησης - Frigate",
"details": {
"scoreInfo": "Η βαθμολογία αντιπροσωπεύει την κατά μέσο όρο ταξινομική εμπιστοσύνη μεταξύ όλων των ανιχνεύσεων αυτού του αντικειμένου.",
"none": "Καμία",
"unknown": "Άγνωστο"
},
"button": {
"deleteClassificationAttempts": "Διαγραφή Εικόνων Ταξινόμησης",
"deleteImages": "Διαγραφή Εικόνων",
"trainModel": "Εκπαίδευση Μοντέλου",
"addClassification": "Προσθήκη Ταξινόμησης"
"none": "Καμία"
}
}

View File

@@ -9,10 +9,7 @@
"empty": {
"alert": "Δεν υπάρχουν ειδοποιήσεις για εξέταση",
"detection": "Δεν υπάρχουν εντοπισμοί για εξέταση",
"motion": "Δεν βρέθηκαν στοιχεία κίνησης",
"recordingsDisabled": {
"title": "Οι καταγραφές πρέπει να είναι ενεργοποιημένες"
}
"motion": "Δεν βρέθηκαν στοιχεία κίνησης"
},
"timeline": "Χρονολόγιο",
"timeline.aria": "Επιλογή χρονοσειράς",

View File

@@ -193,7 +193,6 @@
"yue": "粵語 (Cantonese)",
"th": "ไทย (Thai)",
"ca": "Català (Catalan)",
"hr": "Hrvatski (Croatian)",
"sr": "Српски (Serbian)",
"sl": "Slovenščina (Slovenian)",
"lt": "Lietuvių (Lithuanian)",

View File

@@ -1,7 +1,6 @@
{
"restart": {
"title": "Are you sure you want to restart Frigate?",
"description": "This will briefly stop Frigate while it restarts.",
"button": "Restart",
"restarting": {
"title": "Frigate is Restarting",

View File

@@ -2,8 +2,7 @@
"description": {
"addFace": "Add a new collection to the Face Library by uploading your first image.",
"placeholder": "Enter a name for this collection",
"invalidName": "Invalid name. Names can only include letters, numbers, spaces, apostrophes, underscores, and hyphens.",
"nameCannotContainHash": "Name cannot contain #."
"invalidName": "Invalid name. Names can only include letters, numbers, spaces, apostrophes, underscores, and hyphens."
},
"details": {
"timestamp": "Timestamp",

View File

@@ -468,11 +468,6 @@
}
},
"polygonDrawing": {
"type": {
"zone": "zone",
"motion_mask": "motion mask",
"object_mask": "object mask"
},
"removeLastPoint": "Remove last point",
"reset": {
"label": "Clear all points"
@@ -733,7 +728,10 @@
},
"requirements": {
"title": "Password requirements:",
"length": "At least 12 characters"
"length": "At least 8 characters",
"uppercase": "At least one uppercase letter",
"digit": "At least one digit",
"special": "At least one special character (!@#$%^&*(),.?\":{}|<>)"
},
"match": "Passwords match",
"notMatch": "Passwords don't match"

View File

@@ -90,8 +90,7 @@
},
"inProgress": "En progreso",
"invalidStartTime": "Hora de inicio no válida",
"invalidEndTime": "Hora de finalización no válida",
"never": "Nunca"
"invalidEndTime": "Hora de finalización no válida"
},
"menu": {
"settings": "Ajustes",
@@ -153,8 +152,7 @@
"bg": "Български (Búlgaro)",
"gl": "Galego (Gallego)",
"id": "Bahasa Indonesia (Indonesio)",
"ur": "اردو (Urdu)",
"hr": "Hrvatski (Croata)"
"ur": "اردو (Urdu)"
},
"appearance": "Apariencia",
"darkMode": {
@@ -269,8 +267,7 @@
"show": "Mostrar {{item}}",
"ID": "ID",
"none": "Ninguno",
"all": "Todas",
"other": "Otro"
"all": "Todas"
},
"role": {
"title": "Rol",

View File

@@ -6,8 +6,7 @@
"content": "Esta página se recargará en {{countdown}} segundos."
},
"title": "¿Estás seguro de que quieres reiniciar Frigate?",
"button": "Reiniciar",
"description": "Esto detendrá brevemente Frigate mientras se reinicia."
"button": "Reiniciar"
},
"explore": {
"plus": {

View File

@@ -129,13 +129,13 @@
"classes": {
"label": "Clases",
"all": {
"title": "Todas las clases"
"title": "Todas las Clases"
},
"count_one": "{{count}} Clase",
"count_other": "{{count}} Clases"
},
"attributes": {
"label": "Atributos de clasificación",
"label": "Clasificación de Atributos",
"all": "Todos los Atributos"
}
}

View File

@@ -62,7 +62,7 @@
"details": {
"scoreInfo": "La puntuación representa la confianza media de clasificación en todas las detecciones de este objeto.",
"unknown": "Desconocido",
"none": "Ninguna"
"none": "Nada"
},
"categorizeImage": "Clasificar Imagen",
"menu": {

View File

@@ -14,5 +14,5 @@
"documentTitle": "Editor de Configuración - Frigate",
"confirm": "¿Salir sin guardar?",
"safeConfigEditor": "Editor de Configuración (Modo Seguro)",
"safeModeDescription": "Frigate esta en modo seguro debido a un error en la validación de la configuración."
"safeModeDescription": "Frigate esta en modo seguro debido a un error en la configuración."
}

View File

@@ -2,8 +2,7 @@
"description": {
"addFace": "Agregar una nueva colección a la Biblioteca de Rostros subiendo tu primera imagen.",
"placeholder": "Introduce un nombre para esta colección",
"invalidName": "Nombre incorrecto. Los nombres solo pueden incluir letras, números, espacios, apóstrofes, guiones bajos, y guiones.",
"nameCannotContainHash": "El nombre no puede contener #."
"invalidName": "Nombre incorrecto. Los nombres solo pueden incluir letras, números, espacios, apóstrofes, guiones bajos, y guiones."
},
"details": {
"person": "Persona",

View File

@@ -178,16 +178,6 @@
"restricted": {
"title": "No hay cámaras disponibles",
"description": "No tiene permiso para ver ninguna cámara en este grupo."
},
"default": {
"title": "No hay Cámaras Configuradas",
"description": "Comienza conectando una cámara a Frigate.",
"buttonText": "Añadir Cámara"
},
"group": {
"title": "No hay Cámaras en Grupo",
"description": "Estae grupo de cámaras no tiene cámaras asignadas o habilitadas.",
"buttonText": "Gestionar Grupos"
}
},
"snapshot": {

View File

@@ -52,7 +52,7 @@
},
"title": "Panel en directo",
"displayCameraNames": {
"label": "Siempre mostrar nombres de las Cámaras",
"label": "Siempre mostrar nombres de las Camaras",
"desc": "Siempre mostrar nombres de cámaras en la vista en vivo multi-cámara."
},
"liveFallbackTimeout": {
@@ -276,12 +276,7 @@
"reset": {
"label": "Borrar todos los puntos"
},
"removeLastPoint": "Eliminar el último punto",
"type": {
"zone": "zona",
"motion_mask": "máscara de movimiento",
"object_mask": "máscara de objeto"
}
"removeLastPoint": "Eliminar el último punto"
},
"speed": {
"error": {
@@ -553,7 +548,7 @@
"hide": "Ocultar contraseña",
"requirements": {
"title": "Requisitos de contraseña:",
"length": "Al menos 12 caracteres",
"length": "Al menos 8 caracteres",
"uppercase": "Al menos una mayúscula",
"digit": "Al menos un número",
"special": "Al menos un caracter especial (!@#$%^&*(),.?\":{}|<>)"
@@ -713,7 +708,7 @@
"unsavedChanges": "Cambios en la configuración de Frigate+ no guardados"
},
"enrichments": {
"title": "Configuración de Enriquecimientos",
"title": "Configuración de Análisis Avanzado",
"unsavedChanges": "Cambios sin guardar en la configuración de Análisis Avanzado",
"birdClassification": {
"title": "Clasificación de Aves",
@@ -1196,11 +1191,11 @@
"title": "Configuración de revisión de la cámara",
"object_descriptions": {
"title": "Descripciones de objetos de IA generativa",
"desc": "Habilite o deshabilite temporalmente las descripciones de objetos generadas por IA para esta cámara hasta que Frigate se reinicie. Al deshabilitarlas, no se solicitarán descripciones generadas por IA para los objetos rastreados en esta cámara."
"desc": "Habilite o deshabilite temporalmente las descripciones de objetos generadas por IA para esta cámara. Al deshabilitarlas, no se solicitarán descripciones generadas por IA para los objetos rastreados en esta cámara."
},
"review_descriptions": {
"title": "Revisión de descripciones de IA generativa",
"desc": "Habilita o deshabilita temporalmente las revisión de descripciones generadas por IA para esta cámara hasta que Frigate se reinicie. Al deshabilitarlas, no se solicitarán descripciones generadas por IA para los elementos de revisión de esta cámara."
"desc": "Habilita o deshabilita temporalmente las revisión de descripciones generadas por IA para esta cámara. Al deshabilitarlas, no se solicitarán descripciones generadas por IA para los elementos de revisión de esta cámara."
},
"review": {
"title": "Revisar",

View File

@@ -90,9 +90,7 @@
"series": {
"go2rtc": "go2rtc",
"recording": "grabación",
"review_segment": "revisar segmento",
"embeddings": "embeddings",
"audio_detector": "detector de audio"
"review_segment": "revisar segmento"
}
}
},

View File

@@ -75,8 +75,7 @@
"12hour": "dd. MMM yyyy, hh:mm aaa",
"24hour": "dd. MMM yyyy, HH:mm"
},
"formattedTimestampMonthDay": "dd. MMM",
"never": "Mitte kunagi"
"formattedTimestampMonthDay": "dd. MMM"
},
"menu": {
"user": {
@@ -139,8 +138,7 @@
"bg": "Български (bulgaaria keel)",
"gl": "Galego (galeegi keel)",
"id": "Bahasa Indonesia (indoneesia keel)",
"ur": "اردو (urdu keel)",
"hr": "Hrvatski (horvaadi keel)"
"ur": "اردو (urdu keel)"
},
"system": "Süsteem",
"systemMetrics": "Süsteemi meetrika",

View File

@@ -6,8 +6,7 @@
"title": "Frigate käivitub uuesti",
"content": "See leht laaditakse uuesti {{countdown}} sekundi pärast.",
"button": "Laadi uuesti kohe"
},
"description": "Järgnevaga Frigate uuesti käivitamise ajaks lõpetab korraks töö."
}
},
"search": {
"saveSearch": {

View File

@@ -30,21 +30,11 @@
"stationary": "{{label}} jäi paigale",
"active": "{{label}} muutus aktiivseks",
"entered_zone": "{{label}} sisenes tsooni {{zones}}",
"visible": "{{label}} on tuvastatud",
"header": {
"zones": "Tsoonid",
"ratio": "Suhtarv",
"area": "Ala",
"score": "Punktiskoor"
}
"visible": "{{label}} on tuvastatud"
},
"title": "Jälgimise üksikasjad",
"noImageFound": "Selle ajatempli kohta ei leidu pilti.",
"createObjectMask": "Loo objektimask",
"carousel": {
"previous": "Eelmine slaid",
"next": "Järgmine slaid"
}
"createObjectMask": "Loo objektimask"
},
"documentTitle": "Avasta - Frigate",
"generativeAI": "Generatiivne tehisaru",
@@ -73,15 +63,12 @@
"tips": {
"mismatch_one": "Tuvastasin {{count}} võõra objekti ja need on lisatud ülevaatamiseks. Need objektid kas ei ole piisavad häire või tuvastamise jaoks, aga ka võivad juba olla eemaldatud või kustutatud.",
"mismatch_other": "Tuvastasin {{count}} võõrast objekti ja need on lisatud ülevaatamiseks. Need objektid kas ei ole piisavad häire või tuvastamise jaoks, aga ka võivad juba olla eemaldatud või kustutatud."
},
"title": "Vaata objekti üksikasju",
"desc": "Vaata objekti üksikasju"
}
},
"snapshotScore": {
"label": "Hetkvõttete punktiskoor"
},
"regenerateFromSnapshot": "Loo uuesti hetkvõttest",
"timestamp": "Ajatampel"
},
"trackedObjectDetails": "Jälgitava objekti üksikasjad"
}
}

View File

@@ -10,8 +10,7 @@
"objectDetection": "Objektide tuvastamine",
"audioDetection": "Heli tuvastus",
"transcription": "Heli üleskirjutus",
"snapshots": "Hetkvõtted",
"autotracking": "Automaatne jälgimine"
"snapshots": "Hetkvõtted"
},
"documentTitle": "Otseülekanne - Frigate",
"documentTitle.withCamera": "{{camera}} - Otseülekanne - Frigate",
@@ -96,10 +95,6 @@
"playInBackground": {
"label": "Esita taustal",
"tips": "Selle eelistusega saad määrata, et voogedastus jääb tööle ka siis, kui meesiaesitaja on suletud."
},
"audio": {
"available": "Selles voogedastuses on heliriba saadaval",
"unavailable": "Selles voogedastuses pole heliriba saadaval"
}
},
"notifications": "Teavitused",
@@ -134,25 +129,6 @@
"restricted": {
"title": "Ühtegi kaamerat pole saadaval",
"description": "Sul pole õigust ühegi selle grupi kaamera vaatamiseks."
},
"title": "Ühtegi kaamerat pole seadistatud",
"description": "Alustamiseks ühenda mõni kaamera Frigate'iga."
},
"effectiveRetainMode": {
"modes": {
"active_objects": "Aktiivsed objektid",
"all": "Kõik",
"motion": "Liikumine"
}
},
"editLayout": {
"label": "Muuda paigutust",
"group": {
"label": "Muuda kaameragruppi"
},
"exitEdit": "Välju muutmisest"
},
"history": {
"label": "Näita varasemat sisu"
}
}

View File

@@ -4,23 +4,10 @@
"password": "Salasõna",
"passwordPlaceholder": "Valikuline",
"customUrlPlaceholder": "rtsp://kasutajanimi:salasõna@host:port/asukoht",
"connectionSettings": "Ühenduse seadistused",
"port": "Port",
"username": "Kasutajanimi",
"usernamePlaceholder": "Valikuline"
"connectionSettings": "Ühenduse seadistused"
},
"step3": {
"streamUrlPlaceholder": "rtsp://kasutajanimi:salasõna@host:port/asukoht",
"url": "Võrguaadress",
"resolution": "Resolutsioon",
"quality": "Kvaliteet",
"roles": "Rollid",
"roleLabels": {
"record": "Salvestamine",
"audio": "Heliriba"
},
"connected": "Ühendatud",
"featuresTitle": "Funktsionaalsused"
"streamUrlPlaceholder": "rtsp://kasutajanimi:salasõna@host:port/asukoht"
},
"steps": {
"probeOrSnapshot": "Võta proov või tee hetkvõte"
@@ -28,34 +15,7 @@
"step2": {
"testing": {
"fetchingSnapshot": "Laadin kaamera hetkvõtet alla..."
},
"retry": "Proovi uuesti",
"manufacturer": "Tootja",
"model": "Mudel",
"firmware": "Püsivara",
"profiles": "Profiilid",
"presets": "Eelseadistused",
"useCandidate": "Kasuta",
"uriCopy": "Kopeeri",
"connected": "Ühendatud"
},
"testResultLabels": {
"resolution": "Resolutsioon",
"video": "Video",
"audio": "Heliriba",
"fps": "Kaadrisagedus"
},
"step4": {
"reload": "Laadi uuesti",
"connecting": "Ühendan…",
"valid": "Kehtiv",
"failed": "Ebaõnnestunud",
"connectStream": "Ühenda",
"connectingStream": "Ühendan",
"disconnectStream": "Katkesta ühendus",
"roles": "Rollid",
"none": "Määramata",
"error": "Viga"
}
}
},
"users": {
@@ -69,10 +29,7 @@
}
},
"table": {
"password": "Lähtesta salasõna",
"username": "Kasutajanimi",
"actions": "Tegevused",
"role": "Roll"
"password": "Lähtesta salasõna"
},
"dialog": {
"form": {
@@ -96,7 +53,7 @@
"hide": "Peida salasõna",
"requirements": {
"title": "Salasõna reeglid:",
"length": "Vähemalt 12 tähemärki",
"length": "Vähemalt 8 tähemärki",
"uppercase": "Vähemalt üks suurtäht",
"digit": "Vähemalt üks number",
"special": "Vähemalt üks erimärk (!@#$%^&*(),.?\":{}|<>)"
@@ -113,9 +70,6 @@
"currentPassword": {
"title": "Senine salasõna",
"placeholder": "Sisesta oma senine salasõna"
},
"user": {
"title": "Kasutajanimi"
}
},
"createUser": {
@@ -130,42 +84,12 @@
"currentPasswordRequired": "Senine salasõna on vajalik",
"incorrectCurrentPassword": "Senine salasõna pole õige",
"passwordVerificationFailed": "Salasõna kontrollimine ei õnnestunud"
},
"changeRole": {
"roleInfo": {
"admin": "Peakasutaja",
"viewer": "Vaataja"
}
}
},
"title": "Kasutajad"
}
},
"debug": {
"boundingBoxes": {
"desc": "Näita jälgitavate objektide ümber märgiskaste"
},
"title": "Silumine ja veaotsing",
"debugging": "Veaotsing ja silumine",
"audio": {
"title": "Heliriba",
"score": "punktiskoor"
},
"timestamp": {
"title": "Ajatempel"
},
"zones": {
"title": "Tsoonid"
},
"regions": {
"title": "Alad"
},
"paths": {
"title": "Asukohad"
},
"objectShapeFilterDrawing": {
"score": "Punktiskoor",
"ratio": "Suhtarv",
"area": "Ala"
}
},
"documentTitle": {
@@ -189,31 +113,10 @@
"automaticLiveView": {
"label": "Automaatne otseülekande vaade"
}
},
"calendar": {
"title": "Kalender",
"firstWeekday": {
"sunday": "Pühapäev",
"monday": "Esmaspäev",
"label": "Esimene nädalapäev"
}
},
"storedLayouts": {
"title": "Salvestatud paigutused"
},
"recordingsViewer": {
"title": "Salvestuste vaataja"
}
},
"cameraManagement": {
"backToSettings": "Tagasi kaameraseadistuste juurde",
"cameraConfig": {
"enabled": "Kasutusel",
"ffmpeg": {
"pathPlaceholder": "rtsp://...",
"roles": "Rollid"
}
}
"backToSettings": "Tagasi kaameraseadistuste juurde"
},
"notification": {
"notificationSettings": {
@@ -227,16 +130,6 @@
"success": {
"settingSaved": "Teavituste seadistused on salvestatud."
}
},
"title": "Teavitused",
"email": {
"title": "E-post"
},
"cameras": {
"title": "Kaamerad"
},
"suspendTime": {
"suspend": "Peata arvuti töö"
}
},
"frigatePlus": {
@@ -252,48 +145,20 @@
"cleanCopySnapshots": "<code>clean_copy</code> Hetkvõtted",
"camera": "Kaamera"
}
},
"modelInfo": {
"plusModelType": {
"userModel": "Peenhäälestatud"
},
"cameras": "Kaamerad"
}
},
"masksAndZones": {
"zones": {
"point_one": "{{count}} punkt",
"point_other": "{{count}} punkti",
"label": "Tsoonid",
"desc": {
"documentation": "Dokumentatsioon"
},
"name": {
"title": "Nimi"
},
"inertia": {
"title": "Inerts"
},
"objects": {
"title": "Objektid"
}
"point_other": "{{count}} punkti"
},
"motionMasks": {
"point_one": "{{count}} punkt",
"point_other": "{{count}} punkti",
"desc": {
"documentation": "Dokumentatsioon"
}
"point_other": "{{count}} punkti"
},
"objectMasks": {
"point_one": "{{count}} punkt",
"point_other": "{{count}} punkti",
"desc": {
"documentation": "Dokumentatsioon"
},
"objects": {
"title": "Objektid"
}
"point_other": "{{count}} punkti"
}
},
"roles": {
@@ -302,21 +167,6 @@
"userRolesUpdated_one": "{{count}} selle rolliga kasutaja on nüüd määratud Vaatajaks, kellel on ligipääs kõikidele kaameratele.",
"userRolesUpdated_other": "{{count}} selle rolliga kasutajat on nüüd määratud Vaatajaks, kellel on ligipääs kõikidele kaameratele."
}
},
"table": {
"role": "Roll",
"cameras": "Kaamerad",
"actions": "Tegevused"
},
"dialog": {
"deleteRole": {
"deleting": "Kustutan..."
},
"form": {
"cameras": {
"title": "Kaamerad"
}
}
}
},
"menu": {
@@ -328,8 +178,7 @@
"users": "Kasutajad",
"roles": "Rollid",
"notifications": "Teavitused",
"frigateplus": "Frigate+",
"cameraReview": "Ülevaatamine"
"frigateplus": "Frigate+"
},
"dialog": {
"unsavedChanges": {
@@ -340,84 +189,5 @@
"cameraSetting": {
"camera": "Kaamera",
"noCamera": "Kaamerat pole"
},
"enrichments": {
"semanticSearch": {
"reindexNow": {
"confirmButton": "Indekseeri uuesti",
"label": "Indekseeri uuesti kohe"
},
"modelSize": {
"small": {
"title": "väike"
},
"large": {
"title": "suur"
}
},
"title": "Semantiline otsing"
},
"faceRecognition": {
"modelSize": {
"small": {
"title": "väike"
},
"large": {
"title": "suur"
}
}
},
"birdClassification": {
"title": "Lindude klassifikatsioon"
}
},
"cameraReview": {
"review": {
"title": "Ülevaatamine",
"alerts": "Hoiatused ",
"detections": "Tuvastamise tulemused "
}
},
"motionDetectionTuner": {
"Threshold": {
"title": "Lävi"
}
},
"triggers": {
"documentTitle": "Päästikud",
"management": {
"title": "Päästikud"
},
"table": {
"name": "Nimi",
"type": "Tüüp",
"content": "Sisu",
"threshold": "Lävi",
"actions": "Tegevused",
"edit": "Muuda"
},
"type": {
"thumbnail": "Pisipilt",
"description": "Kirjeldus"
},
"dialog": {
"form": {
"name": {
"title": "Nimi"
},
"type": {
"title": "Tüüp"
},
"content": {
"title": "Sisu"
},
"threshold": {
"title": "Lävi"
},
"actions": {
"title": "Tegevused"
}
}
}
}
}

View File

@@ -1,11 +1 @@
{
"documentTitle": "Luokittelumallit - Frigate",
"details": {
"scoreInfo": "Pistemäärä edustaa tämän objektin kaikkien havaintojen keskimääräistä luokitteluvarmuutta.",
"none": "Ei mitään"
},
"button": {
"deleteImages": "Poista kuvat",
"trainModel": "Kouluta malli"
}
}
{}

View File

@@ -13,8 +13,5 @@
"title": "Nimeä uudelleen",
"desc": "Anna uusi nimi viedylle kohteelle.",
"saveExport": "Tallenna vienti"
},
"tooltip": {
"editName": "Muokkaa nimeä"
}
}

View File

@@ -10,8 +10,7 @@
"object": "Virheenjäljitys - Frigate",
"authentication": "Autentikointiuasetukset - Frigate",
"notifications": "Ilmoitusasetukset - Frigate",
"enrichments": "Laajennusasetukset Frigate",
"cameraManagement": "Hallitse Kameroita - Frigate"
"enrichments": "Laajennusasetukset Frigate"
},
"menu": {
"ui": "Käyttöliittymä",

View File

@@ -90,8 +90,7 @@
},
"inProgress": "En cours",
"invalidStartTime": "Heure de début invalide",
"invalidEndTime": "Heure de fin invalide",
"never": "Jamais"
"invalidEndTime": "Heure de fin invalide"
},
"button": {
"apply": "Appliquer",
@@ -175,8 +174,7 @@
"bg": "Български (Bulgare)",
"gl": "Galego (Galicien)",
"id": "Bahasa Indonesia (Indonésien)",
"ur": "اردو (Ourdou)",
"hr": "Hrvatski (Croate)"
"ur": "اردو (Ourdou)"
},
"appearance": "Apparence",
"darkMode": {
@@ -281,8 +279,7 @@
"show": "Afficher {{item}}",
"ID": "ID",
"none": "Aucun",
"all": "Tous",
"other": "Autre"
"all": "Tous"
},
"unit": {
"speed": {

View File

@@ -6,8 +6,7 @@
"content": "Cette page sera rechargée dans {{countdown}} secondes.",
"button": "Forcer l'actualisation maintenant"
},
"button": "Redémarrer",
"description": "Frigate s'arrêtera momentanément pour redémarrer."
"button": "Redémarrer"
},
"explore": {
"plus": {

View File

@@ -2,8 +2,7 @@
"description": {
"addFace": "Ajoutez une nouvelle collection à la bibliothèque de visages en téléversant votre première image.",
"placeholder": "Saisissez un nom pour cette collection.",
"invalidName": "Nom invalide. Les noms ne peuvent contenir que des lettres, des chiffres, des espaces, des apostrophes, des traits de soulignement et des tirets.",
"nameCannotContainHash": "Le nom ne peut pas contenir le caractère #."
"invalidName": "Nom invalide. Les noms ne peuvent contenir que des lettres, des chiffres, des espaces, des apostrophes, des traits de soulignement et des tirets."
},
"details": {
"person": "Personne",

Some files were not shown because too many files have changed in this diff Show More