Compare commits

..

1 Commits

Author SHA1 Message Date
lebaudantoine 0fc70d4e92 wip explore room's configuration validation 2026-03-11 23:13:51 +01:00
49 changed files with 539 additions and 3221 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ __pycache__
**/__pycache__ **/__pycache__
**/*.pyc **/*.pyc
venv venv
**/.venv .venv
# System-specific files # System-specific files
.DS_Store .DS_Store
+12 -14
View File
@@ -124,17 +124,15 @@ jobs:
uses: actions/setup-python@v6 uses: actions/setup-python@v6
with: with:
python-version: "3.13" python-version: "3.13"
- name: Install uv cache: "pip"
uses: astral-sh/setup-uv@v7 - name: Install development dependencies
- name: Install the project run: pip install --user .[dev]
run: uv sync --locked --all-extras
- name: Check code formatting with ruff - name: Check code formatting with ruff
run: uv run ruff format . --diff run: ~/.local/bin/ruff format . --diff
- name: Lint code with ruff - name: Lint code with ruff
run: uv run ruff check . run: ~/.local/bin/ruff check .
- name: Lint code with pylint - name: Lint code with pylint
run: uv run pylint meet demo core run: ~/.local/bin/pylint meet demo core
lint-agents: lint-agents:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -281,10 +279,10 @@ jobs:
uses: actions/setup-python@v6 uses: actions/setup-python@v6
with: with:
python-version: "3.13" python-version: "3.13"
- name: Install uv cache: "pip"
uses: astral-sh/setup-uv@v7
- name: Install the dependencies - name: Install development dependencies
run: uv sync --locked --all-extras run: pip install --user .[dev]
- name: Install gettext (required to compile messages) - name: Install gettext (required to compile messages)
run: | run: |
@@ -292,10 +290,10 @@ jobs:
sudo apt-get install -y gettext sudo apt-get install -y gettext
- name: Generate a MO file from strings extracted from the project - name: Generate a MO file from strings extracted from the project
run: uv run python manage.py compilemessages run: python manage.py compilemessages
- name: Run tests - name: Run tests
run: uv run pytest -n 2 run: ~/.local/bin/pytest -n 2
lint-front: lint-front:
runs-on: ubuntu-latest runs-on: ubuntu-latest
-1
View File
@@ -31,7 +31,6 @@ MANIFEST
# Translations # Translations # Translations # Translations
*.pot *.pot
*.mo
# Environments # Environments
.env .env
-7
View File
@@ -13,8 +13,6 @@ and this project adheres to
- ✨(helm) support celery with our Django backend #1124 - ✨(helm) support celery with our Django backend #1124
- ✨(helm) support ingress for custom background image #1124 - ✨(helm) support ingress for custom background image #1124
- ✨(backend) add authenticated user rate throttling on request-entry #1129 - ✨(backend) add authenticated user rate throttling on request-entry #1129
- ✨(backend) expose `is_active` field for Application in Django admin #1133
- ✨(file-upload) disable by default & limit count by user #1141
### Changed ### Changed
@@ -24,17 +22,12 @@ and this project adheres to
- ♿(frontend) improve chat toast a11y for screen readers #1109 - ♿(frontend) improve chat toast a11y for screen readers #1109
- ♿(frontend) improve ui and aria labels for help article links #1108 - ♿(frontend) improve ui and aria labels for help article links #1108
- 🌐(frontend) improve German translation #1125 - 🌐(frontend) improve German translation #1125
- 🔨(python-env) migrate meet main app to UV #1120
- ♻️(backend) align Application model field with `is_active` convention #1133
- 🔐(backend) avoids revealing the inactive status of an application #1135
- ⚡️(helm) reduce initialDelaySeconds and add periods seconds #1139
### Fixed ### Fixed
- 🐛(frontend) fix hand icon and queue position alignment and position #1119 - 🐛(frontend) fix hand icon and queue position alignment and position #1119
- 🩹(backend) add page_size to pagination for room endpoints #1131 - 🩹(backend) add page_size to pagination for room endpoints #1131
- 🐛(backend) refactor lobby throttling to use participant id #1129 - 🐛(backend) refactor lobby throttling to use participant id #1129
- 🩹(backend) ignore non-recording uploads in storage webhook handler #1142
## [1.10.0] - 2026-03-05 ## [1.10.0] - 2026-03-05
+23 -39
View File
@@ -13,28 +13,14 @@ RUN apk update && \
# ---- Back-end builder image ---- # ---- Back-end builder image ----
FROM base AS back-builder FROM base AS back-builder
WORKDIR /builder
ENV UV_COMPILE_BYTECODE=1 # Copy required python dependencies
ENV UV_LINK_MODE=copy COPY ./src/backend /builder
# Disable Python downloads, because we want to use the system interpreter RUN mkdir /install && \
# across both images. If using a managed Python version, it needs to be pip install --prefix=/install .
# copied from the build image into the final image;
ENV UV_PYTHON_DOWNLOADS=0
# install uv
COPY --from=ghcr.io/astral-sh/uv:0.10.9 /uv /uvx /bin/
WORKDIR /app
RUN --mount=type=cache,target=/root/.cache/uv \
--mount=type=bind,source=src/backend/uv.lock,target=uv.lock \
--mount=type=bind,source=src/backend/pyproject.toml,target=pyproject.toml \
uv sync --locked --no-install-project --no-dev
COPY src/backend /app
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --locked --no-dev
# ---- mails ---- # ---- mails ----
FROM node:20 AS mail-builder FROM node:20 AS mail-builder
@@ -44,7 +30,7 @@ COPY ./src/mail /mail/app
WORKDIR /mail/app WORKDIR /mail/app
RUN yarn install --frozen-lockfile && \ RUN yarn install --frozen-lockfile && \
yarn build yarn build
# ---- static link collector ---- # ---- static link collector ----
@@ -56,17 +42,17 @@ RUN apk add \
libmagic \ libmagic \
rdfind rdfind
# Copy installed python dependencies
COPY --from=back-builder /install /usr/local
# Copy Meet application (see .dockerignore)
COPY ./src/backend /app/
WORKDIR /app WORKDIR /app
# Copy the application from the builder
COPY --from=back-builder /app /app
ENV PATH="/app/.venv/bin:$PATH"
# collectstatic # collectstatic
RUN DJANGO_CONFIGURATION=Build DJANGO_JWT_PRIVATE_SIGNING_KEY=Dummy \ RUN DJANGO_CONFIGURATION=Build DJANGO_JWT_PRIVATE_SIGNING_KEY=Dummy \
python manage.py collectstatic --noinput python manage.py collectstatic --noinput
# Replace duplicated file by a symlink to decrease the overall size of the # Replace duplicated file by a symlink to decrease the overall size of the
# final image # final image
@@ -95,17 +81,14 @@ COPY ./docker/files/usr/local/bin/entrypoint /usr/local/bin/entrypoint
# docker user (see entrypoint). # docker user (see entrypoint).
RUN chmod g=u /etc/passwd RUN chmod g=u /etc/passwd
# Copy the application from the builder # Copy installed python dependencies
COPY --from=back-builder /app /app COPY --from=back-builder /install /usr/local
# Copy Meet application (see .dockerignore)
COPY ./src/backend /app/
WORKDIR /app WORKDIR /app
ENV PATH="/app/.venv/bin:$PATH"
# Generate compiled translation messages
RUN DJANGO_CONFIGURATION=Build \
python manage.py compilemessages --ignore=".venv/**/*"
# We wrap commands run in this container by the following entrypoint that # We wrap commands run in this container by the following entrypoint that
# creates a user on-the-fly with the container user ID (see USER) and root group # creates a user on-the-fly with the container user ID (see USER) and root group
# ID. # ID.
@@ -120,9 +103,10 @@ USER root:root
# Install psql # Install psql
RUN apk add postgresql-client RUN apk add postgresql-client
# Install development dependencies # Uninstall Meet and re-install it in editable mode along with development
RUN --mount=from=ghcr.io/astral-sh/uv:0.10.9,source=/uv,target=/bin/uv \ # dependencies
uv sync --all-extras --locked RUN pip uninstall -y meet
RUN pip install -e .[dev]
# Restore the un-privileged user running the application # Restore the un-privileged user running the application
ARG DOCKER_USER ARG DOCKER_USER
@@ -131,7 +115,7 @@ USER ${DOCKER_USER}
# Target database host (e.g. database engine following docker compose services # Target database host (e.g. database engine following docker compose services
# name) & port # name) & port
ENV DB_HOST=postgresql \ ENV DB_HOST=postgresql \
DB_PORT=5432 DB_PORT=5432
# Run django development server # Run django development server
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"] CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
+4 -4
View File
@@ -223,7 +223,7 @@ superuser: ## Create an admin superuser with password "admin"
.PHONY: superuser .PHONY: superuser
back-i18n-compile: ## compile the gettext files back-i18n-compile: ## compile the gettext files
@$(MANAGE) compilemessages --ignore=".venv/**/*" @$(MANAGE) compilemessages --ignore="venv/**/*"
.PHONY: back-i18n-compile .PHONY: back-i18n-compile
back-i18n-generate: ## create the .pot files used for i18n back-i18n-generate: ## create the .pot files used for i18n
@@ -354,13 +354,13 @@ install-external-secrets: ## install the kubernetes secrets from Vaultwarden
.PHONY: build-k8s-cluster .PHONY: build-k8s-cluster
start-tilt: ## start the kubernetes cluster using kind start-tilt: ## start the kubernetes cluster using kind
tilt up --namespace=meet -f ./bin/Tiltfile tilt up -f ./bin/Tiltfile
.PHONY: build-k8s-cluster .PHONY: build-k8s-cluster
start-tilt-keycloak: ## start the kubernetes cluster using kind, without Pro Connect for authentication, use keycloak start-tilt-keycloak: ## start the kubernetes cluster using kind, without Pro Connect for authentication, use keycloak
DEV_ENV=dev-keycloak tilt up --namespace=meet -f ./bin/Tiltfile DEV_ENV=dev-keycloak tilt up -f ./bin/Tiltfile
.PHONY: build-k8s-cluster .PHONY: build-k8s-cluster
start-tilt-dinum: ## start the kubernetes cluster using kind, without Pro Connect for authentication, but with DINUM styles start-tilt-dinum: ## start the kubernetes cluster using kind, without Pro Connect for authentication, but with DINUM styles
DEV_ENV=dev-dinum tilt up --namespace=meet -f ./bin/Tiltfile DEV_ENV=dev-dinum tilt up -f ./bin/Tiltfile
.PHONY: build-k8s-cluster .PHONY: build-k8s-cluster
-1
View File
@@ -103,7 +103,6 @@ k8s_resource('meet-celery-backend', resource_deps=['redis'])
k8s_resource('meet-celery-summarize', resource_deps=['redis']) k8s_resource('meet-celery-summarize', resource_deps=['redis'])
k8s_resource('meet-celery-transcribe', resource_deps=['redis']) k8s_resource('meet-celery-transcribe', resource_deps=['redis'])
k8s_resource('meet-backend-migrate', resource_deps=['meet-backend']) k8s_resource('meet-backend-migrate', resource_deps=['meet-backend'])
k8s_resource('livekit-livekit-server', resource_deps=['redis'])
k8s_resource('livekit-livekit-server-test-connection', resource_deps=['livekit-livekit-server']) k8s_resource('livekit-livekit-server-test-connection', resource_deps=['livekit-livekit-server'])
k8s_resource('keycloak', resource_deps=['kc-postgresql']) k8s_resource('keycloak', resource_deps=['kc-postgresql'])
k8s_resource('meet-backend-createsuperuser', resource_deps=['meet-backend-migrate']) k8s_resource('meet-backend-createsuperuser', resource_deps=['meet-backend-migrate'])
+1 -3
View File
@@ -58,7 +58,7 @@ services:
/usr/bin/mc admin config set meet notify_webhook:meet-webhook endpoint='http://app-dev:8000/api/v1.0/recordings/storage-hook/' auth_token='Bearer password' && /usr/bin/mc admin config set meet notify_webhook:meet-webhook endpoint='http://app-dev:8000/api/v1.0/recordings/storage-hook/' auth_token='Bearer password' &&
/usr/bin/mc admin service restart meet --wait --json && /usr/bin/mc admin service restart meet --wait --json &&
sleep 15 && sleep 15 &&
/usr/bin/mc event add meet/meet-media-storage arn:minio:sqs::meet-webhook:webhook --event put --prefix "recordings" && /usr/bin/mc event add meet/meet-media-storage arn:minio:sqs::meet-webhook:webhook --event put &&
exit 0;" exit 0;"
app-dev: app-dev:
@@ -80,7 +80,6 @@ services:
volumes: volumes:
- ./src/backend:/app - ./src/backend:/app
- ./data/static:/data/static - ./data/static:/data/static
- /app/.venv
depends_on: depends_on:
- postgresql - postgresql
- mailcatcher - mailcatcher
@@ -106,7 +105,6 @@ services:
volumes: volumes:
- ./src/backend:/app - ./src/backend:/app
- ./data/static:/data/static - ./data/static:/data/static
- /app/.venv
depends_on: depends_on:
- app-dev - app-dev
+1 -1
View File
@@ -71,7 +71,7 @@ backend:
# Extra volume to manage our local custom CA and avoid to set ssl_verify: false # Extra volume to manage our local custom CA and avoid to set ssl_verify: false
extraVolumeMounts: extraVolumeMounts:
- name: certs - name: certs
mountPath: /app/.venv/lib/python3.13/site-packages/certifi/cacert.pem mountPath: /usr/local/lib/python3.12/site-packages/certifi/cacert.pem
subPath: cacert.pem subPath: cacert.pem
# Extra volume to manage our local custom CA and avoid to set ssl_verify: false # Extra volume to manage our local custom CA and avoid to set ssl_verify: false
-1
View File
@@ -28,7 +28,6 @@ AWS_S3_ENDPOINT_URL=http://minio:9000
AWS_S3_ACCESS_KEY_ID=meet AWS_S3_ACCESS_KEY_ID=meet
AWS_S3_SECRET_ACCESS_KEY=password AWS_S3_SECRET_ACCESS_KEY=password
MEDIA_BASE_URL=http://localhost:8083 MEDIA_BASE_URL=http://localhost:8083
FILE_UPLOAD_ENABLED=True
# OIDC # OIDC
OIDC_OP_JWKS_ENDPOINT=http://nginx:8083/realms/meet/protocol/openid-connect/certs OIDC_OP_JWKS_ENDPOINT=http://nginx:8083/realms/meet/protocol/openid-connect/certs
+2 -35
View File
@@ -197,38 +197,6 @@ def resend_notification(modeladmin, request, queryset): # pylint: disable=unuse
) )
@admin.action(description=_("Mark selected recordings as 'Failed to Stop'"))
def mark_as_failed_to_stop(modeladmin, request, queryset):
"""Force selected recordings status to failed_to_stop."""
eligible_statuses = [
models.RecordingStatusChoices.ACTIVE,
models.RecordingStatusChoices.INITIATED,
models.RecordingStatusChoices.STOPPED,
]
eligible = queryset.filter(status__in=eligible_statuses)
skipped = queryset.exclude(status__in=eligible_statuses).count()
updated = eligible.update(status=models.RecordingStatusChoices.FAILED_TO_STOP)
if updated > 0:
modeladmin.message_user(
request,
_("%(count)s recording(s) successfully marked as 'Failed to Stop'.")
% {"count": updated},
level=messages.SUCCESS,
)
if skipped > 0:
modeladmin.message_user(
request,
_("Skipped %(count)s recording(s) with an ineligible status.")
% {"count": skipped},
level=messages.WARNING,
)
@admin.register(models.Recording) @admin.register(models.Recording)
class RecordingAdmin(admin.ModelAdmin): class RecordingAdmin(admin.ModelAdmin):
"""Recording admin interface declaration.""" """Recording admin interface declaration."""
@@ -256,7 +224,7 @@ class RecordingAdmin(admin.ModelAdmin):
"updated_at", "updated_at",
"worker_id", "worker_id",
) )
actions = [resend_notification, mark_as_failed_to_stop] actions = [resend_notification]
def get_queryset(self, request): def get_queryset(self, request):
"""Optimize queries by prefetching related access and user data to avoid N+1 queries.""" """Optimize queries by prefetching related access and user data to avoid N+1 queries."""
@@ -308,7 +276,7 @@ class ApplicationAdmin(admin.ModelAdmin):
form = ApplicationAdminForm form = ApplicationAdminForm
list_display = ("id", "name", "client_id", "get_scopes_display", "is_active") list_display = ("id", "name", "client_id", "get_scopes_display")
fields = [ fields = [
"name", "name",
"id", "id",
@@ -317,7 +285,6 @@ class ApplicationAdmin(admin.ModelAdmin):
"scopes", "scopes",
"client_id", "client_id",
"client_secret", "client_secret",
"is_active",
] ]
readonly_fields = ["id", "created_at", "updated_at"] readonly_fields = ["id", "created_at", "updated_at"]
inlines = [ApplicationDomainInline] inlines = [ApplicationDomainInline]
-15
View File
@@ -43,21 +43,6 @@ def get_frontend_configuration(request):
"expiration_days": settings.RECORDING_EXPIRATION_DAYS, "expiration_days": settings.RECORDING_EXPIRATION_DAYS,
"max_duration": settings.RECORDING_MAX_DURATION, "max_duration": settings.RECORDING_MAX_DURATION,
}, },
"background_image": {
"upload_is_enabled": settings.FILE_UPLOAD_ENABLED,
"max_count_by_user": settings.FILE_UPLOAD_RESTRICTIONS["background_image"][
"max_count_by_user"
],
"max_size": settings.FILE_UPLOAD_RESTRICTIONS["background_image"][
"max_size"
],
"allowed_extensions": settings.FILE_UPLOAD_RESTRICTIONS["background_image"][
"allowed_extensions"
],
"allowed_mimetypes": settings.FILE_UPLOAD_RESTRICTIONS["background_image"][
"allowed_mimetypes"
],
},
"telephony": { "telephony": {
"enabled": settings.ROOM_TELEPHONY_ENABLED, "enabled": settings.ROOM_TELEPHONY_ENABLED,
"phone_number": settings.ROOM_TELEPHONY_PHONE_NUMBER "phone_number": settings.ROOM_TELEPHONY_PHONE_NUMBER
-1
View File
@@ -13,7 +13,6 @@ class FeatureFlag:
"recording": "RECORDING_ENABLE", "recording": "RECORDING_ENABLE",
"storage_event": "RECORDING_STORAGE_EVENT_ENABLE", "storage_event": "RECORDING_STORAGE_EVENT_ENABLE",
"subtitle": "ROOM_SUBTITLE_ENABLED", "subtitle": "ROOM_SUBTITLE_ENABLED",
"file_upload": "FILE_UPLOAD_ENABLED",
} }
@classmethod @classmethod
-8
View File
@@ -1,6 +1,5 @@
"""Permission handlers for the Meet core app.""" """Permission handlers for the Meet core app."""
from django.conf import settings
from django.http import Http404 from django.http import Http404
from rest_framework import permissions from rest_framework import permissions
@@ -117,13 +116,6 @@ class FilePermission(IsAuthenticated):
Handling soft deletions specificities Handling soft deletions specificities
""" """
def has_permission(self, request, view):
"""Allow access only to authenticated users."""
if not settings.FILE_UPLOAD_ENABLED:
raise Http404
return super().has_permission(request, view)
def has_object_permission(self, request, view, obj): def has_object_permission(self, request, view, obj):
""" """
Return a 404 on deleted files or if the user is not the owner Return a 404 on deleted files or if the user is not the owner
+25 -1
View File
@@ -13,7 +13,7 @@ from django.core.exceptions import SuspiciousOperation
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django_pydantic_field.rest_framework import SchemaField from django_pydantic_field.rest_framework import SchemaField
from pydantic import BaseModel, Field from pydantic import BaseModel, Field, ValidationError
from rest_framework import serializers from rest_framework import serializers
from rest_framework.exceptions import PermissionDenied from rest_framework.exceptions import PermissionDenied
from timezone_field.rest_framework import TimeZoneSerializerField from timezone_field.rest_framework import TimeZoneSerializerField
@@ -123,6 +123,16 @@ class ListRoomSerializer(serializers.ModelSerializer):
read_only_fields = ["id", "slug"] read_only_fields = ["id", "slug"]
class RoomConfiguration(BaseModel):
"""Wip"""
can_publish_sources: list[Literal[
"microphone", "screen_share", "screen_share_audio", "camera"
]] | None = None
model_config = {"extra": "forbid"}
class RoomSerializer(serializers.ModelSerializer): class RoomSerializer(serializers.ModelSerializer):
"""Serialize Room model for the API.""" """Serialize Room model for the API."""
@@ -131,6 +141,20 @@ class RoomSerializer(serializers.ModelSerializer):
fields = ["id", "name", "slug", "configuration", "access_level", "pin_code"] fields = ["id", "name", "slug", "configuration", "access_level", "pin_code"]
read_only_fields = ["id", "slug", "pin_code"] read_only_fields = ["id", "slug", "pin_code"]
def validate_configuration(self, configuration):
"""Wip."""
if configuration is None:
return configuration
try:
RoomConfiguration.model_validate(configuration)
except ValidationError as e:
raise SuspiciousOperation("Wip, invalid room configuration")
return configuration
def to_representation(self, instance): def to_representation(self, instance):
""" """
Add users only for administrator users. Add users only for administrator users.
+14 -32
View File
@@ -1,6 +1,7 @@
"""API endpoints""" """API endpoints"""
# pylint: disable=too-many-lines # pylint: disable=too-many-lines
import re
import uuid import uuid
from logging import getLogger from logging import getLogger
from urllib.parse import unquote, urlparse from urllib.parse import unquote, urlparse
@@ -11,7 +12,6 @@ from django.db.models import Q
from django.http import Http404 from django.http import Http404
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.utils.text import slugify from django.utils.text import slugify
from django.utils.translation import gettext_lazy as _
from django_filters import rest_framework as django_filters from django_filters import rest_framework as django_filters
from rest_framework import ( from rest_framework import (
@@ -33,12 +33,10 @@ from rest_framework import (
from core import enums, models, utils from core import enums, models, utils
from core.api.filters import ListFileFilter from core.api.filters import ListFileFilter
from core.enums import MEDIA_STORAGE_URL_PATTERN
from core.recording.enums import FileExtension from core.recording.enums import FileExtension
from core.recording.event.authentication import StorageEventAuthentication from core.recording.event.authentication import StorageEventAuthentication
from core.recording.event.exceptions import ( from core.recording.event.exceptions import (
InvalidBucketError, InvalidBucketError,
InvalidFilepathError,
InvalidFileTypeError, InvalidFileTypeError,
ParsingEventDataError, ParsingEventDataError,
) )
@@ -80,6 +78,17 @@ from .feature_flag import FeatureFlag
logger = getLogger(__name__) logger = getLogger(__name__)
FILE_FOLDER = settings.FILE_UPLOAD_PATH
UUID_REGEX = (
r"[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}"
)
FILE_EXT_REGEX = r"[\d\w]+"
MEDIA_STORAGE_URL_PATTERN = re.compile(
f"{settings.MEDIA_URL:s}"
rf"(?P<key>{FILE_FOLDER:s}/(?P<pk>{UUID_REGEX:s})/\.{FILE_EXT_REGEX:s})$"
)
class NestedGenericViewSet(viewsets.GenericViewSet): class NestedGenericViewSet(viewsets.GenericViewSet):
""" """
A generic Viewset aims to be used in a nested route context. A generic Viewset aims to be used in a nested route context.
@@ -762,14 +771,9 @@ class RecordingViewSet(
except InvalidBucketError as e: except InvalidBucketError as e:
raise drf_exceptions.PermissionDenied("Invalid bucket specified") from e raise drf_exceptions.PermissionDenied("Invalid bucket specified") from e
except InvalidFilepathError: except InvalidFileTypeError as e:
return drf_response.Response( return drf_response.Response(
{"message": "Notification ignored."}, {"message": f"Ignore this file type, {e}"},
)
except InvalidFileTypeError:
return drf_response.Response(
{"message": "Notification ignored."},
) )
try: try:
@@ -969,26 +973,6 @@ class FileViewSet(
def perform_create(self, serializer): def perform_create(self, serializer):
"""Set the current user as creator of the newly created file.""" """Set the current user as creator of the newly created file."""
if settings.FILE_UPLOAD_APPLY_RESTRICTIONS:
file_type = serializer.validated_data["type"]
config_for_file_type = settings.FILE_UPLOAD_RESTRICTIONS[file_type]
count = models.File.objects.filter(
creator=self.request.user,
deleted_at__isnull=True,
type=file_type,
).count()
if count >= config_for_file_type["max_count_by_user"]:
logger.info(
"create_item: user reached max files per user for type %s",
file_type,
)
raise serializers.PermissionDenied(
_("You have reached the maximum number of files for this type.")
)
serializer.save(creator=self.request.user) serializer.save(creator=self.request.user)
def perform_destroy(self, instance): def perform_destroy(self, instance):
@@ -996,7 +980,6 @@ class FileViewSet(
instance.soft_delete() instance.soft_delete()
@decorators.action(detail=True, methods=["post"], url_path="upload-ended") @decorators.action(detail=True, methods=["post"], url_path="upload-ended")
@FeatureFlag.require("file_upload")
def upload_ended(self, request, *args, **kwargs): def upload_ended(self, request, *args, **kwargs):
""" """
Check the actual uploaded file and mark it as ready. Check the actual uploaded file and mark it as ready.
@@ -1179,7 +1162,6 @@ class FileViewSet(
return url_params, request.user.id, file return url_params, request.user.id, file
@decorators.action(detail=False, methods=["get"], url_path="media-auth") @decorators.action(detail=False, methods=["get"], url_path="media-auth")
@FeatureFlag.require("file_upload")
def media_auth(self, request, *args, **kwargs): def media_auth(self, request, *args, **kwargs):
""" """
This view is used by an Nginx subrequest to control access to an file's This view is used by an Nginx subrequest to control access to an file's
+1 -7
View File
@@ -14,15 +14,9 @@ FILE_EXT_REGEX = r"[a-zA-Z0-9]{1,10}"
# pylint: disable=line-too-long # pylint: disable=line-too-long
RECORDING_STORAGE_URL_PATTERN = re.compile( RECORDING_STORAGE_URL_PATTERN = re.compile(
f"{settings.MEDIA_URL:s}{settings.RECORDING_OUTPUT_FOLDER}/(?P<recording_id>{UUID_REGEX:s})\.(?P<extension>{FILE_EXT_REGEX:s})" f"/media/{settings.RECORDING_OUTPUT_FOLDER}/(?P<recording_id>{UUID_REGEX:s}).(?P<extension>{FILE_EXT_REGEX:s})"
) )
MEDIA_STORAGE_URL_PATTERN = re.compile(
f"{settings.MEDIA_URL:s}"
rf"(?P<key>{settings.FILE_UPLOAD_PATH:s}/(?P<pk>{UUID_REGEX:s})\.{FILE_EXT_REGEX:s})$"
)
# Django sets `LANGUAGES` by default with all supported languages. We can use it for # Django sets `LANGUAGES` by default with all supported languages. We can use it for
# the choice of languages which should not be limited to the few languages active in # the choice of languages which should not be limited to the few languages active in
# the app. # the app.
@@ -203,7 +203,7 @@ class ApplicationJWTAuthentication(BaseJWTAuthentication):
logger.warning("Application not found: %s", client_id) logger.warning("Application not found: %s", client_id)
raise exceptions.AuthenticationFailed("Application not found.") from e raise exceptions.AuthenticationFailed("Application not found.") from e
if not application.is_active: if not application.active:
logger.warning( logger.warning(
"Inactive application attempted authentication: %s", client_id "Inactive application attempted authentication: %s", client_id
) )
+3 -3
View File
@@ -61,12 +61,12 @@ class ApplicationViewSet(viewsets.ViewSet):
except models.Application.DoesNotExist as e: except models.Application.DoesNotExist as e:
raise drf_exceptions.AuthenticationFailed("Invalid credentials") from e raise drf_exceptions.AuthenticationFailed("Invalid credentials") from e
if not application.active:
raise drf_exceptions.AuthenticationFailed("Application is inactive")
if not check_password(client_secret, application.client_secret): if not check_password(client_secret, application.client_secret):
raise drf_exceptions.AuthenticationFailed("Invalid credentials") raise drf_exceptions.AuthenticationFailed("Invalid credentials")
if not application.is_active:
raise drf_exceptions.AuthenticationFailed("Application is inactive")
email = serializer.validated_data["scope"] email = serializer.validated_data["scope"]
try: try:
validate_email(email) validate_email(email)
+1 -1
View File
@@ -129,7 +129,7 @@ class ApplicationFactory(factory.django.DjangoModelFactory):
model = models.Application model = models.Application
name = factory.Faker("company") name = factory.Faker("company")
is_active = True active = True
client_id = factory.LazyFunction(utils.generate_client_id) client_id = factory.LazyFunction(utils.generate_client_id)
client_secret = factory.LazyFunction(utils.generate_client_secret) client_secret = factory.LazyFunction(utils.generate_client_secret)
scopes = [] scopes = []
@@ -1,18 +0,0 @@
# Generated by Django 5.2.12 on 2026-03-11 14:39
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('core', '0017_file'),
]
operations = [
migrations.RenameField(
model_name='application',
old_name='active',
new_name='is_active',
),
]
+2 -2
View File
@@ -759,7 +759,7 @@ class Application(BaseModel):
verbose_name=_("Application name"), verbose_name=_("Application name"),
help_text=_("Descriptive name for this application."), help_text=_("Descriptive name for this application."),
) )
is_active = models.BooleanField(default=True) active = models.BooleanField(default=True)
client_id = models.CharField( client_id = models.CharField(
max_length=100, unique=True, default=utils.generate_client_id max_length=100, unique=True, default=utils.generate_client_id
) )
@@ -952,7 +952,7 @@ class File(BaseModel):
_, extension = splitext(self.filename) _, extension = splitext(self.filename)
# We store only the extension in the storage system to avoid # We store only the extension in the storage system to avoid
# leaking Personal Information in logs, etc. # leaking Personal Information in logs, etc.
return f"{self.key_base}{extension!s}" return f"{self.key_base}/{extension!s}"
def get_abilities(self, user): def get_abilities(self, user):
""" """
+1 -3
View File
@@ -9,8 +9,6 @@ from typing import Any, Dict, Optional, Protocol
from django.conf import settings from django.conf import settings
from django.utils.module_loading import import_string from django.utils.module_loading import import_string
from core.enums import FILE_EXT_REGEX, UUID_REGEX
from .exceptions import ( from .exceptions import (
InvalidBucketError, InvalidBucketError,
InvalidFilepathError, InvalidFilepathError,
@@ -88,7 +86,7 @@ class MinioParser:
# pylint: disable=line-too-long # pylint: disable=line-too-long
self._filepath_regex = re.compile( self._filepath_regex = re.compile(
rf"(?P<url_encoded_folder_path>(?:[^%]+%2F)+)?{settings.RECORDING_OUTPUT_FOLDER}%2F(?P<recording_id>{UUID_REGEX})\.(?P<extension>{FILE_EXT_REGEX})" r"(?P<url_encoded_folder_path>(?:[^%]+%2F)+)?(?P<recording_id>[0-9a-fA-F\-]{36})\.(?P<extension>[a-zA-Z0-9]+)"
) )
@staticmethod @staticmethod
@@ -118,7 +118,7 @@ def test_api_files_create_file_authenticated_success():
assert policy_parsed.scheme == "http" assert policy_parsed.scheme == "http"
assert policy_parsed.netloc == "localhost:9000" assert policy_parsed.netloc == "localhost:9000"
assert policy_parsed.path == f"/meet-media-storage/files/{file.id!s}.png" assert policy_parsed.path == f"/meet-media-storage/files/{file.id!s}/.png"
query_params = parse_qs(policy_parsed.query) query_params = parse_qs(policy_parsed.query)
@@ -174,26 +174,6 @@ def test_api_files_create_file_authenticated_extension_case_insensitive():
assert file.title == "file" assert file.title == "file"
def test_api_files_create_file_disabled(settings):
"""
Creating a file is denied if file upload is disabled
"""
settings.FILE_UPLOAD_ENABLED = False
user = factories.UserFactory()
client = APIClient()
client.force_login(user)
response = client.post(
"/api/v1.0/files/",
{
"type": FileTypeChoices.BACKGROUND_IMAGE,
"filename": "file.JPG",
},
format="json",
)
assert response.status_code == 404
assert not File.objects.exists()
def test_api_files_create_file_authenticated_not_checking_extension(settings): def test_api_files_create_file_authenticated_not_checking_extension(settings):
""" """
Creating a file with an extension not allowed should not fail when restrictions are disabled. Creating a file with an extension not allowed should not fail when restrictions are disabled.
@@ -259,48 +239,6 @@ def test_api_files_create_file_authenticated_hidden_file_but_checking_extension_
assert response.json() == {"filename": ["This file extension is not allowed."]} assert response.json() == {"filename": ["This file extension is not allowed."]}
def test_api_files_create_file_too_many(
settings,
):
"""
Creating a file is forbidden if above user limit.
"""
settings.FILE_UPLOAD_APPLY_RESTRICTIONS = True
settings.FILE_UPLOAD_RESTRICTIONS = {
"background_image": {
**settings.FILE_UPLOAD_RESTRICTIONS["background_image"],
"max_count_by_user": 1,
},
}
user = factories.UserFactory()
client = APIClient()
client.force_login(user)
response = client.post(
"/api/v1.0/files/",
{
"type": FileTypeChoices.BACKGROUND_IMAGE,
"filename": "1.png",
},
)
assert response.status_code == 201
response = client.post(
"/api/v1.0/files/",
{
"type": FileTypeChoices.BACKGROUND_IMAGE,
"filename": "2.png",
},
)
assert response.status_code == 403
assert response.json() == {
"detail": "You have reached the maximum number of files for this type."
}
assert File.objects.count() == 1
def test_api_files_create_force_id_success(): def test_api_files_create_force_id_success():
"""It should be possible to force the item ID when creating a item.""" """It should be possible to force the item ID when creating a item."""
user = factories.UserFactory() user = factories.UserFactory()
@@ -39,7 +39,7 @@ def test_api_files_update_anonymous_forbidden():
def test_api_files_update_description_and_title(): def test_api_files_update_description_and_title():
""" """
Test the description and title of a file can be updated. Test the description and title of an file can be updated.
""" """
user = factories.UserFactory() user = factories.UserFactory()
@@ -32,7 +32,7 @@ def valid_minio_event():
"s3": { "s3": {
"bucket": {"name": "test-bucket"}, "bucket": {"name": "test-bucket"},
"object": { "object": {
"key": "recordings%2F46d1a121-2426-484d-8fb3-09b5d886f7a8.ogg", "key": "recording%2F46d1a121-2426-484d-8fb3-09b5d886f7a8.ogg",
"contentType": "audio/ogg", "contentType": "audio/ogg",
}, },
} }
@@ -51,7 +51,7 @@ def test_parse_valid_event(minio_parser, valid_minio_event):
"""Test parsing a valid Minio event.""" """Test parsing a valid Minio event."""
event = minio_parser.parse(valid_minio_event) event = minio_parser.parse(valid_minio_event)
assert isinstance(event, StorageEvent) assert isinstance(event, StorageEvent)
assert event.filepath == "recordings%2F46d1a121-2426-484d-8fb3-09b5d886f7a8.ogg" assert event.filepath == "recording%2F46d1a121-2426-484d-8fb3-09b5d886f7a8.ogg"
assert event.filetype == "audio/ogg" assert event.filetype == "audio/ogg"
assert event.bucket_name == "test-bucket" assert event.bucket_name == "test-bucket"
assert event.metadata is None assert event.metadata is None
@@ -130,13 +130,11 @@ def test_validate_invalid_filetype(minio_parser):
"invalid_filepath", "invalid_filepath",
[ [
"invalid_filepath", # totally invalid string "invalid_filepath", # totally invalid string
"recordings/46d1a121-2426-484d-8fb3-09b5d886f7a8.ogg", "recording/46d1a121-2426-484d-8fb3-09b5d886f7a8.ogg",
"recordings/46d1a121-2426-484d-8fb3-09b5d886f7a8", # missing extension "recording/46d1a121-2426-484d-8fb3-09b5d886f7a8", # missing extension
"46d1a121-2426-484d-8fb3-09b5d886f7a8", # missing url_encoded_folder_path and extension "46d1a121-2426-484d-8fb3-09b5d886f7a8", # missing url_encoded_folder_path and extension
"", # empty string "", # empty string
"46d1a121-2426-484d-8fb3-09b5d886f7a8.ogg", # no folder at all "recording%2F46d1a1212426484d8fb309b5d886f7a8.ogg",
"uploads%2F46d1a121-2426-484d-8fb3-09b5d886f7a8.ogg", # wrong folder name
"folder%2Fuploads%2F46d1a121-2426-484d-8fb3-09b5d886f7a8.ogg", # nested but no recordings/
], ],
) )
def test_validate_invalid_filepath(invalid_filepath, minio_parser): def test_validate_invalid_filepath(invalid_filepath, minio_parser):
@@ -154,7 +152,7 @@ def test_validate_invalid_filepath(invalid_filepath, minio_parser):
def test_validate_valid_event(minio_parser): def test_validate_valid_event(minio_parser):
"""Test validation with valid event data.""" """Test validation with valid event data."""
event = StorageEvent( event = StorageEvent(
filepath="recordings%2F46d1a121-2426-484d-8fb3-09b5d886f7a8.ogg", filepath="recording%2F46d1a121-2426-484d-8fb3-09b5d886f7a8.ogg",
filetype="audio/ogg", filetype="audio/ogg",
bucket_name="test-bucket", bucket_name="test-bucket",
metadata=None, metadata=None,
@@ -172,7 +170,7 @@ def test_get_recording_id_success(minio_parser, valid_minio_event):
def test_validate_filepath_with_folder(minio_parser): def test_validate_filepath_with_folder(minio_parser):
"""Test validation of filepath with folder structure.""" """Test validation of filepath with folder structure."""
event = StorageEvent( event = StorageEvent(
filepath="parent_folder%2Frecordings%2F46d1a121-2426-484d-8fb3-09b5d886f7a8.ogg", filepath="parent_folder%2Ffolder%2F46d1a121-2426-484d-8fb3-09b5d886f7a8.ogg",
filetype="audio/ogg", filetype="audio/ogg",
bucket_name="test-bucket", bucket_name="test-bucket",
metadata=None, metadata=None,
@@ -221,7 +219,7 @@ def test_validate_custom_filetypes():
parser = MinioParser(bucket_name="test-bucket", allowed_filetypes={"audio/mp3"}) parser = MinioParser(bucket_name="test-bucket", allowed_filetypes={"audio/mp3"})
event = StorageEvent( event = StorageEvent(
filepath="parent_folder%2Frecordings%2F46d1a121-2426-484d-8fb3-09b5d886f7a8.ogg", filepath="parent_folder%2Ffolder%2F46d1a121-2426-484d-8fb3-09b5d886f7a8.ogg",
filetype="audio/mp3", filetype="audio/mp3",
bucket_name="test-bucket", bucket_name="test-bucket",
metadata=None, metadata=None,
@@ -14,7 +14,6 @@ from ...factories import RecordingFactory
from ...models import Recording, RecordingStatusChoices from ...models import Recording, RecordingStatusChoices
from ...recording.event.exceptions import ( from ...recording.event.exceptions import (
InvalidBucketError, InvalidBucketError,
InvalidFilepathError,
InvalidFileTypeError, InvalidFileTypeError,
ParsingEventDataError, ParsingEventDataError,
) )
@@ -133,28 +132,7 @@ def test_save_recording_filetype_error(recording_settings, mock_get_parser):
) )
assert response.status_code == 200 assert response.status_code == 200
assert response.json() == {"message": "Notification ignored."} assert response.json() == {"message": "Ignore this file type, unsupported '.json'"}
def test_save_recording_filepath_error(recording_settings, mock_get_parser):
"""Test handling of unsupported filepath in recording event data."""
mock_parser = mock.Mock()
mock_parser.get_recording_id.side_effect = InvalidFilepathError(
"Invalid filepath structure: parent/folder/recording.jpeg"
)
mock_get_parser.return_value = mock_parser
client = APIClient()
response = client.post(
"/api/v1.0/recordings/storage-hook/",
{"recording_data": "valid-data"},
HTTP_AUTHORIZATION="Bearer testAuthToken",
)
assert response.status_code == 200
assert response.json() == {"message": "Notification ignored."}
def test_save_recording_unknown_recording(recording_settings, mock_get_parser, client): def test_save_recording_unknown_recording(recording_settings, mock_get_parser, client):
@@ -232,7 +232,7 @@ def test_api_rooms_retrieve_authenticated_public(mock_token):
""" """
room = RoomFactory( room = RoomFactory(
access_level=RoomAccessLevel.PUBLIC, access_level=RoomAccessLevel.PUBLIC,
configuration={"can_publish_sources": ["mock-source"]}, configuration={"can_publish_sources": ["camera"]},
) )
user = UserFactory() user = UserFactory()
@@ -264,7 +264,7 @@ def test_api_rooms_retrieve_authenticated_public(mock_token):
user=user, user=user,
username=None, username=None,
color=None, color=None,
sources=["mock-source"], sources=["camera"],
is_admin_or_owner=False, is_admin_or_owner=False,
participant_id=None, participant_id=None,
) )
@@ -363,7 +363,7 @@ def test_api_rooms_retrieve_members(mock_token, django_assert_num_queries, setti
other_user = UserFactory() other_user = UserFactory()
room = RoomFactory( room = RoomFactory(
configuration={"can_publish_sources": ["mock-source"]}, configuration={"can_publish_sources": ["camera"]},
) )
UserResourceAccessFactory(resource=room, user=user, role="member") UserResourceAccessFactory(resource=room, user=user, role="member")
UserResourceAccessFactory(resource=room, user=other_user, role="member") UserResourceAccessFactory(resource=room, user=other_user, role="member")
@@ -401,7 +401,7 @@ def test_api_rooms_retrieve_members(mock_token, django_assert_num_queries, setti
user=user, user=user,
username=None, username=None,
color=None, color=None,
sources=["mock-source"], sources=["camera"],
is_admin_or_owner=False, is_admin_or_owner=False,
participant_id=None, participant_id=None,
) )
@@ -95,7 +95,7 @@ def test_api_rooms_update_administrators():
"name": "New name", "name": "New name",
"slug": "should-be-ignored", "slug": "should-be-ignored",
"access_level": RoomAccessLevel.PUBLIC, "access_level": RoomAccessLevel.PUBLIC,
"configuration": {"the_key": "the_value"}, "configuration": {"can_publish_sources": ["camera"]},
}, },
format="json", format="json",
) )
@@ -104,7 +104,7 @@ def test_api_rooms_update_administrators():
assert room.name == "New name" assert room.name == "New name"
assert room.slug == "new-name" assert room.slug == "new-name"
assert room.access_level == RoomAccessLevel.PUBLIC assert room.access_level == RoomAccessLevel.PUBLIC
assert room.configuration == {"the_key": "the_value"} assert room.configuration == {"can_publish_sources": ["camera"]}
def test_api_rooms_update_administrators_of_another(): def test_api_rooms_update_administrators_of_another():
@@ -904,7 +904,7 @@ def test_api_rooms_token_unknown_application(settings):
def test_api_rooms_token_inactive_application(settings): def test_api_rooms_token_inactive_application(settings):
"""Token for inactive application should be rejected.""" """Token for inactive application should be rejected."""
application = ApplicationFactory(is_active=False) application = ApplicationFactory(active=False)
now = datetime.now(timezone.utc) now = datetime.now(timezone.utc)
payload = { payload = {
@@ -23,7 +23,7 @@ def test_api_applications_generate_token_success(settings):
"""Valid credentials should return a JWT token.""" """Valid credentials should return a JWT token."""
UserFactory(email="User.Family@example.com") UserFactory(email="User.Family@example.com")
application = ApplicationFactory( application = ApplicationFactory(
is_active=True, active=True,
scopes=[ApplicationScope.ROOMS_LIST, ApplicationScope.ROOMS_CREATE], scopes=[ApplicationScope.ROOMS_LIST, ApplicationScope.ROOMS_CREATE],
) )
@@ -79,7 +79,7 @@ def test_api_applications_generate_token_invalid_client_id():
def test_api_applications_generate_token_invalid_client_secret(): def test_api_applications_generate_token_invalid_client_secret():
"""Invalid client_secret should return 401.""" """Invalid client_secret should return 401."""
user = UserFactory(email="user@example.com") user = UserFactory(email="user@example.com")
application = ApplicationFactory(is_active=True) application = ApplicationFactory(active=True)
client = APIClient() client = APIClient()
response = client.post( response = client.post(
@@ -100,7 +100,7 @@ def test_api_applications_generate_token_invalid_client_secret():
def test_api_applications_generate_token_inactive_application(): def test_api_applications_generate_token_inactive_application():
"""Inactive application should return 401.""" """Inactive application should return 401."""
user = UserFactory(email="user@example.com") user = UserFactory(email="user@example.com")
application = ApplicationFactory(is_active=False) application = ApplicationFactory(active=False)
plain_secret = "test-secret-123" plain_secret = "test-secret-123"
application.client_secret = plain_secret application.client_secret = plain_secret
@@ -122,31 +122,9 @@ def test_api_applications_generate_token_inactive_application():
assert "Application is inactive" in str(response.data) assert "Application is inactive" in str(response.data)
def test_api_applications_generate_token_inactive_application_wrong_secret():
"""An inactive application with a wrong secret should return 401."""
user = UserFactory(email="user@example.com")
application = ApplicationFactory(is_active=False)
client = APIClient()
response = client.post(
"/external-api/v1.0/application/token/",
{
"client_id": application.client_id,
"client_secret": "wrong-secret",
"grant_type": "client_credentials",
"scope": user.email,
},
format="json",
)
assert response.status_code == 401
assert "Invalid credentials" in str(response.data)
assert "inactive" not in str(response.data).lower()
def test_api_applications_generate_token_invalid_email_format(): def test_api_applications_generate_token_invalid_email_format():
"""Invalid email format should return 400.""" """Invalid email format should return 400."""
application = ApplicationFactory(is_active=True) application = ApplicationFactory(active=True)
plain_secret = "test-secret-123" plain_secret = "test-secret-123"
application.client_secret = plain_secret application.client_secret = plain_secret
@@ -171,7 +149,7 @@ def test_api_applications_generate_token_invalid_email_format():
def test_api_applications_generate_token_domain_not_authorized(): def test_api_applications_generate_token_domain_not_authorized():
"""Application without domain authorization should return 403.""" """Application without domain authorization should return 403."""
user = UserFactory(email="user@denied.com") user = UserFactory(email="user@denied.com")
application = ApplicationFactory(is_active=True) application = ApplicationFactory(active=True)
ApplicationDomainFactory(application=application, domain="allowed.com") ApplicationDomainFactory(application=application, domain="allowed.com")
plain_secret = "test-secret-123" plain_secret = "test-secret-123"
@@ -198,7 +176,7 @@ def test_api_applications_generate_token_domain_authorized():
"""Application with domain authorization should succeed.""" """Application with domain authorization should succeed."""
user = UserFactory(email="user@allowed.com") user = UserFactory(email="user@allowed.com")
application = ApplicationFactory( application = ApplicationFactory(
is_active=True, active=True,
scopes=[ApplicationScope.ROOMS_LIST], scopes=[ApplicationScope.ROOMS_LIST],
) )
ApplicationDomainFactory(application=application, domain="allowed.com") ApplicationDomainFactory(application=application, domain="allowed.com")
@@ -225,7 +203,7 @@ def test_api_applications_generate_token_domain_authorized():
def test_api_applications_generate_token_user_not_found(): def test_api_applications_generate_token_user_not_found():
"""Non-existent user should return 404.""" """Non-existent user should return 404."""
application = ApplicationFactory(is_active=True) application = ApplicationFactory(active=True)
plain_secret = "test-secret-123" plain_secret = "test-secret-123"
application.client_secret = plain_secret application.client_secret = plain_secret
@@ -253,7 +231,7 @@ def test_api_applications_token_payload_structure(settings):
user = UserFactory(email="user@example.com") user = UserFactory(email="user@example.com")
application = ApplicationFactory( application = ApplicationFactory(
is_active=True, active=True,
scopes=[ApplicationScope.ROOMS_LIST, ApplicationScope.ROOMS_CREATE], scopes=[ApplicationScope.ROOMS_LIST, ApplicationScope.ROOMS_CREATE],
) )
@@ -306,7 +284,7 @@ def test_api_applications_token_new_user(settings):
assert len(User.objects.all()) == 0 assert len(User.objects.all()) == 0
application = ApplicationFactory( application = ApplicationFactory(
is_active=True, active=True,
scopes=[ApplicationScope.ROOMS_LIST, ApplicationScope.ROOMS_CREATE], scopes=[ApplicationScope.ROOMS_LIST, ApplicationScope.ROOMS_CREATE],
) )
@@ -364,7 +342,7 @@ def test_api_applications_token_existing_user(settings):
assert len(User.objects.all()) == 1 assert len(User.objects.all()) == 1
application = ApplicationFactory( application = ApplicationFactory(
is_active=True, active=True,
scopes=[ApplicationScope.ROOMS_LIST, ApplicationScope.ROOMS_CREATE], scopes=[ApplicationScope.ROOMS_LIST, ApplicationScope.ROOMS_CREATE],
) )
@@ -41,7 +41,7 @@ def test_models_application_name_maxlength():
def test_models_application_active_default(): def test_models_application_active_default():
"""An application should be active by default.""" """An application should be active by default."""
application = Application.objects.create(name="Test App") application = Application.objects.create(name="Test App")
assert application.is_active is True assert application.active is True
def test_models_application_scopes_default(): def test_models_application_scopes_default():
Binary file not shown.
+96 -115
View File
@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-03-12 13:46+0000\n" "POT-Creation-Date: 2026-02-26 17:34+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -29,11 +29,11 @@ msgstr "Berechtigungen"
msgid "Important dates" msgid "Important dates"
msgstr "Wichtige Daten" msgstr "Wichtige Daten"
#: core/admin.py:132 core/admin.py:275 #: core/admin.py:132 core/admin.py:243
msgid "No owner" msgid "No owner"
msgstr "Kein Eigentümer" msgstr "Kein Eigentümer"
#: core/admin.py:135 core/admin.py:278 #: core/admin.py:135 core/admin.py:246
msgid "Multiple owners" msgid "Multiple owners"
msgstr "Mehrere Eigentümer" msgstr "Mehrere Eigentümer"
@@ -61,136 +61,117 @@ msgstr "Benachrichtigungen für %(count)s Aufnahme(n) erfolgreich gesendet."
msgid "Skipped %(count)s expired recording(s)." msgid "Skipped %(count)s expired recording(s)."
msgstr "%(count)s abgelaufene Aufnahme(n) übersprungen." msgstr "%(count)s abgelaufene Aufnahme(n) übersprungen."
#: core/admin.py:200 #: core/admin.py:309
msgid "Mark selected recordings as 'Failed to Stop'"
msgstr "Ausgewählte Aufnahmen als Fehler beim Stoppen markieren"
#: core/admin.py:218
#, python-format
msgid "%(count)s recording(s) successfully marked as 'Failed to Stop'."
msgstr "%(count)s Aufnahme(n) erfolgreich als Fehler beim Stoppen markiert."
#: core/admin.py:226
#, fuzzy, python-format
#| msgid "Skipped %(count)s expired recording(s)."
msgid "Skipped %(count)s recording(s) with an ineligible status."
msgstr "%(count)s abgelaufene Aufnahme(n) übersprungen."
#: core/admin.py:342
msgid "No scopes" msgid "No scopes"
msgstr "Keine Scopes" msgstr "Keine Scopes"
#: core/admin.py:344 #: core/admin.py:311
msgid "Scopes" msgid "Scopes"
msgstr "Scopes" msgstr "Scopes"
#: core/api/filters.py:25 #: core/api/filters.py:24
msgid "Creator is me" msgid "Creator is me"
msgstr "Ersteller bin ich" msgstr "Ersteller bin ich"
#: core/api/serializers.py:88 #: core/api/serializers.py:84
msgid "You must be administrator or owner of a room to add accesses to it." msgid "You must be administrator or owner of a room to add accesses to it."
msgstr "" msgstr ""
"Sie müssen Administrator oder Eigentümer eines Raums sein, um Zugriffe " "Sie müssen Administrator oder Eigentümer eines Raums sein, um Zugriffe "
"hinzuzufügen." "hinzuzufügen."
#: core/api/serializers.py:516 #: core/api/serializers.py:443
msgid "This file extension is not allowed." msgid "This file extension is not allowed."
msgstr "Diese Dateiendung ist nicht erlaubt." msgstr "Diese Dateiendung ist nicht erlaubt."
#: core/api/serializers.py:533 #: core/models.py:35
msgid "You have reached the maximum number of files for this type."
msgstr "Sie haben die maximale Anzahl an Dateien dieses Typs erreicht."
#: core/models.py:37
msgid "Member" msgid "Member"
msgstr "Mitglied" msgstr "Mitglied"
#: core/models.py:38 #: core/models.py:36
msgid "Administrator" msgid "Administrator"
msgstr "Administrator" msgstr "Administrator"
#: core/models.py:39 #: core/models.py:37
msgid "Owner" msgid "Owner"
msgstr "Eigentümer" msgstr "Eigentümer"
#: core/models.py:55 #: core/models.py:53
msgid "Initiated" msgid "Initiated"
msgstr "Gestartet" msgstr "Gestartet"
#: core/models.py:56 #: core/models.py:54
msgid "Active" msgid "Active"
msgstr "Aktiv" msgstr "Aktiv"
#: core/models.py:57 #: core/models.py:55
msgid "Stopped" msgid "Stopped"
msgstr "Beendet" msgstr "Beendet"
#: core/models.py:58 #: core/models.py:56
msgid "Saved" msgid "Saved"
msgstr "Gespeichert" msgstr "Gespeichert"
#: core/models.py:59 #: core/models.py:57
msgid "Aborted" msgid "Aborted"
msgstr "Abgebrochen" msgstr "Abgebrochen"
#: core/models.py:60 #: core/models.py:58
msgid "Failed to Start" msgid "Failed to Start"
msgstr "Start fehlgeschlagen" msgstr "Start fehlgeschlagen"
#: core/models.py:61 #: core/models.py:59
msgid "Failed to Stop" msgid "Failed to Stop"
msgstr "Stopp fehlgeschlagen" msgstr "Stopp fehlgeschlagen"
#: core/models.py:62 #: core/models.py:60
msgid "Notification succeeded" msgid "Notification succeeded"
msgstr "Benachrichtigung erfolgreich" msgstr "Benachrichtigung erfolgreich"
#: core/models.py:89 #: core/models.py:87
msgid "SCREEN_RECORDING" msgid "SCREEN_RECORDING"
msgstr "BILDSCHIRMAUFZEICHNUNG" msgstr "BILDSCHIRMAUFZEICHNUNG"
#: core/models.py:90 #: core/models.py:88
msgid "TRANSCRIPT" msgid "TRANSCRIPT"
msgstr "TRANSKRIPT" msgstr "TRANSKRIPT"
#: core/models.py:96 #: core/models.py:94
msgid "Public Access" msgid "Public Access"
msgstr "Öffentlicher Zugriff" msgstr "Öffentlicher Zugriff"
#: core/models.py:97 #: core/models.py:95
msgid "Trusted Access" msgid "Trusted Access"
msgstr "Vertrauenswürdiger Zugriff" msgstr "Vertrauenswürdiger Zugriff"
#: core/models.py:98 #: core/models.py:96
msgid "Restricted Access" msgid "Restricted Access"
msgstr "Eingeschränkter Zugriff" msgstr "Eingeschränkter Zugriff"
#: core/models.py:110 #: core/models.py:108
msgid "id" msgid "id"
msgstr "ID" msgstr "ID"
#: core/models.py:111 #: core/models.py:109
msgid "primary key for the record as UUID" msgid "primary key for the record as UUID"
msgstr "Primärschlüssel des Eintrags als UUID" msgstr "Primärschlüssel des Eintrags als UUID"
#: core/models.py:117 #: core/models.py:115
msgid "created on" msgid "created on"
msgstr "erstellt am" msgstr "erstellt am"
#: core/models.py:118 #: core/models.py:116
msgid "date and time at which a record was created" msgid "date and time at which a record was created"
msgstr "Datum und Uhrzeit der Erstellung eines Eintrags" msgstr "Datum und Uhrzeit der Erstellung eines Eintrags"
#: core/models.py:123 #: core/models.py:121
msgid "updated on" msgid "updated on"
msgstr "aktualisiert am" msgstr "aktualisiert am"
#: core/models.py:124 #: core/models.py:122
msgid "date and time at which a record was last updated" msgid "date and time at which a record was last updated"
msgstr "Datum und Uhrzeit der letzten Aktualisierung eines Eintrags" msgstr "Datum und Uhrzeit der letzten Aktualisierung eines Eintrags"
#: core/models.py:144 #: core/models.py:142
msgid "" msgid ""
"Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/" "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/"
"_ characters." "_ characters."
@@ -198,11 +179,11 @@ msgstr ""
"Geben Sie einen gültigen Sub ein. Dieser Wert darf nur Buchstaben, Zahlen " "Geben Sie einen gültigen Sub ein. Dieser Wert darf nur Buchstaben, Zahlen "
"und die Zeichen @/./+/-/_ enthalten." "und die Zeichen @/./+/-/_ enthalten."
#: core/models.py:150 #: core/models.py:148
msgid "sub" msgid "sub"
msgstr "Sub" msgstr "Sub"
#: core/models.py:152 #: core/models.py:150
msgid "" msgid ""
"Optional for pending users; required upon account activation. 255 characters " "Optional for pending users; required upon account activation. 255 characters "
"or fewer. Letters, numbers, and @/./+/-/_ characters only." "or fewer. Letters, numbers, and @/./+/-/_ characters only."
@@ -210,55 +191,55 @@ msgstr ""
"Optional für ausstehende Benutzer; erforderlich nach Kontoaktivierung. " "Optional für ausstehende Benutzer; erforderlich nach Kontoaktivierung. "
"Maximal 255 Zeichen. Nur Buchstaben, Zahlen und @/./+/-/_ Zeichen erlaubt." "Maximal 255 Zeichen. Nur Buchstaben, Zahlen und @/./+/-/_ Zeichen erlaubt."
#: core/models.py:161 #: core/models.py:159
msgid "identity email address" msgid "identity email address"
msgstr "Identitäts-E-Mail-Adresse" msgstr "Identitäts-E-Mail-Adresse"
#: core/models.py:166 #: core/models.py:164
msgid "admin email address" msgid "admin email address"
msgstr "Administrator-E-Mail-Adresse" msgstr "Administrator-E-Mail-Adresse"
#: core/models.py:168 #: core/models.py:166
msgid "full name" msgid "full name"
msgstr "Vollständiger Name" msgstr "Vollständiger Name"
#: core/models.py:170 #: core/models.py:168
msgid "short name" msgid "short name"
msgstr "Kurzname" msgstr "Kurzname"
#: core/models.py:176 #: core/models.py:174
msgid "language" msgid "language"
msgstr "Sprache" msgstr "Sprache"
#: core/models.py:177 #: core/models.py:175
msgid "The language in which the user wants to see the interface." msgid "The language in which the user wants to see the interface."
msgstr "Die Sprache, in der der Benutzer die Oberfläche sehen möchte." msgstr "Die Sprache, in der der Benutzer die Oberfläche sehen möchte."
#: core/models.py:183 #: core/models.py:181
msgid "The timezone in which the user wants to see times." msgid "The timezone in which the user wants to see times."
msgstr "Die Zeitzone, in der der Benutzer die Zeiten sehen möchte." msgstr "Die Zeitzone, in der der Benutzer die Zeiten sehen möchte."
#: core/models.py:186 #: core/models.py:184
msgid "device" msgid "device"
msgstr "Gerät" msgstr "Gerät"
#: core/models.py:188 #: core/models.py:186
msgid "Whether the user is a device or a real user." msgid "Whether the user is a device or a real user."
msgstr "Ob es sich um ein Gerät oder einen echten Benutzer handelt." msgstr "Ob es sich um ein Gerät oder einen echten Benutzer handelt."
#: core/models.py:191 #: core/models.py:189
msgid "staff status" msgid "staff status"
msgstr "Mitarbeiterstatus" msgstr "Mitarbeiterstatus"
#: core/models.py:193 #: core/models.py:191
msgid "Whether the user can log into this admin site." msgid "Whether the user can log into this admin site."
msgstr "Ob der Benutzer sich bei dieser Admin-Seite anmelden kann." msgstr "Ob der Benutzer sich bei dieser Admin-Seite anmelden kann."
#: core/models.py:196 #: core/models.py:194
msgid "active" msgid "active"
msgstr "aktiv" msgstr "aktiv"
#: core/models.py:199 #: core/models.py:197
msgid "" msgid ""
"Whether this user should be treated as active. Unselect this instead of " "Whether this user should be treated as active. Unselect this instead of "
"deleting accounts." "deleting accounts."
@@ -266,66 +247,66 @@ msgstr ""
"Ob dieser Benutzer als aktiv behandelt werden soll. Deaktivieren Sie dies " "Ob dieser Benutzer als aktiv behandelt werden soll. Deaktivieren Sie dies "
"anstelle des Löschens des Kontos." "anstelle des Löschens des Kontos."
#: core/models.py:212 #: core/models.py:210
msgid "user" msgid "user"
msgstr "Benutzer" msgstr "Benutzer"
#: core/models.py:213 #: core/models.py:211
msgid "users" msgid "users"
msgstr "Benutzer" msgstr "Benutzer"
#: core/models.py:272 #: core/models.py:270
msgid "Resource" msgid "Resource"
msgstr "Ressource" msgstr "Ressource"
#: core/models.py:273 #: core/models.py:271
msgid "Resources" msgid "Resources"
msgstr "Ressourcen" msgstr "Ressourcen"
#: core/models.py:331 #: core/models.py:329
msgid "Resource access" msgid "Resource access"
msgstr "Ressourcenzugriff" msgstr "Ressourcenzugriff"
#: core/models.py:332 #: core/models.py:330
msgid "Resource accesses" msgid "Resource accesses"
msgstr "Ressourcenzugriffe" msgstr "Ressourcenzugriffe"
#: core/models.py:338 #: core/models.py:336
msgid "Resource access with this User and Resource already exists." msgid "Resource access with this User and Resource already exists."
msgstr "" msgstr ""
"Ein Ressourcenzugriff mit diesem Benutzer und dieser Ressource existiert " "Ein Ressourcenzugriff mit diesem Benutzer und dieser Ressource existiert "
"bereits." "bereits."
#: core/models.py:394 #: core/models.py:392
msgid "Visio room configuration" msgid "Visio room configuration"
msgstr "Visio-Raumkonfiguration" msgstr "Visio-Raumkonfiguration"
#: core/models.py:395 #: core/models.py:393
msgid "Values for Visio parameters to configure the room." msgid "Values for Visio parameters to configure the room."
msgstr "Werte für Visio-Parameter zur Konfiguration des Raums." msgstr "Werte für Visio-Parameter zur Konfiguration des Raums."
#: core/models.py:402 #: core/models.py:400
msgid "Room PIN code" msgid "Room PIN code"
msgstr "PIN-Code für den Raum" msgstr "PIN-Code für den Raum"
#: core/models.py:403 #: core/models.py:401
msgid "Unique n-digit code that identifies this room in telephony mode." msgid "Unique n-digit code that identifies this room in telephony mode."
msgstr "" msgstr ""
"Eindeutiger n-stelliger Code, der diesen Raum im Telephonmodus identifiziert." "Eindeutiger n-stelliger Code, der diesen Raum im Telephonmodus identifiziert."
#: core/models.py:409 core/models.py:563 #: core/models.py:407 core/models.py:561
msgid "Room" msgid "Room"
msgstr "Raum" msgstr "Raum"
#: core/models.py:410 #: core/models.py:408
msgid "Rooms" msgid "Rooms"
msgstr "Räume" msgstr "Räume"
#: core/models.py:574 #: core/models.py:572
msgid "Worker ID" msgid "Worker ID"
msgstr "Worker-ID" msgstr "Worker-ID"
#: core/models.py:576 #: core/models.py:574
msgid "" msgid ""
"Enter an identifier for the worker recording.This ID is retained even when " "Enter an identifier for the worker recording.This ID is retained even when "
"the worker stops, allowing for easy tracking." "the worker stops, allowing for easy tracking."
@@ -334,120 +315,120 @@ msgstr ""
"erhalten, auch wenn der Worker stoppt, was ein einfaches Nachverfolgen " "erhalten, auch wenn der Worker stoppt, was ein einfaches Nachverfolgen "
"ermöglicht." "ermöglicht."
#: core/models.py:584 #: core/models.py:582
msgid "Recording mode" msgid "Recording mode"
msgstr "Aufzeichnungsmodus" msgstr "Aufzeichnungsmodus"
#: core/models.py:585 #: core/models.py:583
msgid "Defines the mode of recording being called." msgid "Defines the mode of recording being called."
msgstr "Definiert den aufgerufenen Aufzeichnungsmodus." msgstr "Definiert den aufgerufenen Aufzeichnungsmodus."
#: core/models.py:590 core/models.py:591 #: core/models.py:588 core/models.py:589
msgid "Recording options" msgid "Recording options"
msgstr "Aufnahmeoptionen" msgstr "Aufnahmeoptionen"
#: core/models.py:597 #: core/models.py:595
msgid "Recording" msgid "Recording"
msgstr "Aufzeichnung" msgstr "Aufzeichnung"
#: core/models.py:598 #: core/models.py:596
msgid "Recordings" msgid "Recordings"
msgstr "Aufzeichnungen" msgstr "Aufzeichnungen"
#: core/models.py:706 #: core/models.py:704
msgid "Recording/user relation" msgid "Recording/user relation"
msgstr "Beziehung Aufzeichnung/Benutzer" msgstr "Beziehung Aufzeichnung/Benutzer"
#: core/models.py:707 #: core/models.py:705
msgid "Recording/user relations" msgid "Recording/user relations"
msgstr "Beziehungen Aufzeichnung/Benutzer" msgstr "Beziehungen Aufzeichnung/Benutzer"
#: core/models.py:713 #: core/models.py:711
msgid "This user is already in this recording." msgid "This user is already in this recording."
msgstr "Dieser Benutzer ist bereits Teil dieser Aufzeichnung." msgstr "Dieser Benutzer ist bereits Teil dieser Aufzeichnung."
#: core/models.py:719 #: core/models.py:717
msgid "This team is already in this recording." msgid "This team is already in this recording."
msgstr "Dieses Team ist bereits Teil dieser Aufzeichnung." msgstr "Dieses Team ist bereits Teil dieser Aufzeichnung."
#: core/models.py:725 #: core/models.py:723
msgid "Either user or team must be set, not both." msgid "Either user or team must be set, not both."
msgstr "Entweder Benutzer oder Team muss festgelegt werden, nicht beides." msgstr "Entweder Benutzer oder Team muss festgelegt werden, nicht beides."
#: core/models.py:742 #: core/models.py:740
msgid "Create rooms" msgid "Create rooms"
msgstr "Räume erstellen" msgstr "Räume erstellen"
#: core/models.py:743 #: core/models.py:741
msgid "List rooms" msgid "List rooms"
msgstr "Räume auflisten" msgstr "Räume auflisten"
#: core/models.py:744 #: core/models.py:742
msgid "Retrieve room details" msgid "Retrieve room details"
msgstr "Raumdetails abrufen" msgstr "Raumdetails abrufen"
#: core/models.py:745 #: core/models.py:743
msgid "Update rooms" msgid "Update rooms"
msgstr "Räume aktualisieren" msgstr "Räume aktualisieren"
#: core/models.py:746 #: core/models.py:744
msgid "Delete rooms" msgid "Delete rooms"
msgstr "Räume löschen" msgstr "Räume löschen"
#: core/models.py:759 #: core/models.py:757
msgid "Application name" msgid "Application name"
msgstr "Anwendungsname" msgstr "Anwendungsname"
#: core/models.py:760 #: core/models.py:758
msgid "Descriptive name for this application." msgid "Descriptive name for this application."
msgstr "Beschreibender Name für diese Anwendung." msgstr "Beschreibender Name für diese Anwendung."
#: core/models.py:770 #: core/models.py:768
msgid "Hashed on Save. Copy it now if this is a new secret." msgid "Hashed on Save. Copy it now if this is a new secret."
msgstr "" msgstr ""
"Beim Speichern gehasht. Jetzt kopieren, wenn dies ein neues Geheimnis ist." "Beim Speichern gehasht. Jetzt kopieren, wenn dies ein neues Geheimnis ist."
#: core/models.py:781 #: core/models.py:779
msgid "Application" msgid "Application"
msgstr "Anwendung" msgstr "Anwendung"
#: core/models.py:782 #: core/models.py:780
msgid "Applications" msgid "Applications"
msgstr "Anwendungen" msgstr "Anwendungen"
#: core/models.py:805 #: core/models.py:803
msgid "Enter a valid domain" msgid "Enter a valid domain"
msgstr "Geben Sie eine gültige Domain ein" msgstr "Geben Sie eine gültige Domain ein"
#: core/models.py:808 #: core/models.py:806
msgid "Domain" msgid "Domain"
msgstr "Domain" msgstr "Domain"
#: core/models.py:809 #: core/models.py:807
msgid "Email domain this application can act on behalf of." msgid "Email domain this application can act on behalf of."
msgstr "E-Mail-Domain, im Namen der diese Anwendung handeln kann." msgstr "E-Mail-Domain, im Namen der diese Anwendung handeln kann."
#: core/models.py:821 #: core/models.py:819
msgid "Application domain" msgid "Application domain"
msgstr "Anwendungsdomain" msgstr "Anwendungsdomain"
#: core/models.py:822 #: core/models.py:820
msgid "Application domains" msgid "Application domains"
msgstr "Anwendungsdomains" msgstr "Anwendungsdomains"
#: core/models.py:840 #: core/models.py:838
msgid "Pending" msgid "Pending"
msgstr "Ausstehend" msgstr "Ausstehend"
#: core/models.py:848 #: core/models.py:846
msgid "Ready" msgid "Ready"
msgstr "Bereit" msgstr "Bereit"
#: core/models.py:854 #: core/models.py:852
msgid "Background image" msgid "Background image"
msgstr "Hintergrundbild" msgstr "Hintergrundbild"
#: core/models.py:866 #: core/models.py:864
msgid "title" msgid "title"
msgstr "Titel" msgstr "Titel"
@@ -464,11 +445,11 @@ msgstr "Datei"
msgid "Files" msgid "Files"
msgstr "Dateien" msgstr "Dateien"
#: core/models.py:1000 #: core/models.py:970
msgid "This file is already hard deleted." msgid "This file is already hard deleted."
msgstr "Diese Datei wurde bereits endgültig gelöscht." msgstr "Diese Datei wurde bereits endgültig gelöscht."
#: core/models.py:1010 #: core/models.py:980
#, fuzzy #, fuzzy
#| msgid "To hard delete a file, it must first be soft deleted." #| msgid "To hard delete a file, it must first be soft deleted."
msgid "To hard delete a file, it must first be soft deleted." msgid "To hard delete a file, it must first be soft deleted."
@@ -604,18 +585,18 @@ msgstr ""
" Wenn Sie Fragen haben oder Unterstützung benötigen, wenden Sie sich bitte " " Wenn Sie Fragen haben oder Unterstützung benötigen, wenden Sie sich bitte "
"an unser Support-Team unter %(support_email)s. " "an unser Support-Team unter %(support_email)s. "
#: meet/settings.py:223 #: meet/settings.py:224
msgid "English" msgid "English"
msgstr "Englisch" msgstr "Englisch"
#: meet/settings.py:224 #: meet/settings.py:225
msgid "French" msgid "French"
msgstr "Französisch" msgstr "Französisch"
#: meet/settings.py:225 #: meet/settings.py:226
msgid "Dutch" msgid "Dutch"
msgstr "Niederländisch" msgstr "Niederländisch"
#: meet/settings.py:226 #: meet/settings.py:227
msgid "German" msgid "German"
msgstr "Deutsch" msgstr "Deutsch"
Binary file not shown.
+96 -115
View File
@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-03-12 13:46+0000\n" "POT-Creation-Date: 2026-02-26 17:26+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -29,11 +29,11 @@ msgstr "Permissions"
msgid "Important dates" msgid "Important dates"
msgstr "Important dates" msgstr "Important dates"
#: core/admin.py:132 core/admin.py:275 #: core/admin.py:132 core/admin.py:243
msgid "No owner" msgid "No owner"
msgstr "No owner" msgstr "No owner"
#: core/admin.py:135 core/admin.py:278 #: core/admin.py:135 core/admin.py:246
msgid "Multiple owners" msgid "Multiple owners"
msgstr "Multiple owners" msgstr "Multiple owners"
@@ -61,134 +61,115 @@ msgstr "Successfully sent notifications for %(count)s recording(s)."
msgid "Skipped %(count)s expired recording(s)." msgid "Skipped %(count)s expired recording(s)."
msgstr "Skipped %(count)s expired recording(s)." msgstr "Skipped %(count)s expired recording(s)."
#: core/admin.py:200 #: core/admin.py:309
msgid "Mark selected recordings as 'Failed to Stop'"
msgstr "Mark selected recordings as 'Failed to Stop'"
#: core/admin.py:218
#, python-format
msgid "%(count)s recording(s) successfully marked as 'Failed to Stop'."
msgstr "%(count)s recording(s) successfully marked as 'Failed to Stop'."
#: core/admin.py:226
#, fuzzy, python-format
#| msgid "Skipped %(count)s expired recording(s)."
msgid "Skipped %(count)s recording(s) with an ineligible status."
msgstr "Skipped %(count)s expired recording(s)."
#: core/admin.py:342
msgid "No scopes" msgid "No scopes"
msgstr "No scopes" msgstr "No scopes"
#: core/admin.py:344 #: core/admin.py:311
msgid "Scopes" msgid "Scopes"
msgstr "Scopes" msgstr "Scopes"
#: core/api/filters.py:25 #: core/api/filters.py:24
msgid "Creator is me" msgid "Creator is me"
msgstr "Creator is me" msgstr "Creator is me"
#: core/api/serializers.py:88 #: core/api/serializers.py:84
msgid "You must be administrator or owner of a room to add accesses to it." msgid "You must be administrator or owner of a room to add accesses to it."
msgstr "You must be administrator or owner of a room to add accesses to it." msgstr "You must be administrator or owner of a room to add accesses to it."
#: core/api/serializers.py:516 #: core/api/serializers.py:443
msgid "This file extension is not allowed." msgid "This file extension is not allowed."
msgstr "This file extension is not allowed." msgstr "This file extension is not allowed."
#: core/api/serializers.py:533 #: core/models.py:35
msgid "You have reached the maximum number of files for this type."
msgstr "You have reached the maximum number of files for this type."
#: core/models.py:37
msgid "Member" msgid "Member"
msgstr "Member" msgstr "Member"
#: core/models.py:38 #: core/models.py:36
msgid "Administrator" msgid "Administrator"
msgstr "Administrator" msgstr "Administrator"
#: core/models.py:39 #: core/models.py:37
msgid "Owner" msgid "Owner"
msgstr "Owner" msgstr "Owner"
#: core/models.py:55 #: core/models.py:53
msgid "Initiated" msgid "Initiated"
msgstr "Initiated" msgstr "Initiated"
#: core/models.py:56 #: core/models.py:54
msgid "Active" msgid "Active"
msgstr "Active" msgstr "Active"
#: core/models.py:57 #: core/models.py:55
msgid "Stopped" msgid "Stopped"
msgstr "Stopped" msgstr "Stopped"
#: core/models.py:58 #: core/models.py:56
msgid "Saved" msgid "Saved"
msgstr "Saved" msgstr "Saved"
#: core/models.py:59 #: core/models.py:57
msgid "Aborted" msgid "Aborted"
msgstr "Aborted" msgstr "Aborted"
#: core/models.py:60 #: core/models.py:58
msgid "Failed to Start" msgid "Failed to Start"
msgstr "Failed to Start" msgstr "Failed to Start"
#: core/models.py:61 #: core/models.py:59
msgid "Failed to Stop" msgid "Failed to Stop"
msgstr "Failed to Stop" msgstr "Failed to Stop"
#: core/models.py:62 #: core/models.py:60
msgid "Notification succeeded" msgid "Notification succeeded"
msgstr "Notification succeeded" msgstr "Notification succeeded"
#: core/models.py:89 #: core/models.py:87
msgid "SCREEN_RECORDING" msgid "SCREEN_RECORDING"
msgstr "SCREEN_RECORDING" msgstr "SCREEN_RECORDING"
#: core/models.py:90 #: core/models.py:88
msgid "TRANSCRIPT" msgid "TRANSCRIPT"
msgstr "TRANSCRIPT" msgstr "TRANSCRIPT"
#: core/models.py:96 #: core/models.py:94
msgid "Public Access" msgid "Public Access"
msgstr "Public Access" msgstr "Public Access"
#: core/models.py:97 #: core/models.py:95
msgid "Trusted Access" msgid "Trusted Access"
msgstr "Trusted Access" msgstr "Trusted Access"
#: core/models.py:98 #: core/models.py:96
msgid "Restricted Access" msgid "Restricted Access"
msgstr "Restricted Access" msgstr "Restricted Access"
#: core/models.py:110 #: core/models.py:108
msgid "id" msgid "id"
msgstr "id" msgstr "id"
#: core/models.py:111 #: core/models.py:109
msgid "primary key for the record as UUID" msgid "primary key for the record as UUID"
msgstr "primary key for the record as UUID" msgstr "primary key for the record as UUID"
#: core/models.py:117 #: core/models.py:115
msgid "created on" msgid "created on"
msgstr "created on" msgstr "created on"
#: core/models.py:118 #: core/models.py:116
msgid "date and time at which a record was created" msgid "date and time at which a record was created"
msgstr "date and time at which a record was created" msgstr "date and time at which a record was created"
#: core/models.py:123 #: core/models.py:121
msgid "updated on" msgid "updated on"
msgstr "updated on" msgstr "updated on"
#: core/models.py:124 #: core/models.py:122
msgid "date and time at which a record was last updated" msgid "date and time at which a record was last updated"
msgstr "date and time at which a record was last updated" msgstr "date and time at which a record was last updated"
#: core/models.py:144 #: core/models.py:142
msgid "" msgid ""
"Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/" "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/"
"_ characters." "_ characters."
@@ -196,11 +177,11 @@ msgstr ""
"Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/" "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/"
"_ characters." "_ characters."
#: core/models.py:150 #: core/models.py:148
msgid "sub" msgid "sub"
msgstr "sub" msgstr "sub"
#: core/models.py:152 #: core/models.py:150
msgid "" msgid ""
"Optional for pending users; required upon account activation. 255 characters " "Optional for pending users; required upon account activation. 255 characters "
"or fewer. Letters, numbers, and @/./+/-/_ characters only." "or fewer. Letters, numbers, and @/./+/-/_ characters only."
@@ -208,55 +189,55 @@ msgstr ""
"Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_ " "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_ "
"characters only." "characters only."
#: core/models.py:161 #: core/models.py:159
msgid "identity email address" msgid "identity email address"
msgstr "identity email address" msgstr "identity email address"
#: core/models.py:166 #: core/models.py:164
msgid "admin email address" msgid "admin email address"
msgstr "admin email address" msgstr "admin email address"
#: core/models.py:168 #: core/models.py:166
msgid "full name" msgid "full name"
msgstr "full name" msgstr "full name"
#: core/models.py:170 #: core/models.py:168
msgid "short name" msgid "short name"
msgstr "short name" msgstr "short name"
#: core/models.py:176 #: core/models.py:174
msgid "language" msgid "language"
msgstr "language" msgstr "language"
#: core/models.py:177 #: core/models.py:175
msgid "The language in which the user wants to see the interface." msgid "The language in which the user wants to see the interface."
msgstr "The language in which the user wants to see the interface." msgstr "The language in which the user wants to see the interface."
#: core/models.py:183 #: core/models.py:181
msgid "The timezone in which the user wants to see times." msgid "The timezone in which the user wants to see times."
msgstr "The timezone in which the user wants to see times." msgstr "The timezone in which the user wants to see times."
#: core/models.py:186 #: core/models.py:184
msgid "device" msgid "device"
msgstr "device" msgstr "device"
#: core/models.py:188 #: core/models.py:186
msgid "Whether the user is a device or a real user." msgid "Whether the user is a device or a real user."
msgstr "Whether the user is a device or a real user." msgstr "Whether the user is a device or a real user."
#: core/models.py:191 #: core/models.py:189
msgid "staff status" msgid "staff status"
msgstr "staff status" msgstr "staff status"
#: core/models.py:193 #: core/models.py:191
msgid "Whether the user can log into this admin site." msgid "Whether the user can log into this admin site."
msgstr "Whether the user can log into this admin site." msgstr "Whether the user can log into this admin site."
#: core/models.py:196 #: core/models.py:194
msgid "active" msgid "active"
msgstr "active" msgstr "active"
#: core/models.py:199 #: core/models.py:197
msgid "" msgid ""
"Whether this user should be treated as active. Unselect this instead of " "Whether this user should be treated as active. Unselect this instead of "
"deleting accounts." "deleting accounts."
@@ -264,63 +245,63 @@ msgstr ""
"Whether this user should be treated as active. Unselect this instead of " "Whether this user should be treated as active. Unselect this instead of "
"deleting accounts." "deleting accounts."
#: core/models.py:212 #: core/models.py:210
msgid "user" msgid "user"
msgstr "user" msgstr "user"
#: core/models.py:213 #: core/models.py:211
msgid "users" msgid "users"
msgstr "users" msgstr "users"
#: core/models.py:272 #: core/models.py:270
msgid "Resource" msgid "Resource"
msgstr "Resource" msgstr "Resource"
#: core/models.py:273 #: core/models.py:271
msgid "Resources" msgid "Resources"
msgstr "Resources" msgstr "Resources"
#: core/models.py:331 #: core/models.py:329
msgid "Resource access" msgid "Resource access"
msgstr "Resource access" msgstr "Resource access"
#: core/models.py:332 #: core/models.py:330
msgid "Resource accesses" msgid "Resource accesses"
msgstr "Resource accesses" msgstr "Resource accesses"
#: core/models.py:338 #: core/models.py:336
msgid "Resource access with this User and Resource already exists." msgid "Resource access with this User and Resource already exists."
msgstr "Resource access with this User and Resource already exists." msgstr "Resource access with this User and Resource already exists."
#: core/models.py:394 #: core/models.py:392
msgid "Visio room configuration" msgid "Visio room configuration"
msgstr "Visio room configuration" msgstr "Visio room configuration"
#: core/models.py:395 #: core/models.py:393
msgid "Values for Visio parameters to configure the room." msgid "Values for Visio parameters to configure the room."
msgstr "Values for Visio parameters to configure the room." msgstr "Values for Visio parameters to configure the room."
#: core/models.py:402 #: core/models.py:400
msgid "Room PIN code" msgid "Room PIN code"
msgstr "Room PIN code" msgstr "Room PIN code"
#: core/models.py:403 #: core/models.py:401
msgid "Unique n-digit code that identifies this room in telephony mode." msgid "Unique n-digit code that identifies this room in telephony mode."
msgstr "Unique n-digit code that identifies this room in telephony mode." msgstr "Unique n-digit code that identifies this room in telephony mode."
#: core/models.py:409 core/models.py:563 #: core/models.py:407 core/models.py:561
msgid "Room" msgid "Room"
msgstr "Room" msgstr "Room"
#: core/models.py:410 #: core/models.py:408
msgid "Rooms" msgid "Rooms"
msgstr "Rooms" msgstr "Rooms"
#: core/models.py:574 #: core/models.py:572
msgid "Worker ID" msgid "Worker ID"
msgstr "Worker ID" msgstr "Worker ID"
#: core/models.py:576 #: core/models.py:574
msgid "" msgid ""
"Enter an identifier for the worker recording.This ID is retained even when " "Enter an identifier for the worker recording.This ID is retained even when "
"the worker stops, allowing for easy tracking." "the worker stops, allowing for easy tracking."
@@ -328,125 +309,125 @@ msgstr ""
"Enter an identifier for the worker recording.This ID is retained even when " "Enter an identifier for the worker recording.This ID is retained even when "
"the worker stops, allowing for easy tracking." "the worker stops, allowing for easy tracking."
#: core/models.py:584 #: core/models.py:582
msgid "Recording mode" msgid "Recording mode"
msgstr "Recording mode" msgstr "Recording mode"
#: core/models.py:585 #: core/models.py:583
msgid "Defines the mode of recording being called." msgid "Defines the mode of recording being called."
msgstr "Defines the mode of recording being called." msgstr "Defines the mode of recording being called."
#: core/models.py:590 core/models.py:591 #: core/models.py:588 core/models.py:589
msgid "Recording options" msgid "Recording options"
msgstr "Recording options" msgstr "Recording options"
#: core/models.py:597 #: core/models.py:595
msgid "Recording" msgid "Recording"
msgstr "Recording" msgstr "Recording"
#: core/models.py:598 #: core/models.py:596
msgid "Recordings" msgid "Recordings"
msgstr "Recordings" msgstr "Recordings"
#: core/models.py:706 #: core/models.py:704
msgid "Recording/user relation" msgid "Recording/user relation"
msgstr "Recording/user relation" msgstr "Recording/user relation"
#: core/models.py:707 #: core/models.py:705
msgid "Recording/user relations" msgid "Recording/user relations"
msgstr "Recording/user relations" msgstr "Recording/user relations"
#: core/models.py:713 #: core/models.py:711
msgid "This user is already in this recording." msgid "This user is already in this recording."
msgstr "This user is already in this recording." msgstr "This user is already in this recording."
#: core/models.py:719 #: core/models.py:717
msgid "This team is already in this recording." msgid "This team is already in this recording."
msgstr "This team is already in this recording." msgstr "This team is already in this recording."
#: core/models.py:725 #: core/models.py:723
msgid "Either user or team must be set, not both." msgid "Either user or team must be set, not both."
msgstr "Either user or team must be set, not both." msgstr "Either user or team must be set, not both."
#: core/models.py:742 #: core/models.py:740
#, fuzzy #, fuzzy
#| msgid "created on" #| msgid "created on"
msgid "Create rooms" msgid "Create rooms"
msgstr "Create rooms" msgstr "Create rooms"
#: core/models.py:743 #: core/models.py:741
msgid "List rooms" msgid "List rooms"
msgstr "List rooms" msgstr "List rooms"
#: core/models.py:744 #: core/models.py:742
msgid "Retrieve room details" msgid "Retrieve room details"
msgstr "Retrieve room details" msgstr "Retrieve room details"
#: core/models.py:745 #: core/models.py:743
#, fuzzy #, fuzzy
#| msgid "updated on" #| msgid "updated on"
msgid "Update rooms" msgid "Update rooms"
msgstr "Update rooms" msgstr "Update rooms"
#: core/models.py:746 #: core/models.py:744
msgid "Delete rooms" msgid "Delete rooms"
msgstr "Delete rooms" msgstr "Delete rooms"
#: core/models.py:759 #: core/models.py:757
msgid "Application name" msgid "Application name"
msgstr "Application name" msgstr "Application name"
#: core/models.py:760 #: core/models.py:758
msgid "Descriptive name for this application." msgid "Descriptive name for this application."
msgstr "Descriptive name for this application." msgstr "Descriptive name for this application."
#: core/models.py:770 #: core/models.py:768
msgid "Hashed on Save. Copy it now if this is a new secret." msgid "Hashed on Save. Copy it now if this is a new secret."
msgstr "Hashed on Save. Copy it now if this is a new secret." msgstr "Hashed on Save. Copy it now if this is a new secret."
#: core/models.py:781 #: core/models.py:779
msgid "Application" msgid "Application"
msgstr "Application" msgstr "Application"
#: core/models.py:782 #: core/models.py:780
msgid "Applications" msgid "Applications"
msgstr "Applications" msgstr "Applications"
#: core/models.py:805 #: core/models.py:803
msgid "Enter a valid domain" msgid "Enter a valid domain"
msgstr "Enter a valid domain" msgstr "Enter a valid domain"
#: core/models.py:808 #: core/models.py:806
msgid "Domain" msgid "Domain"
msgstr "Domain" msgstr "Domain"
#: core/models.py:809 #: core/models.py:807
msgid "Email domain this application can act on behalf of." msgid "Email domain this application can act on behalf of."
msgstr "Email domain this application can act on behalf of." msgstr "Email domain this application can act on behalf of."
#: core/models.py:821 #: core/models.py:819
msgid "Application domain" msgid "Application domain"
msgstr "Application domain" msgstr "Application domain"
#: core/models.py:822 #: core/models.py:820
msgid "Application domains" msgid "Application domains"
msgstr "Application domains" msgstr "Application domains"
#: core/models.py:840 #: core/models.py:838
#, fuzzy #, fuzzy
#| msgid "Recording" #| msgid "Recording"
msgid "Pending" msgid "Pending"
msgstr "Pending" msgstr "Pending"
#: core/models.py:848 #: core/models.py:846
msgid "Ready" msgid "Ready"
msgstr "Ready" msgstr "Ready"
#: core/models.py:854 #: core/models.py:852
msgid "Background image" msgid "Background image"
msgstr "Background image" msgstr "Background image"
#: core/models.py:866 #: core/models.py:864
msgid "title" msgid "title"
msgstr "title" msgstr "title"
@@ -462,13 +443,13 @@ msgstr "File"
msgid "Files" msgid "Files"
msgstr "Files" msgstr "Files"
#: core/models.py:1000 #: core/models.py:970
#, fuzzy #, fuzzy
#| msgid "This user is already in this recording." #| msgid "This user is already in this recording."
msgid "This file is already hard deleted." msgid "This file is already hard deleted."
msgstr "This file is already hard deleted." msgstr "This file is already hard deleted."
#: core/models.py:1010 #: core/models.py:980
msgid "To hard delete a file, it must first be soft deleted." msgid "To hard delete a file, it must first be soft deleted."
msgstr "To hard delete a file, it must first be soft deleted." msgstr "To hard delete a file, it must first be soft deleted."
@@ -600,18 +581,18 @@ msgstr ""
" If you have any questions or need assistance, please contact our support " " If you have any questions or need assistance, please contact our support "
"team at %(support_email)s. " "team at %(support_email)s. "
#: meet/settings.py:223 #: meet/settings.py:224
msgid "English" msgid "English"
msgstr "English" msgstr "English"
#: meet/settings.py:224 #: meet/settings.py:225
msgid "French" msgid "French"
msgstr "French" msgstr "French"
#: meet/settings.py:225 #: meet/settings.py:226
msgid "Dutch" msgid "Dutch"
msgstr "Dutch" msgstr "Dutch"
#: meet/settings.py:226 #: meet/settings.py:227
msgid "German" msgid "German"
msgstr "German" msgstr "German"
Binary file not shown.
+98 -121
View File
@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-03-12 13:46+0000\n" "POT-Creation-Date: 2026-02-26 17:26+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: antoine.lebaud@mail.numerique.gouv.fr\n" "Last-Translator: antoine.lebaud@mail.numerique.gouv.fr\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -29,11 +29,11 @@ msgstr "Permissions"
msgid "Important dates" msgid "Important dates"
msgstr "Dates importantes" msgstr "Dates importantes"
#: core/admin.py:132 core/admin.py:275 #: core/admin.py:132 core/admin.py:243
msgid "No owner" msgid "No owner"
msgstr "Pas de propriétaire" msgstr "Pas de propriétaire"
#: core/admin.py:135 core/admin.py:278 #: core/admin.py:135 core/admin.py:246
msgid "Multiple owners" msgid "Multiple owners"
msgstr "Plusieurs propriétaires" msgstr "Plusieurs propriétaires"
@@ -61,139 +61,119 @@ msgstr "Notifications envoyées avec succès pour %(count)s enregistrement(s)."
msgid "Skipped %(count)s expired recording(s)." msgid "Skipped %(count)s expired recording(s)."
msgstr "%(count)s enregistrement(s) expiré(s) ignoré(s)." msgstr "%(count)s enregistrement(s) expiré(s) ignoré(s)."
#: core/admin.py:200 #: core/admin.py:309
msgid "Mark selected recordings as 'Failed to Stop'"
msgstr "Marquer les enregistrements sélectionnés comme « Échec darrêt »"
#: core/admin.py:218
#, python-format
msgid "%(count)s recording(s) successfully marked as 'Failed to Stop'."
msgstr ""
"%(count)s enregistrement(s) marqué(s) avec succès comme « Échec darrêt »."
#: core/admin.py:226
#, fuzzy, python-format
#| msgid "Skipped %(count)s expired recording(s)."
msgid "Skipped %(count)s recording(s) with an ineligible status."
msgstr "%(count)s enregistrement(s) avec un statut inéligible ignoré(s)."
#: core/admin.py:342
msgid "No scopes" msgid "No scopes"
msgstr "Aucun scopes" msgstr "Aucun scopes"
#: core/admin.py:344 #: core/admin.py:311
msgid "Scopes" msgid "Scopes"
msgstr "Scopes" msgstr "Scopes"
#: core/api/filters.py:25 #: core/api/filters.py:24
msgid "Creator is me" msgid "Creator is me"
msgstr "Je suis le créateur" msgstr "Je suis le créateur"
#: core/api/serializers.py:88 #: core/api/serializers.py:84
msgid "You must be administrator or owner of a room to add accesses to it." msgid "You must be administrator or owner of a room to add accesses to it."
msgstr "" msgstr ""
"Vous devez être administrateur ou propriétaire d'une salle pour y ajouter " "Vous devez être administrateur ou propriétaire d'une salle pour y ajouter "
"des accès." "des accès."
#: core/api/serializers.py:516 #: core/api/serializers.py:443
msgid "This file extension is not allowed." msgid "This file extension is not allowed."
msgstr "Cette extension n'est pas autorisée" msgstr "Cette extension n'est pas autorisée"
#: core/api/serializers.py:533 #: core/models.py:35
msgid "You have reached the maximum number of files for this type."
msgstr "Vous avez atteint le nombre maximum de fichiers de ce type"
#: core/models.py:37
msgid "Member" msgid "Member"
msgstr "Membre" msgstr "Membre"
#: core/models.py:38 #: core/models.py:36
msgid "Administrator" msgid "Administrator"
msgstr "Administrateur" msgstr "Administrateur"
#: core/models.py:39 #: core/models.py:37
msgid "Owner" msgid "Owner"
msgstr "Propriétaire" msgstr "Propriétaire"
#: core/models.py:55 #: core/models.py:53
msgid "Initiated" msgid "Initiated"
msgstr "Initié" msgstr "Initié"
#: core/models.py:56 #: core/models.py:54
msgid "Active" msgid "Active"
msgstr "Actif" msgstr "Actif"
#: core/models.py:57 #: core/models.py:55
msgid "Stopped" msgid "Stopped"
msgstr "Arrêté" msgstr "Arrêté"
#: core/models.py:58 #: core/models.py:56
msgid "Saved" msgid "Saved"
msgstr "Enregistré" msgstr "Enregistré"
#: core/models.py:59 #: core/models.py:57
msgid "Aborted" msgid "Aborted"
msgstr "Abandonné" msgstr "Abandonné"
#: core/models.py:60 #: core/models.py:58
msgid "Failed to Start" msgid "Failed to Start"
msgstr "Échec au démarrage" msgstr "Échec au démarrage"
#: core/models.py:61 #: core/models.py:59
msgid "Failed to Stop" msgid "Failed to Stop"
msgstr "Échec à l'arrêt" msgstr "Échec à l'arrêt"
#: core/models.py:62 #: core/models.py:60
msgid "Notification succeeded" msgid "Notification succeeded"
msgstr "Notification réussie" msgstr "Notification réussie"
#: core/models.py:89 #: core/models.py:87
msgid "SCREEN_RECORDING" msgid "SCREEN_RECORDING"
msgstr "ENREGISTREMENT_ÉCRAN" msgstr "ENREGISTREMENT_ÉCRAN"
#: core/models.py:90 #: core/models.py:88
msgid "TRANSCRIPT" msgid "TRANSCRIPT"
msgstr "TRANSCRIPTION" msgstr "TRANSCRIPTION"
#: core/models.py:96 #: core/models.py:94
msgid "Public Access" msgid "Public Access"
msgstr "Accès public" msgstr "Accès public"
#: core/models.py:97 #: core/models.py:95
msgid "Trusted Access" msgid "Trusted Access"
msgstr "Accès de confiance" msgstr "Accès de confiance"
#: core/models.py:98 #: core/models.py:96
msgid "Restricted Access" msgid "Restricted Access"
msgstr "Accès restreint" msgstr "Accès restreint"
#: core/models.py:110 #: core/models.py:108
msgid "id" msgid "id"
msgstr "id" msgstr "id"
#: core/models.py:111 #: core/models.py:109
msgid "primary key for the record as UUID" msgid "primary key for the record as UUID"
msgstr "clé primaire pour l'enregistrement sous forme d'UUID" msgstr "clé primaire pour l'enregistrement sous forme d'UUID"
#: core/models.py:117 #: core/models.py:115
msgid "created on" msgid "created on"
msgstr "créé le" msgstr "créé le"
#: core/models.py:118 #: core/models.py:116
msgid "date and time at which a record was created" msgid "date and time at which a record was created"
msgstr "date et heure auxquelles un enregistrement a été créé" msgstr "date et heure auxquelles un enregistrement a été créé"
#: core/models.py:123 #: core/models.py:121
msgid "updated on" msgid "updated on"
msgstr "mis à jour le" msgstr "mis à jour le"
#: core/models.py:124 #: core/models.py:122
msgid "date and time at which a record was last updated" msgid "date and time at which a record was last updated"
msgstr "" msgstr ""
"date et heure auxquelles un enregistrement a été mis à jour pour la dernière " "date et heure auxquelles un enregistrement a été mis à jour pour la dernière "
"fois" "fois"
#: core/models.py:144 #: core/models.py:142
msgid "" msgid ""
"Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/" "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/"
"_ characters." "_ characters."
@@ -201,11 +181,11 @@ msgstr ""
"Entrez un sub valide. Cette valeur ne peut contenir que des lettres, des " "Entrez un sub valide. Cette valeur ne peut contenir que des lettres, des "
"chiffres et les caractères @/./+/-/_." "chiffres et les caractères @/./+/-/_."
#: core/models.py:150 #: core/models.py:148
msgid "sub" msgid "sub"
msgstr "sub" msgstr "sub"
#: core/models.py:152 #: core/models.py:150
msgid "" msgid ""
"Optional for pending users; required upon account activation. 255 characters " "Optional for pending users; required upon account activation. 255 characters "
"or fewer. Letters, numbers, and @/./+/-/_ characters only." "or fewer. Letters, numbers, and @/./+/-/_ characters only."
@@ -213,55 +193,55 @@ msgstr ""
"Optionnel pour les utilisateurs en attente ; requis lors de l'activation du " "Optionnel pour les utilisateurs en attente ; requis lors de l'activation du "
"compte. 255 caractères maximum. Lettres, chiffres et @/./+/-/_ uniquement." "compte. 255 caractères maximum. Lettres, chiffres et @/./+/-/_ uniquement."
#: core/models.py:161 #: core/models.py:159
msgid "identity email address" msgid "identity email address"
msgstr "adresse e-mail d'identité" msgstr "adresse e-mail d'identité"
#: core/models.py:166 #: core/models.py:164
msgid "admin email address" msgid "admin email address"
msgstr "adresse e-mail d'administrateur" msgstr "adresse e-mail d'administrateur"
#: core/models.py:168 #: core/models.py:166
msgid "full name" msgid "full name"
msgstr "nom complet" msgstr "nom complet"
#: core/models.py:170 #: core/models.py:168
msgid "short name" msgid "short name"
msgstr "nom court" msgstr "nom court"
#: core/models.py:176 #: core/models.py:174
msgid "language" msgid "language"
msgstr "langue" msgstr "langue"
#: core/models.py:177 #: core/models.py:175
msgid "The language in which the user wants to see the interface." msgid "The language in which the user wants to see the interface."
msgstr "La langue dans laquelle l'utilisateur souhaite voir l'interface." msgstr "La langue dans laquelle l'utilisateur souhaite voir l'interface."
#: core/models.py:183 #: core/models.py:181
msgid "The timezone in which the user wants to see times." msgid "The timezone in which the user wants to see times."
msgstr "Le fuseau horaire dans lequel l'utilisateur souhaite voir les heures." msgstr "Le fuseau horaire dans lequel l'utilisateur souhaite voir les heures."
#: core/models.py:186 #: core/models.py:184
msgid "device" msgid "device"
msgstr "appareil" msgstr "appareil"
#: core/models.py:188 #: core/models.py:186
msgid "Whether the user is a device or a real user." msgid "Whether the user is a device or a real user."
msgstr "Si l'utilisateur est un appareil ou un utilisateur réel." msgstr "Si l'utilisateur est un appareil ou un utilisateur réel."
#: core/models.py:191 #: core/models.py:189
msgid "staff status" msgid "staff status"
msgstr "statut du personnel" msgstr "statut du personnel"
#: core/models.py:193 #: core/models.py:191
msgid "Whether the user can log into this admin site." msgid "Whether the user can log into this admin site."
msgstr "Si l'utilisateur peut se connecter à ce site d'administration." msgstr "Si l'utilisateur peut se connecter à ce site d'administration."
#: core/models.py:196 #: core/models.py:194
msgid "active" msgid "active"
msgstr "actif" msgstr "actif"
#: core/models.py:199 #: core/models.py:197
msgid "" msgid ""
"Whether this user should be treated as active. Unselect this instead of " "Whether this user should be treated as active. Unselect this instead of "
"deleting accounts." "deleting accounts."
@@ -269,65 +249,65 @@ msgstr ""
"Si cet utilisateur doit être traité comme actif. Désélectionnez cette option " "Si cet utilisateur doit être traité comme actif. Désélectionnez cette option "
"au lieu de supprimer des comptes." "au lieu de supprimer des comptes."
#: core/models.py:212 #: core/models.py:210
msgid "user" msgid "user"
msgstr "utilisateur" msgstr "utilisateur"
#: core/models.py:213 #: core/models.py:211
msgid "users" msgid "users"
msgstr "utilisateurs" msgstr "utilisateurs"
#: core/models.py:272 #: core/models.py:270
msgid "Resource" msgid "Resource"
msgstr "Ressource" msgstr "Ressource"
#: core/models.py:273 #: core/models.py:271
msgid "Resources" msgid "Resources"
msgstr "Ressources" msgstr "Ressources"
#: core/models.py:331 #: core/models.py:329
msgid "Resource access" msgid "Resource access"
msgstr "Accès aux ressources" msgstr "Accès aux ressources"
#: core/models.py:332 #: core/models.py:330
msgid "Resource accesses" msgid "Resource accesses"
msgstr "Accès aux ressources" msgstr "Accès aux ressources"
#: core/models.py:338 #: core/models.py:336
msgid "Resource access with this User and Resource already exists." msgid "Resource access with this User and Resource already exists."
msgstr "" msgstr ""
"L'accès à la ressource avec cet utilisateur et cette ressource existe déjà." "L'accès à la ressource avec cet utilisateur et cette ressource existe déjà."
#: core/models.py:394 #: core/models.py:392
msgid "Visio room configuration" msgid "Visio room configuration"
msgstr "Configuration de la salle de visioconférence" msgstr "Configuration de la salle de visioconférence"
#: core/models.py:395 #: core/models.py:393
msgid "Values for Visio parameters to configure the room." msgid "Values for Visio parameters to configure the room."
msgstr "Valeurs des paramètres de visioconférence pour configurer la salle." msgstr "Valeurs des paramètres de visioconférence pour configurer la salle."
#: core/models.py:402 #: core/models.py:400
msgid "Room PIN code" msgid "Room PIN code"
msgstr "Code PIN de la salle" msgstr "Code PIN de la salle"
#: core/models.py:403 #: core/models.py:401
msgid "Unique n-digit code that identifies this room in telephony mode." msgid "Unique n-digit code that identifies this room in telephony mode."
msgstr "" msgstr ""
"Code unique à n chiffres qui identifie cette salle en mode téléphonique." "Code unique à n chiffres qui identifie cette salle en mode téléphonique."
#: core/models.py:409 core/models.py:563 #: core/models.py:407 core/models.py:561
msgid "Room" msgid "Room"
msgstr "Salle" msgstr "Salle"
#: core/models.py:410 #: core/models.py:408
msgid "Rooms" msgid "Rooms"
msgstr "Salles" msgstr "Salles"
#: core/models.py:574 #: core/models.py:572
msgid "Worker ID" msgid "Worker ID"
msgstr "ID du Worker" msgstr "ID du Worker"
#: core/models.py:576 #: core/models.py:574
msgid "" msgid ""
"Enter an identifier for the worker recording.This ID is retained even when " "Enter an identifier for the worker recording.This ID is retained even when "
"the worker stops, allowing for easy tracking." "the worker stops, allowing for easy tracking."
@@ -335,128 +315,127 @@ msgstr ""
"Entrez un identifiant pour l'enregistrement du Worker. Cet identifiant est " "Entrez un identifiant pour l'enregistrement du Worker. Cet identifiant est "
"conservé même lorsque le Worker s'arrête, permettant un suivi facile." "conservé même lorsque le Worker s'arrête, permettant un suivi facile."
#: core/models.py:584 #: core/models.py:582
msgid "Recording mode" msgid "Recording mode"
msgstr "Mode d'enregistrement" msgstr "Mode d'enregistrement"
#: core/models.py:585 #: core/models.py:583
msgid "Defines the mode of recording being called." msgid "Defines the mode of recording being called."
msgstr "Définit le mode d'enregistrement appelé." msgstr "Définit le mode d'enregistrement appelé."
#: core/models.py:590 core/models.py:591 #: core/models.py:588 core/models.py:589
msgid "Recording options" msgid "Recording options"
msgstr "Options d'enregistrement" msgstr "Options d'enregistrement"
#: core/models.py:597 #: core/models.py:595
msgid "Recording" msgid "Recording"
msgstr "Enregistrement" msgstr "Enregistrement"
#: core/models.py:598 #: core/models.py:596
msgid "Recordings" msgid "Recordings"
msgstr "Enregistrements" msgstr "Enregistrements"
#: core/models.py:706 #: core/models.py:704
msgid "Recording/user relation" msgid "Recording/user relation"
msgstr "Relation enregistrement/utilisateur" msgstr "Relation enregistrement/utilisateur"
#: core/models.py:707 #: core/models.py:705
msgid "Recording/user relations" msgid "Recording/user relations"
msgstr "Relations enregistrement/utilisateur" msgstr "Relations enregistrement/utilisateur"
#: core/models.py:713 #: core/models.py:711
msgid "This user is already in this recording." msgid "This user is already in this recording."
msgstr "Cet utilisateur est déjà dans cet enregistrement." msgstr "Cet utilisateur est déjà dans cet enregistrement."
#: core/models.py:719 #: core/models.py:717
msgid "This team is already in this recording." msgid "This team is already in this recording."
msgstr "Cette équipe est déjà dans cet enregistrement." msgstr "Cette équipe est déjà dans cet enregistrement."
#: core/models.py:725 #: core/models.py:723
msgid "Either user or team must be set, not both." msgid "Either user or team must be set, not both."
msgstr "Soit l'utilisateur, soit l'équipe doit être défini, pas les deux." msgstr "Soit l'utilisateur, soit l'équipe doit être défini, pas les deux."
#: core/models.py:742 #: core/models.py:740
msgid "Create rooms" msgid "Create rooms"
msgstr "Créer des salles" msgstr "Créer des salles"
#: core/models.py:743 #: core/models.py:741
msgid "List rooms" msgid "List rooms"
msgstr "Lister les salles" msgstr "Lister les salles"
#: core/models.py:744 #: core/models.py:742
msgid "Retrieve room details" msgid "Retrieve room details"
msgstr "Afficher les détails dune salle" msgstr "Afficher les détails dune salle"
#: core/models.py:745 #: core/models.py:743
msgid "Update rooms" msgid "Update rooms"
msgstr "Mettre à jour les salles" msgstr "Mettre à jour les salles"
#: core/models.py:746 #: core/models.py:744
msgid "Delete rooms" msgid "Delete rooms"
msgstr "Supprimer les salles" msgstr "Supprimer les salles"
#: core/models.py:759 #: core/models.py:757
msgid "Application name" msgid "Application name"
msgstr "Nom de lapplication" msgstr "Nom de lapplication"
#: core/models.py:760 #: core/models.py:758
msgid "Descriptive name for this application." msgid "Descriptive name for this application."
msgstr "Nom descriptif de cette application." msgstr "Nom descriptif de cette application."
#: core/models.py:770 #: core/models.py:768
msgid "Hashed on Save. Copy it now if this is a new secret." msgid "Hashed on Save. Copy it now if this is a new secret."
msgstr "" msgstr ""
"Haché lors de lenregistrement. Copiez-le maintenant sil sagit dun " "Haché lors de lenregistrement. Copiez-le maintenant sil sagit dun "
"nouveau secret." "nouveau secret."
#: core/models.py:781 #: core/models.py:779
msgid "Application" msgid "Application"
msgstr "Application" msgstr "Application"
#: core/models.py:782 #: core/models.py:780
msgid "Applications" msgid "Applications"
msgstr "Applications" msgstr "Applications"
#: core/models.py:805 #: core/models.py:803
msgid "Enter a valid domain" msgid "Enter a valid domain"
msgstr "Saisissez un domaine valide" msgstr "Saisissez un domaine valide"
#: core/models.py:808 #: core/models.py:806
msgid "Domain" msgid "Domain"
msgstr "Domaine" msgstr "Domaine"
#: core/models.py:809 #: core/models.py:807
msgid "Email domain this application can act on behalf of." msgid "Email domain this application can act on behalf of."
msgstr "Domaine de messagerie au nom duquel cette application peut agir." msgstr "Domaine de messagerie au nom duquel cette application peut agir."
#: core/models.py:821 #: core/models.py:819
msgid "Application domain" msgid "Application domain"
msgstr "Domaine dapplication" msgstr "Domaine dapplication"
#: core/models.py:822 #: core/models.py:820
msgid "Application domains" msgid "Application domains"
msgstr "Domaines dapplication" msgstr "Domaines dapplication"
#: core/models.py:840 #: core/models.py:838
msgid "Pending" msgid "Pending"
msgstr "En attente" msgstr "En attente"
#: core/models.py:848 #: core/models.py:846
msgid "Ready" msgid "Ready"
msgstr "Prêt" msgstr "Prêt"
#: core/models.py:854 #: core/models.py:852
msgid "Background image" msgid "Background image"
msgstr "Image de fond" msgstr "Image de fond"
#: core/models.py:866 #: core/models.py:864
msgid "title" msgid "title"
msgstr "Titre" msgstr "Titre"
#: core/models.py:890 #: core/models.py:890
msgid "Malware detection info when the analysis status is unsafe." msgid "Malware detection info when the analysis status is unsafe."
msgstr "" msgstr "Information concernant la détection de Malware cand le statut n'est pas sain"
"Information concernant la détection de Malware cand le statut n'est pas sain"
#: core/models.py:895 #: core/models.py:895
msgid "File" msgid "File"
@@ -466,17 +445,15 @@ msgstr "Fichier"
msgid "Files" msgid "Files"
msgstr "Fichiers" msgstr "Fichiers"
#: core/models.py:1000 #: core/models.py:970
#, fuzzy #, fuzzy
#| msgid "This user is already in this recording." #| msgid "This user is already in this recording."
msgid "This file is already hard deleted." msgid "This file is already hard deleted."
msgstr "Ce fichier a été supprimé." msgstr "Ce fichier a été supprimé."
#: core/models.py:1010 #: core/models.py:980
msgid "To hard delete a file, it must first be soft deleted." msgid "To hard delete a file, it must first be soft deleted."
msgstr "" msgstr "Pour supprimer définitivement un fichier il doit d'abord avoir été marqué comme supprimé (soft delete)"
"Pour supprimer définitivement un fichier il doit d'abord avoir été marqué "
"comme supprimé (soft delete)"
#: core/recording/event/notification.py:116 #: core/recording/event/notification.py:116
msgid "Your recording is ready" msgid "Your recording is ready"
@@ -606,18 +583,18 @@ msgstr ""
" Si vous avez des questions ou besoin d'assistance, veuillez contacter notre " " Si vous avez des questions ou besoin d'assistance, veuillez contacter notre "
"équipe d'assistance à %(support_email)s. " "équipe d'assistance à %(support_email)s. "
#: meet/settings.py:223 #: meet/settings.py:224
msgid "English" msgid "English"
msgstr "Anglais" msgstr "Anglais"
#: meet/settings.py:224 #: meet/settings.py:225
msgid "French" msgid "French"
msgstr "Français" msgstr "Français"
#: meet/settings.py:225 #: meet/settings.py:226
msgid "Dutch" msgid "Dutch"
msgstr "Néerlandais" msgstr "Néerlandais"
#: meet/settings.py:226 #: meet/settings.py:227
msgid "German" msgid "German"
msgstr "Allemand" msgstr "Allemand"
Binary file not shown.
+96 -115
View File
@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-03-12 13:46+0000\n" "POT-Creation-Date: 2026-02-26 17:34+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -29,11 +29,11 @@ msgstr "Rechten"
msgid "Important dates" msgid "Important dates"
msgstr "Belangrijke datums" msgstr "Belangrijke datums"
#: core/admin.py:132 core/admin.py:275 #: core/admin.py:132 core/admin.py:243
msgid "No owner" msgid "No owner"
msgstr "Geen eigenaar" msgstr "Geen eigenaar"
#: core/admin.py:135 core/admin.py:278 #: core/admin.py:135 core/admin.py:246
msgid "Multiple owners" msgid "Multiple owners"
msgstr "Meerdere eigenaren" msgstr "Meerdere eigenaren"
@@ -61,135 +61,116 @@ msgstr "Meldingen succesvol verzonden voor %(count)s opname(n)."
msgid "Skipped %(count)s expired recording(s)." msgid "Skipped %(count)s expired recording(s)."
msgstr "%(count)s verlopen opname(n) overgeslagen." msgstr "%(count)s verlopen opname(n) overgeslagen."
#: core/admin.py:200 #: core/admin.py:309
msgid "Mark selected recordings as 'Failed to Stop'"
msgstr "Geselecteerde opnames markeren als 'Mislukt bij stoppen'"
#: core/admin.py:218
#, python-format
msgid "%(count)s recording(s) successfully marked as 'Failed to Stop'."
msgstr "%(count)s opname(s) succesvol gemarkeerd als 'Mislukt bij stoppen'."
#: core/admin.py:226
#, fuzzy, python-format
#| msgid "Skipped %(count)s expired recording(s)."
msgid "Skipped %(count)s recording(s) with an ineligible status."
msgstr "%(count)s opname(s) met een niet-toegestane status overgeslagen."
#: core/admin.py:342
msgid "No scopes" msgid "No scopes"
msgstr "Geen scopes" msgstr "Geen scopes"
#: core/admin.py:344 #: core/admin.py:311
msgid "Scopes" msgid "Scopes"
msgstr "Scopes" msgstr "Scopes"
#: core/api/filters.py:25 #: core/api/filters.py:24
msgid "Creator is me" msgid "Creator is me"
msgstr "Maker ben ik" msgstr "Maker ben ik"
#: core/api/serializers.py:88 #: core/api/serializers.py:84
msgid "You must be administrator or owner of a room to add accesses to it." msgid "You must be administrator or owner of a room to add accesses to it."
msgstr "" msgstr ""
"Je moet beheerder of eigenaar van een ruimte zijn om toegang toe te voegen." "Je moet beheerder of eigenaar van een ruimte zijn om toegang toe te voegen."
#: core/api/serializers.py:516 #: core/api/serializers.py:443
msgid "This file extension is not allowed." msgid "This file extension is not allowed."
msgstr "Deze bestandsextensie is niet toegestaan." msgstr "Deze bestandsextensie is niet toegestaan."
#: core/api/serializers.py:533 #: core/models.py:35
msgid "You have reached the maximum number of files for this type."
msgstr "Het maximale aantal bestanden voor dit type is bereikt."
#: core/models.py:37
msgid "Member" msgid "Member"
msgstr "Lid" msgstr "Lid"
#: core/models.py:38 #: core/models.py:36
msgid "Administrator" msgid "Administrator"
msgstr "Beheerder" msgstr "Beheerder"
#: core/models.py:39 #: core/models.py:37
msgid "Owner" msgid "Owner"
msgstr "Eigenaar" msgstr "Eigenaar"
#: core/models.py:55 #: core/models.py:53
msgid "Initiated" msgid "Initiated"
msgstr "Gestart" msgstr "Gestart"
#: core/models.py:56 #: core/models.py:54
msgid "Active" msgid "Active"
msgstr "Actief" msgstr "Actief"
#: core/models.py:57 #: core/models.py:55
msgid "Stopped" msgid "Stopped"
msgstr "Gestopt" msgstr "Gestopt"
#: core/models.py:58 #: core/models.py:56
msgid "Saved" msgid "Saved"
msgstr "Opgeslagen" msgstr "Opgeslagen"
#: core/models.py:59 #: core/models.py:57
msgid "Aborted" msgid "Aborted"
msgstr "Afgebroken" msgstr "Afgebroken"
#: core/models.py:60 #: core/models.py:58
msgid "Failed to Start" msgid "Failed to Start"
msgstr "Starten mislukt" msgstr "Starten mislukt"
#: core/models.py:61 #: core/models.py:59
msgid "Failed to Stop" msgid "Failed to Stop"
msgstr "Stoppen mislukt" msgstr "Stoppen mislukt"
#: core/models.py:62 #: core/models.py:60
msgid "Notification succeeded" msgid "Notification succeeded"
msgstr "Notificatie geslaagd" msgstr "Notificatie geslaagd"
#: core/models.py:89 #: core/models.py:87
msgid "SCREEN_RECORDING" msgid "SCREEN_RECORDING"
msgstr "SCHERM_OPNAME" msgstr "SCHERM_OPNAME"
#: core/models.py:90 #: core/models.py:88
msgid "TRANSCRIPT" msgid "TRANSCRIPT"
msgstr "TRANSCRIPT" msgstr "TRANSCRIPT"
#: core/models.py:96 #: core/models.py:94
msgid "Public Access" msgid "Public Access"
msgstr "Openbare toegang" msgstr "Openbare toegang"
#: core/models.py:97 #: core/models.py:95
msgid "Trusted Access" msgid "Trusted Access"
msgstr "Vertrouwde toegang" msgstr "Vertrouwde toegang"
#: core/models.py:98 #: core/models.py:96
msgid "Restricted Access" msgid "Restricted Access"
msgstr "Beperkte toegang" msgstr "Beperkte toegang"
#: core/models.py:110 #: core/models.py:108
msgid "id" msgid "id"
msgstr "id" msgstr "id"
#: core/models.py:111 #: core/models.py:109
msgid "primary key for the record as UUID" msgid "primary key for the record as UUID"
msgstr "primaire sleutel voor het record als UUID" msgstr "primaire sleutel voor het record als UUID"
#: core/models.py:117 #: core/models.py:115
msgid "created on" msgid "created on"
msgstr "aangemaakt op" msgstr "aangemaakt op"
#: core/models.py:118 #: core/models.py:116
msgid "date and time at which a record was created" msgid "date and time at which a record was created"
msgstr "datum en tijd waarop een record werd aangemaakt" msgstr "datum en tijd waarop een record werd aangemaakt"
#: core/models.py:123 #: core/models.py:121
msgid "updated on" msgid "updated on"
msgstr "bijgewerkt op" msgstr "bijgewerkt op"
#: core/models.py:124 #: core/models.py:122
msgid "date and time at which a record was last updated" msgid "date and time at which a record was last updated"
msgstr "datum en tijd waarop een record voor het laatst werd bijgewerkt" msgstr "datum en tijd waarop een record voor het laatst werd bijgewerkt"
#: core/models.py:144 #: core/models.py:142
msgid "" msgid ""
"Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/" "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/"
"_ characters." "_ characters."
@@ -197,11 +178,11 @@ msgstr ""
"Voer een geldige sub in. Deze waarde mag alleen letters, cijfers en @/./+/-/" "Voer een geldige sub in. Deze waarde mag alleen letters, cijfers en @/./+/-/"
"_ tekens bevatten." "_ tekens bevatten."
#: core/models.py:150 #: core/models.py:148
msgid "sub" msgid "sub"
msgstr "sub" msgstr "sub"
#: core/models.py:152 #: core/models.py:150
msgid "" msgid ""
"Optional for pending users; required upon account activation. 255 characters " "Optional for pending users; required upon account activation. 255 characters "
"or fewer. Letters, numbers, and @/./+/-/_ characters only." "or fewer. Letters, numbers, and @/./+/-/_ characters only."
@@ -209,55 +190,55 @@ msgstr ""
"Optioneel voor gebruikers in afwachting; vereist bij accountactivering. " "Optioneel voor gebruikers in afwachting; vereist bij accountactivering. "
"Maximum 255 tekens. Alleen letters, cijfers en @/./+/-/_ toegestaan." "Maximum 255 tekens. Alleen letters, cijfers en @/./+/-/_ toegestaan."
#: core/models.py:161 #: core/models.py:159
msgid "identity email address" msgid "identity email address"
msgstr "identiteit e-mailadres" msgstr "identiteit e-mailadres"
#: core/models.py:166 #: core/models.py:164
msgid "admin email address" msgid "admin email address"
msgstr "beheerder e-mailadres" msgstr "beheerder e-mailadres"
#: core/models.py:168 #: core/models.py:166
msgid "full name" msgid "full name"
msgstr "volledige naam" msgstr "volledige naam"
#: core/models.py:170 #: core/models.py:168
msgid "short name" msgid "short name"
msgstr "korte naam" msgstr "korte naam"
#: core/models.py:176 #: core/models.py:174
msgid "language" msgid "language"
msgstr "taal" msgstr "taal"
#: core/models.py:177 #: core/models.py:175
msgid "The language in which the user wants to see the interface." msgid "The language in which the user wants to see the interface."
msgstr "De taal waarin de gebruiker de interface wil zien." msgstr "De taal waarin de gebruiker de interface wil zien."
#: core/models.py:183 #: core/models.py:181
msgid "The timezone in which the user wants to see times." msgid "The timezone in which the user wants to see times."
msgstr "De tijdzone waarin de gebruiker tijden wil zien." msgstr "De tijdzone waarin de gebruiker tijden wil zien."
#: core/models.py:186 #: core/models.py:184
msgid "device" msgid "device"
msgstr "apparaat" msgstr "apparaat"
#: core/models.py:188 #: core/models.py:186
msgid "Whether the user is a device or a real user." msgid "Whether the user is a device or a real user."
msgstr "Of de gebruiker een apparaat is of een echte gebruiker." msgstr "Of de gebruiker een apparaat is of een echte gebruiker."
#: core/models.py:191 #: core/models.py:189
msgid "staff status" msgid "staff status"
msgstr "personeelsstatus" msgstr "personeelsstatus"
#: core/models.py:193 #: core/models.py:191
msgid "Whether the user can log into this admin site." msgid "Whether the user can log into this admin site."
msgstr "Of de gebruiker kan inloggen op deze beheersite." msgstr "Of de gebruiker kan inloggen op deze beheersite."
#: core/models.py:196 #: core/models.py:194
msgid "active" msgid "active"
msgstr "actief" msgstr "actief"
#: core/models.py:199 #: core/models.py:197
msgid "" msgid ""
"Whether this user should be treated as active. Unselect this instead of " "Whether this user should be treated as active. Unselect this instead of "
"deleting accounts." "deleting accounts."
@@ -265,64 +246,64 @@ msgstr ""
"Of deze gebruiker als actief moet worden behandeld. Deselecteer dit in " "Of deze gebruiker als actief moet worden behandeld. Deselecteer dit in "
"plaats van accounts te verwijderen." "plaats van accounts te verwijderen."
#: core/models.py:212 #: core/models.py:210
msgid "user" msgid "user"
msgstr "gebruiker" msgstr "gebruiker"
#: core/models.py:213 #: core/models.py:211
msgid "users" msgid "users"
msgstr "gebruikers" msgstr "gebruikers"
#: core/models.py:272 #: core/models.py:270
msgid "Resource" msgid "Resource"
msgstr "Bron" msgstr "Bron"
#: core/models.py:273 #: core/models.py:271
msgid "Resources" msgid "Resources"
msgstr "Bronnen" msgstr "Bronnen"
#: core/models.py:331 #: core/models.py:329
msgid "Resource access" msgid "Resource access"
msgstr "Brontoegang" msgstr "Brontoegang"
#: core/models.py:332 #: core/models.py:330
msgid "Resource accesses" msgid "Resource accesses"
msgstr "Brontoegangsrechten" msgstr "Brontoegangsrechten"
#: core/models.py:338 #: core/models.py:336
msgid "Resource access with this User and Resource already exists." msgid "Resource access with this User and Resource already exists."
msgstr "Brontoegang met deze gebruiker en bron bestaat al." msgstr "Brontoegang met deze gebruiker en bron bestaat al."
#: core/models.py:394 #: core/models.py:392
msgid "Visio room configuration" msgid "Visio room configuration"
msgstr "Visio-ruimteconfiguratie" msgstr "Visio-ruimteconfiguratie"
#: core/models.py:395 #: core/models.py:393
msgid "Values for Visio parameters to configure the room." msgid "Values for Visio parameters to configure the room."
msgstr "Waarden voor Visio-parameters om de ruimte te configureren." msgstr "Waarden voor Visio-parameters om de ruimte te configureren."
#: core/models.py:402 #: core/models.py:400
msgid "Room PIN code" msgid "Room PIN code"
msgstr "Pincode van de kamer" msgstr "Pincode van de kamer"
#: core/models.py:403 #: core/models.py:401
msgid "Unique n-digit code that identifies this room in telephony mode." msgid "Unique n-digit code that identifies this room in telephony mode."
msgstr "" msgstr ""
"Unieke n-cijferige code die deze kamer identificeert in telefonie-modus." "Unieke n-cijferige code die deze kamer identificeert in telefonie-modus."
#: core/models.py:409 core/models.py:563 #: core/models.py:407 core/models.py:561
msgid "Room" msgid "Room"
msgstr "Ruimte" msgstr "Ruimte"
#: core/models.py:410 #: core/models.py:408
msgid "Rooms" msgid "Rooms"
msgstr "Ruimtes" msgstr "Ruimtes"
#: core/models.py:574 #: core/models.py:572
msgid "Worker ID" msgid "Worker ID"
msgstr "Worker ID" msgstr "Worker ID"
#: core/models.py:576 #: core/models.py:574
msgid "" msgid ""
"Enter an identifier for the worker recording.This ID is retained even when " "Enter an identifier for the worker recording.This ID is retained even when "
"the worker stops, allowing for easy tracking." "the worker stops, allowing for easy tracking."
@@ -330,120 +311,120 @@ msgstr ""
"Voer een identificatie in voor de worker-opname. Deze ID blijft behouden, " "Voer een identificatie in voor de worker-opname. Deze ID blijft behouden, "
"zelfs wanneer de worker stopt, waardoor eenvoudige tracking mogelijk is." "zelfs wanneer de worker stopt, waardoor eenvoudige tracking mogelijk is."
#: core/models.py:584 #: core/models.py:582
msgid "Recording mode" msgid "Recording mode"
msgstr "Opnamemodus" msgstr "Opnamemodus"
#: core/models.py:585 #: core/models.py:583
msgid "Defines the mode of recording being called." msgid "Defines the mode of recording being called."
msgstr "Definieert de modus van opname die wordt aangeroepen." msgstr "Definieert de modus van opname die wordt aangeroepen."
#: core/models.py:590 core/models.py:591 #: core/models.py:588 core/models.py:589
msgid "Recording options" msgid "Recording options"
msgstr "Opnameopties" msgstr "Opnameopties"
#: core/models.py:597 #: core/models.py:595
msgid "Recording" msgid "Recording"
msgstr "Opname" msgstr "Opname"
#: core/models.py:598 #: core/models.py:596
msgid "Recordings" msgid "Recordings"
msgstr "Opnames" msgstr "Opnames"
#: core/models.py:706 #: core/models.py:704
msgid "Recording/user relation" msgid "Recording/user relation"
msgstr "Opname/gebruiker-relatie" msgstr "Opname/gebruiker-relatie"
#: core/models.py:707 #: core/models.py:705
msgid "Recording/user relations" msgid "Recording/user relations"
msgstr "Opname/gebruiker-relaties" msgstr "Opname/gebruiker-relaties"
#: core/models.py:713 #: core/models.py:711
msgid "This user is already in this recording." msgid "This user is already in this recording."
msgstr "Deze gebruiker is al in deze opname." msgstr "Deze gebruiker is al in deze opname."
#: core/models.py:719 #: core/models.py:717
msgid "This team is already in this recording." msgid "This team is already in this recording."
msgstr "Dit team is al in deze opname." msgstr "Dit team is al in deze opname."
#: core/models.py:725 #: core/models.py:723
msgid "Either user or team must be set, not both." msgid "Either user or team must be set, not both."
msgstr "Ofwel gebruiker of team moet worden ingesteld, niet beide." msgstr "Ofwel gebruiker of team moet worden ingesteld, niet beide."
#: core/models.py:742 #: core/models.py:740
msgid "Create rooms" msgid "Create rooms"
msgstr "Ruimtes aanmaken" msgstr "Ruimtes aanmaken"
#: core/models.py:743 #: core/models.py:741
msgid "List rooms" msgid "List rooms"
msgstr "Ruimtes weergeven" msgstr "Ruimtes weergeven"
#: core/models.py:744 #: core/models.py:742
msgid "Retrieve room details" msgid "Retrieve room details"
msgstr "Details van een ruimte ophalen" msgstr "Details van een ruimte ophalen"
#: core/models.py:745 #: core/models.py:743
msgid "Update rooms" msgid "Update rooms"
msgstr "Ruimtes bijwerken" msgstr "Ruimtes bijwerken"
#: core/models.py:746 #: core/models.py:744
msgid "Delete rooms" msgid "Delete rooms"
msgstr "Ruimtes verwijderen" msgstr "Ruimtes verwijderen"
#: core/models.py:759 #: core/models.py:757
msgid "Application name" msgid "Application name"
msgstr "Naam van de applicatie" msgstr "Naam van de applicatie"
#: core/models.py:760 #: core/models.py:758
msgid "Descriptive name for this application." msgid "Descriptive name for this application."
msgstr "Beschrijvende naam voor deze applicatie." msgstr "Beschrijvende naam voor deze applicatie."
#: core/models.py:770 #: core/models.py:768
msgid "Hashed on Save. Copy it now if this is a new secret." msgid "Hashed on Save. Copy it now if this is a new secret."
msgstr "" msgstr ""
"Wordt gehasht bij het opslaan. Kopieer het nu als dit een nieuw geheim is." "Wordt gehasht bij het opslaan. Kopieer het nu als dit een nieuw geheim is."
#: core/models.py:781 #: core/models.py:779
msgid "Application" msgid "Application"
msgstr "Applicatie" msgstr "Applicatie"
#: core/models.py:782 #: core/models.py:780
msgid "Applications" msgid "Applications"
msgstr "Applicaties" msgstr "Applicaties"
#: core/models.py:805 #: core/models.py:803
msgid "Enter a valid domain" msgid "Enter a valid domain"
msgstr "Voer een geldig domein in" msgstr "Voer een geldig domein in"
#: core/models.py:808 #: core/models.py:806
msgid "Domain" msgid "Domain"
msgstr "Domein" msgstr "Domein"
#: core/models.py:809 #: core/models.py:807
msgid "Email domain this application can act on behalf of." msgid "Email domain this application can act on behalf of."
msgstr "E-maildomein namens welke deze applicatie kan handelen." msgstr "E-maildomein namens welke deze applicatie kan handelen."
#: core/models.py:821 #: core/models.py:819
msgid "Application domain" msgid "Application domain"
msgstr "Applicatiedomein" msgstr "Applicatiedomein"
#: core/models.py:822 #: core/models.py:820
msgid "Application domains" msgid "Application domains"
msgstr "Applicatiedomeinen" msgstr "Applicatiedomeinen"
#: core/models.py:840 #: core/models.py:838
msgid "Pending" msgid "Pending"
msgstr "In afwachting" msgstr "In afwachting"
#: core/models.py:848 #: core/models.py:846
msgid "Ready" msgid "Ready"
msgstr "Klaar" msgstr "Klaar"
#: core/models.py:854 #: core/models.py:852
msgid "Background image" msgid "Background image"
msgstr "Achtergrondafbeelding" msgstr "Achtergrondafbeelding"
#: core/models.py:866 #: core/models.py:864
msgid "title" msgid "title"
msgstr "Titel" msgstr "Titel"
@@ -459,11 +440,11 @@ msgstr "Bestand"
msgid "Files" msgid "Files"
msgstr "Bestanden" msgstr "Bestanden"
#: core/models.py:1000 #: core/models.py:970
msgid "This file is already hard deleted." msgid "This file is already hard deleted."
msgstr "Dit bestand is al definitief verwijderd." msgstr "Dit bestand is al definitief verwijderd."
#: core/models.py:1010 #: core/models.py:980
#, fuzzy #, fuzzy
#| msgid "To hard delete a file, it must first be soft deleted." #| msgid "To hard delete a file, it must first be soft deleted."
msgid "To hard delete a file, it must first be soft deleted." msgid "To hard delete a file, it must first be soft deleted."
@@ -599,18 +580,18 @@ msgstr ""
" Als je vragen hebt of hulp nodig hebt, neem dan contact op met ons support " " Als je vragen hebt of hulp nodig hebt, neem dan contact op met ons support "
"team via %(support_email)s. " "team via %(support_email)s. "
#: meet/settings.py:223 #: meet/settings.py:224
msgid "English" msgid "English"
msgstr "Engels" msgstr "Engels"
#: meet/settings.py:224 #: meet/settings.py:225
msgid "French" msgid "French"
msgstr "Frans" msgstr "Frans"
#: meet/settings.py:225 #: meet/settings.py:226
msgid "Dutch" msgid "Dutch"
msgstr "Nederlands" msgstr "Nederlands"
#: meet/settings.py:226 #: meet/settings.py:227
msgid "German" msgid "German"
msgstr "Duits" msgstr "Duits"
-9
View File
@@ -176,13 +176,6 @@ class Base(Configuration):
environ_prefix=None, environ_prefix=None,
) )
FILE_UPLOAD_ENABLED = values.BooleanValue(
# False to avoid a breaking change for now
default=False,
environ_name="FILE_UPLOAD_ENABLED",
environ_prefix=None,
)
FILE_UPLOAD_PATH = values.Value( FILE_UPLOAD_PATH = values.Value(
"files", environ_name="FILE_UPLOAD_PATH", environ_prefix=None "files", environ_name="FILE_UPLOAD_PATH", environ_prefix=None
) )
@@ -195,7 +188,6 @@ class Base(Configuration):
{ {
"background_image": { "background_image": {
"max_size": 2 * MB, "max_size": 2 * MB,
"max_count_by_user": 10,
"allowed_extensions": [".jpeg", ".jpg", ".png"], "allowed_extensions": [".jpeg", ".jpg", ".png"],
"allowed_mimetypes": ["image/jpeg", "image/png"], "allowed_mimetypes": ["image/jpeg", "image/png"],
}, },
@@ -981,7 +973,6 @@ class Test(Base):
APPLICATION_JWT_AUDIENCE = "Test inc." APPLICATION_JWT_AUDIENCE = "Test inc."
CELERY_TASK_ALWAYS_EAGER = True CELERY_TASK_ALWAYS_EAGER = True
FILE_UPLOAD_ENABLED = True
def __init__(self): def __init__(self):
# pylint: disable=invalid-name # pylint: disable=invalid-name
+11 -11
View File
@@ -2,8 +2,8 @@
# Meet package # Meet package
# #
[build-system] [build-system]
requires = ["uv_build>=0.10.9,<0.11.0"] requires = ["setuptools"]
build-backend = "uv_build" build-backend = "setuptools.build_meta"
[project] [project]
name = "meet" name = "meet"
@@ -21,7 +21,8 @@ classifiers = [
] ]
description = "A simple video and phone conferencing tool, powered by LiveKit" description = "A simple video and phone conferencing tool, powered by LiveKit"
keywords = ["Django", "Contacts", "Templates", "RBAC"] keywords = ["Django", "Contacts", "Templates", "RBAC"]
license = "MIT" license = { file = "LICENSE" }
readme = "README.md"
requires-python = ">=3.13" requires-python = ">=3.13"
dependencies = [ dependencies = [
"boto3==1.42.49", "boto3==1.42.49",
@@ -69,7 +70,7 @@ dependencies = [
"Homepage" = "https://github.com/suitenumerique/meet" "Homepage" = "https://github.com/suitenumerique/meet"
"Repository" = "https://github.com/suitenumerique/meet" "Repository" = "https://github.com/suitenumerique/meet"
[dependency-groups] [project.optional-dependencies]
dev = [ dev = [
"django-extensions==4.1", "django-extensions==4.1",
"drf-spectacular-sidecar==2026.1.1", "drf-spectacular-sidecar==2026.1.1",
@@ -89,13 +90,12 @@ dev = [
"types-requests==2.32.4.20260107", "types-requests==2.32.4.20260107",
] ]
[tool.uv.build-backend] [tool.setuptools]
module-root = "" packages = { find = { where = ["."], exclude = ["tests"] } }
source-exclude = [ zip-safe = true
"**/tests/**",
"**/test_*.py", [tool.distutils.bdist_wheel]
"**/tests.py", universal = true
]
[tool.ruff] [tool.ruff]
exclude = [ exclude = [
-2365
View File
File diff suppressed because it is too large Load Diff
@@ -72,8 +72,7 @@ export const RecordingDownload = () => {
if ( if (
data.status !== RecordingStatus.Saved && data.status !== RecordingStatus.Saved &&
data.status !== RecordingStatus.NotificationSucceed && data.status !== RecordingStatus.NotificationSucceed
data.status !== RecordingStatus.FailedToStop
) { ) {
return <ErrorScreen title={t('unsaved.title')} body={t('unsaved.body')} /> return <ErrorScreen title={t('unsaved.title')} body={t('unsaved.body')} />
} }
@@ -71,7 +71,7 @@ backend:
SUMMARY_SERVICE_API_TOKEN: password SUMMARY_SERVICE_API_TOKEN: password
RECORDING_DOWNLOAD_BASE_URL: https://meet.127.0.0.1.nip.io/recording RECORDING_DOWNLOAD_BASE_URL: https://meet.127.0.0.1.nip.io/recording
ROOM_TELEPHONY_ENABLED: True ROOM_TELEPHONY_ENABLED: True
SSL_CERT_FILE: /app/.venv/lib/python3.13/site-packages/certifi/cacert.pem SSL_CERT_FILE: /usr/local/lib/python3.12/site-packages/certifi/cacert.pem
migrate: migrate:
@@ -100,7 +100,7 @@ backend:
# Extra volume mounts to manage our local custom CA and avoid to set ssl_verify: false # Extra volume mounts to manage our local custom CA and avoid to set ssl_verify: false
extraVolumeMounts: extraVolumeMounts:
- name: certs - name: certs
mountPath: /app/.venv/lib/python3.13/site-packages/certifi/cacert.pem mountPath: /usr/local/lib/python3.13/site-packages/certifi/cacert.pem
subPath: cacert.pem subPath: cacert.pem
# Extra volumes to manage our local custom CA and avoid to set ssl_verify: false # Extra volumes to manage our local custom CA and avoid to set ssl_verify: false
@@ -75,7 +75,7 @@ backend:
ROOM_TELEPHONY_ENABLED: True ROOM_TELEPHONY_ENABLED: True
ROOM_TELEPHONY_DEFAULT_COUNTRY: 'FR' ROOM_TELEPHONY_DEFAULT_COUNTRY: 'FR'
ROOM_TELEPHONY_PHONE_NUMBER: '+33901020304' ROOM_TELEPHONY_PHONE_NUMBER: '+33901020304'
SSL_CERT_FILE: /app/.venv/lib/python3.13/site-packages/certifi/cacert.pem SSL_CERT_FILE: /usr/local/lib/python3.13/site-packages/certifi/cacert.pem
ROOM_SUBTITLE_ENABLED: True ROOM_SUBTITLE_ENABLED: True
EXTERNAL_API_ENABLED: True EXTERNAL_API_ENABLED: True
APPLICATION_JWT_AUDIENCE: https://meet.127.0.0.1.nip.io/external-api/v1.0/ APPLICATION_JWT_AUDIENCE: https://meet.127.0.0.1.nip.io/external-api/v1.0/
@@ -113,7 +113,7 @@ backend:
# Extra volume mounts to manage our local custom CA and avoid to set ssl_verify: false # Extra volume mounts to manage our local custom CA and avoid to set ssl_verify: false
extraVolumeMounts: extraVolumeMounts:
- name: certs - name: certs
mountPath: /app/.venv/lib/python3.13/site-packages/certifi/cacert.pem mountPath: /usr/local/lib/python3.13/site-packages/certifi/cacert.pem
subPath: cacert.pem subPath: cacert.pem
# Extra volumes to manage our local custom CA and avoid to set ssl_verify: false # Extra volumes to manage our local custom CA and avoid to set ssl_verify: false
+2 -2
View File
@@ -94,7 +94,7 @@ backend:
key: BREVO_API_KEY key: BREVO_API_KEY
BREVO_API_CONTACT_LIST_IDS: 8 BREVO_API_CONTACT_LIST_IDS: 8
ROOM_TELEPHONY_ENABLED: True ROOM_TELEPHONY_ENABLED: True
SSL_CERT_FILE: /app/.venv/lib/python3.13/site-packages/certifi/cacert.pem SSL_CERT_FILE: /usr/local/lib/python3.13/site-packages/certifi/cacert.pem
migrate: migrate:
@@ -123,7 +123,7 @@ backend:
# Extra volume mounts to manage our local custom CA and avoid to set ssl_verify: false # Extra volume mounts to manage our local custom CA and avoid to set ssl_verify: false
extraVolumeMounts: extraVolumeMounts:
- name: certs - name: certs
mountPath: /app/.venv/lib/python3.13/site-packages/certifi/cacert.pem mountPath: /usr/local/lib/python3.13/site-packages/certifi/cacert.pem
subPath: cacert.pem subPath: cacert.pem
# Extra volumes to manage our local custom CA and avoid to set ssl_verify: false # Extra volumes to manage our local custom CA and avoid to set ssl_verify: false
+3 -3
View File
@@ -112,7 +112,7 @@ spec:
/usr/bin/mc mb meet/meet-media-storage && \ /usr/bin/mc mb meet/meet-media-storage && \
exit 0 exit 0
restartPolicy: Never restartPolicy: Never
backoffLimit: 3 backoffLimit: 1
--- ---
apiVersion: batch/v1 apiVersion: batch/v1
kind: Job kind: Job
@@ -132,7 +132,7 @@ spec:
/usr/bin/mc admin config set meet notify_webhook:meet-webhook endpoint="https://meet.127.0.0.1.nip.io/api/v1.0/recordings/storage-hook/" auth_token="Bearer password" && \ /usr/bin/mc admin config set meet notify_webhook:meet-webhook endpoint="https://meet.127.0.0.1.nip.io/api/v1.0/recordings/storage-hook/" auth_token="Bearer password" && \
/usr/bin/mc admin service restart meet --wait --json && \ /usr/bin/mc admin service restart meet --wait --json && \
sleep 15 && \ sleep 15 && \
/usr/bin/mc event add meet/meet-media-storage arn:minio:sqs::meet-webhook:webhook --event put --prefix "recordings" && \ /usr/bin/mc event add meet/meet-media-storage arn:minio:sqs::meet-webhook:webhook --event put && \
exit 0 exit 0
restartPolicy: Never restartPolicy: Never
backoffLimit: 3 backoffLimit: 1
+11 -23
View File
@@ -279,28 +279,22 @@ backend:
## @param backend.probes.liveness.path [nullable] Configure path for backend HTTP liveness probe ## @param backend.probes.liveness.path [nullable] Configure path for backend HTTP liveness probe
## @param backend.probes.liveness.targetPort [nullable] Configure port for backend HTTP liveness probe ## @param backend.probes.liveness.targetPort [nullable] Configure port for backend HTTP liveness probe
## @param backend.probes.liveness.initialDelaySeconds [nullable] Configure initial delay for backend liveness probe ## @param backend.probes.liveness.initialDelaySeconds [nullable] Configure initial delay for backend liveness probe
## @param backend.probes.liveness.periodSeconds [nullable] Configure period for backend liveness probe ## @param backend.probes.liveness.initialDelaySeconds [nullable] Configure timeout for backend liveness probe
## @param backend.probes.liveness.timeoutSeconds [nullable] Configure timeout for backend liveness probe
## @param backend.probes.startup.path [nullable] Configure path for backend HTTP startup probe ## @param backend.probes.startup.path [nullable] Configure path for backend HTTP startup probe
## @param backend.probes.startup.targetPort [nullable] Configure port for backend HTTP startup probe ## @param backend.probes.startup.targetPort [nullable] Configure port for backend HTTP startup probe
## @param backend.probes.startup.initialDelaySeconds [nullable] Configure initial delay for backend startup probe ## @param backend.probes.startup.initialDelaySeconds [nullable] Configure initial delay for backend startup probe
## @param backend.probes.startup.periodSeconds [nullable] Configure period for backend startup probe ## @param backend.probes.startup.initialDelaySeconds [nullable] Configure timeout for backend startup probe
## @param backend.probes.startup.timeoutSeconds [nullable] Configure timeout for backend startup probe
## @param backend.probes.readiness.path [nullable] Configure path for backend HTTP readiness probe ## @param backend.probes.readiness.path [nullable] Configure path for backend HTTP readiness probe
## @param backend.probes.readiness.targetPort [nullable] Configure port for backend HTTP readiness probe ## @param backend.probes.readiness.targetPort [nullable] Configure port for backend HTTP readiness probe
## @param backend.probes.readiness.initialDelaySeconds [nullable] Configure initial delay for backend readiness probe ## @param backend.probes.readiness.initialDelaySeconds [nullable] Configure initial delay for backend readiness probe
## @param backend.probes.readiness.periodSeconds [nullable] Configure period for backend readiness probe ## @param backend.probes.readiness.initialDelaySeconds [nullable] Configure timeout for backend readiness probe
## @param backend.probes.readiness.timeoutSeconds [nullable] Configure timeout for backend readiness probe
probes: probes:
liveness: liveness:
path: /__heartbeat__ path: /__heartbeat__
initialDelaySeconds: 5 initialDelaySeconds: 30
periodSeconds: 30
readiness: readiness:
path: /__lbheartbeat__ path: /__lbheartbeat__
initialDelaySeconds: 5 initialDelaySeconds: 30
periodSeconds: 30
## @param backend.resources Resource requirements for the backend container ## @param backend.resources Resource requirements for the backend container
resources: {} resources: {}
@@ -561,29 +555,23 @@ summary:
## @param summary.probes.liveness.path [nullable] Configure path for summary HTTP liveness probe ## @param summary.probes.liveness.path [nullable] Configure path for summary HTTP liveness probe
## @param summary.probes.liveness.targetPort [nullable] Configure port for summary HTTP liveness probe ## @param summary.probes.liveness.targetPort [nullable] Configure port for summary HTTP liveness probe
## @param summary.probes.liveness.initialDelaySeconds [nullable] Configure initial delay summary liveness probe ## @param summary.probes.liveness.initialDelaySeconds [nullable] Configure initial delay for summary liveness probe
## @param summary.probes.liveness.periodSeconds [nullable] Configure the period for the summary liveness probe ## @param summary.probes.liveness.initialDelaySeconds [nullable] Configure timeout for summary liveness probe
## @param summary.probes.liveness.timeoutSeconds [nullable] Configure timeout for summary liveness probe
## @param summary.probes.startup.path [nullable] Configure path for summary HTTP startup probe ## @param summary.probes.startup.path [nullable] Configure path for summary HTTP startup probe
## @param summary.probes.startup.targetPort [nullable] Configure port for summary HTTP startup probe ## @param summary.probes.startup.targetPort [nullable] Configure port for summary HTTP startup probe
## @param summary.probes.startup.initialDelaySeconds [nullable] Configure initial delay for summary startup probe ## @param summary.probes.startup.initialDelaySeconds [nullable] Configure initial delay for summary startup probe
## @param summary.probes.startup.periodSeconds [nullable] Configure period for the summary startup probe ## @param summary.probes.startup.initialDelaySeconds [nullable] Configure timeout for summary startup probe
## @param summary.probes.startup.timeoutSeconds [nullable] Configure timeout for summary startup probe
## @param summary.probes.readiness.path [nullable] Configure path for summary HTTP readiness probe ## @param summary.probes.readiness.path [nullable] Configure path for summary HTTP readiness probe
## @param summary.probes.readiness.targetPort [nullable] Configure port for summary HTTP readiness probe ## @param summary.probes.readiness.targetPort [nullable] Configure port for summary HTTP readiness probe
## @param summary.probes.readiness.initialDelaySeconds [nullable] Configure initial delay for summary readiness probe ## @param summary.probes.readiness.initialDelaySeconds [nullable] Configure initial delay for summary readiness probe
## @param summary.probes.readiness.periodSeconds [nullable] Configure period for the summary readiness probe ## @param summary.probes.readiness.initialDelaySeconds [nullable] Configure timeout for summary readiness probe
## @param summary.probes.readiness.timeoutSeconds [nullable] Configure timeout for summary readiness probe
probes: probes:
liveness: liveness:
path: /__heartbeat__ path: /__heartbeat__
initialDelaySeconds: 5 initialDelaySeconds: 30
periodSeconds: 30
readiness: readiness:
path: /__lbheartbeat__ path: /__lbheartbeat__
initialDelaySeconds: 5 initialDelaySeconds: 30
periodSeconds: 30
## @param summary.resources Resource requirements for the summary container ## @param summary.resources Resource requirements for the summary container
resources: {} resources: {}