Compare commits

..

33 Commits

Author SHA1 Message Date
lebaudantoine eb7924a531 🔧(backend) trigger webhook only for recording file uploads
With the introduction of file background uploads, only trigger the
webhook for files related to recordings.

Avoid firing the "recording saved" event for other file uploads,
preventing unnecessary queries and false triggers.
2026-03-11 19:34:31 +01:00
lebaudantoine d15fb37932 📝(doc) fix changelog formatting
Wrap lines that were excessively long to improve readability.
2026-03-11 19:19:00 +01:00
lebaudantoine 051f33bb1e (backend) add tests for request-entry throttling in the lobby system
Cover throttling behavior for both authenticated users and
anonymous participants identified via the lobby cookie.
2026-03-11 18:07:39 +01:00
lebaudantoine 566eacc8fe (backend) add authenticated user rate throttling on request-entry
Throttle the request-entry endpoint for authenticated users also
to guard against accidental hammering from buggy clients.

Authenticated users are throttled via RequestEntryUserRateThrottle.
Anonymous users are throttled using the lobby participant cookie
through RequestEntryAnonRateThrottle.
2026-03-11 18:07:39 +01:00
lebaudantoine d19023a1ba 🐛(backend) refactor lobby throttling to use participant id instead of IP
use the lobby participant cookie ID as the throttle cache key
rather than the client IP address.

This avoids penalising multiple users behind the same NAT or proxy
and aligns throttling with how LobbyService identifies participants.

This bug was spotted in production where users from ministries behind
NAT was blocked by throttling while using visio.

If no cookie is present yet, skip throttling for the request. The
cookie will be set on the first response and throttling will apply
from subsequent requests.

This throttle is intended to protect against accidental hammering
from buggy clients, not as a security control against DoS attacks.

This is not a security measure, we should use a WAF.
2026-03-11 18:07:39 +01:00
lebaudantoine a6e36f02a7 🌐(frontend) remove references to ProConnect from the login hint
ProConnect (DINUM SSO) should not be mentioned in this context.
Clean up the login hint to avoid confusion.
2026-03-11 17:06:07 +01:00
lebaudantoine c3fd1a89ef 🩹(backend) add page_size to pagination for room endpoints
Align room endpoints with the pagination behavior used across
other API endpoints and resolve issue #1055.
2026-03-11 16:27:02 +01:00
ColorfulRhino 8b1ff536b3 🌐(frontend) improve German translation
Enhancements to the German localization include:

- Refine phrases to sound more natural
- Ensure consistency in words usage
- Adapt phrases to be inclusive and gender-neutral
- Standardize to the "du" form of address, aligning with modern
  practices and other open-source projects like GitLab and the Android
  Open Source Project
2026-03-10 22:56:25 +01:00
Hadrien Blanc 6b08b8da1b 📝 Fix documentation and comment typos 2026-03-10 22:56:08 +01:00
lebaudantoine c4ff42f181 📝(doc) update changelog 2026-03-10 18:36:04 +01:00
lebaudantoine ee0aa0fe5b 🔖(helm) release chart 0.0.17 2026-03-10 18:36:04 +01:00
lebaudantoine 09c871fc29 🩹(helm) fix MinIO ingress port configuration
The wrong port was configured, causing CORS issues
when uploading files directly to the MinIO bucket.

Port 9001 is reserved for the MinIO console;
use the correct API port instead.
2026-03-10 18:36:04 +01:00
Florent Chehab 8496959188 (infra) add celery backend to helm & improvements
* Add a simple celery-backend deployment to helm chart, which
  uses the same docker image as the backend, and runs a single
  worker.
* Update dev keycloack values accordingly
* Update Tiltfile to properly set service dependencies
* Update minio deployment to increase the default max body size limit
  (relevant to uploading files)
2026-03-10 18:36:04 +01:00
Florent Chehab 77105001e0 (settings) configure celery & run task in dedicated queue
Improve celery configuration from env variables and set
meet backend related tasks to run in the `meet-backend` queue
to ease sharing a single redis instance for multiple celery.
2026-03-10 18:36:04 +01:00
lebaudantoine 2d57d38644 🔨(helm) update helm chart for custom background image
Add media_files_svc and ingress_media_files to helm chart
for serving medias related to the new files.

This setup is more straightforward than merging the
existing recording related media ones.

Also updated values to enable file upload by default in dev.

Mostly done by @lebaudantoine.
2026-03-10 18:36:04 +01:00
dependabot[bot] c9d13619a6 ⬆️(deps) bump rollup from 4.44.2 to 4.59.0 in /src/frontend #1088
Bumps [rollup](https://github.com/rollup/rollup) from 4.44.2 to 4.59.0.
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.44.2...v4.59.0)

---
updated-dependencies:
- dependency-name: rollup
  dependency-version: 4.59.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-10 16:52:00 +01:00
Florent Chehab b99ec9bb50 🔨(tilt) use default user in dev
This change avoid permission issues when syncing files
with tilt.
2026-03-10 15:51:00 +01:00
Cyril b3f26469c8 🐛(frontend) fix hand icon and queue position alignment
Use inline-flex with alignItems center so number and icon align vertically.
2026-03-10 12:12:42 +01:00
Cyril d5c53c7dd4 ️(frontend) improve ui and qria labels for help article links
Screen readers now announce full link text instead of "Learn more".
2026-03-10 11:36:10 +01:00
Cyril f43ac2e4eb ♻️(refactor) apply caption text size to subtitles
Use accessibilityStore.captionTextSize in Transcription component.
2026-03-10 11:15:52 +01:00
Cyril ea5dd5bc0e (feat) add caption text size setting in Accessibility tab
Extract CaptionsSettings component, Settings > Accessibility > Captions.
2026-03-10 11:14:40 +01:00
Cyril 86427fa2b7 🌐(i18n) add captions text size setting translations
EN, FR, DE, NL for Accessibility > Captions > Text size.
2026-03-10 11:14:39 +01:00
Cyril 3959c3657c ️(frontend) add caption text size to accessibility store
Persist captionTextSize (small/medium/large) in user preferences.
2026-03-10 11:14:08 +01:00
Cyril 191f8abbcc ️(frontend) improve chat a11y for screen readers
Chat messages announced via role="log" when open, toast+announce when closed.
2026-03-09 16:06:08 +01:00
Cyril d91f8bb6e1 Merge branch 'fix/homepage-knowmore-link' 2026-03-09 14:41:26 +01:00
lebaudantoine d612f9b26b 🩹(changelog) fix changelog organisation for v1.10 2026-03-09 14:32:30 +01:00
Cyril 042be17cfa ️(frontend) improve MoreLink a11y and UX on home page
Render MoreLink once in LeftColumn, make full phrase clickable and add icon.
2026-03-09 14:27:39 +01:00
Cyril d00f4fa695 ️(frontend) sync html lang attribute with i18n for screen readers
Set document.documentElement.lang in i18n init and on languageChanged,
2026-03-09 13:32:10 +01:00
Ömer Fatih İlhan fd36469fc2 kubernetes documentation helm path updated 2026-03-06 16:16:14 +01:00
Florent Chehab dc278a6064 (backend) add file upload feature & tests
For the coming features we will need to store files on the meet side.
(for instance user backgrounds).

This commits adds a new Model to manage files, and the associated
serializers & viewsets. All are tested.

This work was heavily inspired by the work done by our friends at
https://github.com/suitenumerique/drive
It build on the same architecture design (upload directly to S3 but
download goes through our proxy), but model is much much simplier
(no folders, no file sharing, etc.).
2026-03-06 11:31:39 +01:00
Florent Chehab 047da94494 (backend) add mimetype detection logic from drive
Reused the logic developed by the team working on drive.
This is usefull for our own upload file backend (that will
come in later commits).

Dockefile was updated to add a required system dependency.

We might want to put this shared logic in a lib.
2026-03-06 11:31:39 +01:00
Florent Chehab 124a8bf8d9 🔨(docker) improve docker ignore
Tweak syntax to ignore folders recursively (important).
And add few missing ones.
2026-03-06 11:22:39 +01:00
Cyril c72c5cae1a ️(frontend) dynamic tab title when connected to meeting
Display room name, date and app title in browser tab
2026-03-05 17:58:00 +01:00
66 changed files with 1371 additions and 408 deletions
+7 -5
View File
@@ -24,13 +24,15 @@ data
.cache
.circleci
.git
.vscode
.iml
.idea
db.sqlite3
.mypy_cache
.pylint.d
.pytest_cache
**/.idea
**/.vscode
**/.pytest_cache
**/.mypy_cache
**/.ruff_cache
# Frontend
node_modules
**/node_modules
+25 -5
View File
@@ -8,14 +8,28 @@ and this project adheres to
## [Unreleased]
## [1.10.0] - 2026-03-05
### Added
- ✨(helm) support celery with our Django backend #1124
- ✨(helm) support ingress for custom background image #1124
- ✨(backend) add authenticated user rate throttling on request-entry #1129
### Changed
- ♿️(frontend) Caption text size setting for accessibility #1062
- ♿️(frontend) sync html lang attribute with i18n for screen readers #1111
- ♿️(frontend) improve MoreLink a11y and UX on home page #1112
- ♿(frontend) improve chat toast a11y for screen readers #1109
- ♿(frontend) improve ui and aria labels for help article links #1108
- 🌐(frontend) improve German translation #1125
### Fixed
- 🩹(frontend) remove incorrect reference to ProConnect on the prejoin #1080
- ✨(frontend) add Ctrl+Shift+/ to open shortcuts settings #1050
- ♿(frontend) announce selected state to screen readers #1081
- 💄(frontend) truncate long names with ellipsis in reaction overlay #1099
- 🐛(frontend) fix hand icon and queue position alignment and position #1119
- 🩹(backend) add page_size to pagination for room endpoints #1131
- 🐛(backend) refactor lobby throttling to use participant id #1129
## [1.10.0] - 2026-03-05
### Changed
@@ -23,12 +37,18 @@ and this project adheres to
- 🦺(backend) strengthen API validation for recording options #1063
- ⚡️(frontend) optimize few performance caveats #1073
- 🔒️(helm) introduce a dedicated Kubernetes Ingress for webhook-livekit #1066
- ⬆️(deps) bump rollup from 4.44.2 to 4.59.0 in /src/frontend #1088
### Fixed
- 🐛(migrations) use settings in migrations #1058
- 💄(frontend) truncate pinned participant name with ellipsis on overflow #1056
- ♿(frontend) prevent focus ring clipping on invite dialog #1078
- ♿(frontend) dynamic tab title when connected to meeting #1060
- 🩹(frontend) remove incorrect reference to ProConnect on the prejoin #1080
- ✨(frontend) add Ctrl+Shift+/ to open shortcuts settings #1050
- ♿(frontend) announce selected state to screen readers #1081
- 💄(frontend) truncate long names with ellipsis in reaction overlay #1099
### Added
+3 -4
View File
@@ -18,7 +18,6 @@ docker_build(
'localhost:5001/meet-backend:latest',
context='..',
dockerfile='../Dockerfile',
build_args={'DOCKER_USER': '1001:127'},
only=['./src/backend', './src/mail', './docker'],
target = 'backend-production',
live_update=[
@@ -34,7 +33,6 @@ clean_old_images('localhost:5001/meet-backend')
docker_build(
'localhost:5001/meet-frontend-dinum:latest',
context='..',
build_args={'DOCKER_USER': '1001:127'},
dockerfile='../docker/dinum-frontend/Dockerfile',
only=['./src/frontend', './docker', './.dockerignore'],
target = 'frontend-production',
@@ -59,7 +57,6 @@ clean_old_images('localhost:5001/meet-frontend-generic')
docker_build(
'localhost:5001/meet-summary:latest',
context='../src/summary',
build_args={'DOCKER_USER': '1001:127'},
dockerfile='../src/summary/Dockerfile',
only=['.'],
target = 'production',
@@ -72,7 +69,6 @@ clean_old_images('localhost:5001/meet-summary')
docker_build(
'localhost:5001/meet-agents:latest',
context='../src/agents',
build_args={'DOCKER_USER': '1001:127'},
dockerfile='../src/agents/Dockerfile',
only=['.'],
target = 'production',
@@ -103,6 +99,9 @@ k8s_yaml(local('cd ../src/helm && helmfile -n meet -e ${DEV_ENV:-dev} template .
k8s_resource('minio-bucket', resource_deps=['minio'])
k8s_resource('meet-backend', resource_deps=['postgresql', 'minio', 'redis', 'livekit-livekit-server'])
k8s_resource('meet-celery-backend', resource_deps=['redis'])
k8s_resource('meet-celery-summarize', resource_deps=['redis'])
k8s_resource('meet-celery-transcribe', resource_deps=['redis'])
k8s_resource('meet-backend-migrate', resource_deps=['meet-backend'])
k8s_resource('livekit-livekit-server-test-connection', resource_deps=['livekit-livekit-server'])
k8s_resource('keycloak', resource_deps=['kc-postgresql'])
+1 -1
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 service restart meet --wait --json &&
sleep 15 &&
/usr/bin/mc event add meet/meet-media-storage arn:minio:sqs::meet-webhook:webhook --event put &&
/usr/bin/mc event add meet/meet-media-storage arn:minio:sqs::meet-webhook:webhook --event put --prefix "recordings" &&
exit 0;"
app-dev:
+2 -2
View File
@@ -68,13 +68,13 @@ backend:
python manage.py createsuperuser --email admin@example.com --password admin
restartPolicy: Never
# Exra 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:
- name: certs
mountPath: /usr/local/lib/python3.12/site-packages/certifi/cacert.pem
subPath: cacert.pem
# Exra 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
extraVolumes:
- name: certs
configMap:
+2 -2
View File
@@ -87,12 +87,12 @@ If you are using an external service, you need to set `REDIS_URL` environment va
Generate a secure key for `LIVEKIT_API_SECRET` in `env.d/common`.
We provide a minimal recommanded config for production environment in `livekit-server.yaml`. Set the previously generated API secret key in the config file.
We provide a minimal recommended config for production environment in `livekit-server.yaml`. Set the previously generated API secret key in the config file.
To view other customization options, see [config-sample.yaml](https://github.com/livekit/livekit/blob/master/config-sample.yaml)
> [!NOTE]
> In this example, we configured multiplexing on a single UDP port. For better performances, you can configure a range of UDP ports.
> In this example, we configured multiplexing on a single UDP port. For better performance, you can configure a range of UDP ports.
### Meet
+7 -7
View File
@@ -122,11 +122,11 @@ If you haven't run the script **bin/start-kind.sh**, you'll need to manually cre
$ kubectl create namespace meet
```
If you have already run the script, you can skip this step and proceed to the next instruction. NOTE: Before you proceed, and is using the kind method, make sure you download this repo examples/ directory and its contents to the location where you will be executing the helm command. Helm will look for "examples/<name>values.yaml" from based on the path it is being executed.
If you have already run the script, you can skip this step and proceed to the next instruction. NOTE: Before you proceed, and is using the kind method, make sure you download this repo examples/helm directory and its contents to the location where you will be executing the helm command. Helm will look for "examples/helm/<name>values.yaml" from based on the path it is being executed.
```
$ kubectl config set-context --current --namespace=meet
$ helm install keycloak oci://registry-1.docker.io/bitnamicharts/keycloak -f examples/keycloak.values.yaml
$ helm install keycloak oci://registry-1.docker.io/bitnamicharts/keycloak -f examples/helm/keycloak.values.yaml
$ #wait until
$ kubectl get po
NAME READY STATUS RESTARTS AGE
@@ -150,7 +150,7 @@ OIDC_RP_SIGN_ALGO: RS256
OIDC_RP_SCOPES: "openid email"
```
You can find these values in **examples/keycloak.values.yaml**
You can find these values in **examples/helm/keycloak.values.yaml**
### Find livekit server connexion values
@@ -159,7 +159,7 @@ LaSuite Meet use livekit for streaming part so if you have a livekit provider, o
Livekit need a redis (and meet too) so we will start by deploying a redis :
```
$ helm install redis oci://registry-1.docker.io/bitnamicharts/redis -f examples/redis.values.yaml
$ helm install redis oci://registry-1.docker.io/bitnamicharts/redis -f examples/helm/redis.values.yaml
$ kubectl get po
NAME READY STATUS RESTARTS AGE
keycloak-0 1/1 Running 0 26m
@@ -172,7 +172,7 @@ When the redis is ready we can deploy livekit-server.
```
$ helm repo add livekit https://helm.livekit.io
$ helm repo update
$ helm install livekit livekit/livekit-server -f examples/livekit.values.yaml
$ helm install livekit livekit/livekit-server -f examples/helm/livekit.values.yaml
$ kubectl get po
NAME READY STATUS RESTARTS AGE
keycloak-0 1/1 Running 0 30m
@@ -199,7 +199,7 @@ CELERY_RESULT_BACKEND: redis://default:pass@redis-master:6379/1
LaSuite Meet uses a postgresql db as backend so if you have a provider, obtain the necessary information to use it. If you do not have, you can install a postgresql testing environment as follows:
```
$ helm install postgresql oci://registry-1.docker.io/bitnamicharts/postgresql -f examples/postgresql.values.yaml
$ helm install postgresql oci://registry-1.docker.io/bitnamicharts/postgresql -f examples/helm/postgresql.values.yaml
$ kubectl get po
NAME READY STATUS RESTARTS AGE
keycloak-0 1/1 Running 0 45m
@@ -226,7 +226,7 @@ Now you are ready to deploy LaSuite Meet without AI. AI required more dependenci
```
$ helm repo add meet https://suitenumerique.github.io/meet/
$ helm repo update
$ helm install meet meet/meet -f examples/meet.values.yaml
$ helm install meet meet/meet -f examples/helm/meet.values.yaml
```
## Test your deployment
+50 -1
View File
@@ -1,7 +1,9 @@
"""Throttling modules for the API."""
from django.conf import settings
from lasuite.drf.throttling import MonitoredThrottleMixin
from rest_framework.throttling import AnonRateThrottle
from rest_framework.throttling import AnonRateThrottle, UserRateThrottle
from sentry_sdk import capture_message
@@ -14,11 +16,58 @@ class MonitoredAnonRateThrottle(MonitoredThrottleMixin, AnonRateThrottle):
"""Throttle for the monitored scoped rate throttle."""
class MonitoredUserRateThrottle(MonitoredThrottleMixin, UserRateThrottle):
"""Throttle for the monitored scoped rate throttle."""
class RequestEntryAuthenticatedUserRateThrottle(MonitoredUserRateThrottle):
"""Throttle authenticated user requesting room entry"""
scope = "request_entry"
def get_cache_key(self, request, view):
"""Use the authenticated user ID as the throttle cache key."""
if request.user and not request.user.is_authenticated:
return None # Defer to RequestEntryAnonRateThrottle for anonymous users.
return super().get_cache_key(request, view)
class RequestEntryAnonRateThrottle(MonitoredAnonRateThrottle):
"""Throttle Anonymous user requesting room entry"""
scope = "request_entry"
def get_cache_key(self, request, view):
"""Use the lobby participant cookie ID as the throttle cache key.
Only throttle if a cookie is already set. If no cookie exists yet,
return None to skip throttling — the cookie will be set on the first
response, and throttling will apply from the second request onward.
Keying on the cookie rather than the IP address prevents penalising
multiple users behind the same NAT/proxy, and is consistent with how
LobbyService identifies participants.
Note: as per DRF documentation, application-level throttling is not a
security measure against brute-force or DoS attacks. This throttle exists
solely to guard against accidental hammering from buggy clients.
"""
if request.user and request.user.is_authenticated:
return None # Only throttle unauthenticated requests.
participant_id = request.COOKIES.get(settings.LOBBY_COOKIE_NAME)
if participant_id is None:
return None # No throttling for cookieless requests
return self.cache_format % {
"scope": self.scope,
"ident": participant_id,
}
class CreationCallbackAnonRateThrottle(MonitoredAnonRateThrottle):
"""Throttle Anonymous user requesting room generation callback"""
+6 -2
View File
@@ -224,6 +224,7 @@ class RoomViewSet(
API endpoints to access and perform actions on rooms.
"""
pagination_class = Pagination
permission_classes = [permissions.RoomPermissions]
queryset = models.Room.objects.all()
serializer_class = serializers.RoomSerializer
@@ -392,7 +393,10 @@ class RoomViewSet(
methods=["post"],
url_path="request-entry",
permission_classes=[],
throttle_classes=[throttling.RequestEntryAnonRateThrottle],
throttle_classes=[
throttling.RequestEntryAuthenticatedUserRateThrottle,
throttling.RequestEntryAnonRateThrottle,
],
)
def request_entry(self, request, pk=None): # pylint: disable=unused-argument
"""Request entry to a room"""
@@ -944,7 +948,7 @@ class FileViewSet(
filterset_class = ListFileFilter
def get_queryset(self):
"""Get queryset that defaults to the the current request user."""
"""Get queryset that defaults to the current request user."""
user = self.request.user
queryset = super().get_queryset().select_related("creator")
@@ -16,7 +16,7 @@ class RecordingEventsError(Exception):
class RecordingEventsService:
"""Handles recording-related Livekit webhook events."""
"""Handles recording-related LiveKit webhook events."""
@staticmethod
def handle_update(recording, egress_status):
+1 -1
View File
@@ -129,7 +129,7 @@ class LobbyService:
) -> Tuple[LobbyParticipant, Optional[Dict]]:
"""Request entry to a room for a participant.
This usual status transitions is:
The usual status transitions are:
UNKNOWN -> WAITING -> (ACCEPTED | DENIED)
Flow:
+1 -1
View File
@@ -1,4 +1,4 @@
"""Custom template tags for the core application of People."""
"""Custom template tags for the core application of Meet."""
import base64
@@ -120,3 +120,39 @@ def test_api_rooms_list_authenticated_distinct():
content = response.json()
assert len(content["results"]) == 1
assert content["results"][0]["id"] == str(room.id)
def test_api_rooms_list_pagination_page_size():
"""Users should be able to customize the number of results per page via page_size param."""
user = UserFactory()
client = APIClient()
client.force_login(user)
RoomFactory.create_batch(11, users=[user])
response = client.get("/api/v1.0/rooms/?page_size=2")
assert response.status_code == 200
content = response.json()
assert content["count"] == 11
assert len(content["results"]) == 2
assert content["next"] == "http://testserver/api/v1.0/rooms/?page=2&page_size=2"
assert content["previous"] is None
response = client.get("/api/v1.0/rooms/?page=6&page_size=2")
assert response.status_code == 200
content = response.json()
assert content["count"] == 11
assert len(content["results"]) == 1
assert content["next"] is None
assert content["previous"] == "http://testserver/api/v1.0/rooms/?page=5&page_size=2"
response = client.get("/api/v1.0/rooms/?page_size=3")
assert response.status_code == 200
content = response.json()
assert content["count"] == 11
assert len(content["results"]) == 3
assert content["next"] == "http://testserver/api/v1.0/rooms/?page=2&page_size=3"
assert content["previous"] is None
@@ -631,3 +631,109 @@ def test_list_waiting_participants_empty(settings):
assert response.status_code == 200
assert response.json() == {"participants": []}
@mock.patch.object(utils, "notify_participants", return_value=None)
@mock.patch.object(
utils, "generate_livekit_config", return_value={"token": "test-token"}
)
def test_request_entry_throttling_anonymous_without_cookie(
mock_notify_participants, mock_generate_livekit_config, settings
):
"""Anonymous users without a cookie should not be throttled."""
room = RoomFactory(access_level=RoomAccessLevel.RESTRICTED)
client = APIClient()
settings.LOBBY_COOKIE_NAME = "mocked-cookie"
settings.REST_FRAMEWORK["DEFAULT_THROTTLE_RATES"]["request_entry"] = "1/minute"
response = client.post(
f"/api/v1.0/rooms/{room.id}/request-entry/",
{"username": "test_user"},
)
assert response.status_code == 200
assert response.cookies.get("mocked-cookie") is not None
client.cookies.clear() # Simulate a new cookieless request
response = client.post(
f"/api/v1.0/rooms/{room.id}/request-entry/",
{"username": "test_user"},
)
assert response.status_code == 200
@mock.patch.object(utils, "notify_participants", return_value=None)
@mock.patch.object(
utils, "generate_livekit_config", return_value={"token": "test-token"}
)
def test_request_entry_throttling_anonymous_with_cookie(
mock_notify_participants, mock_generate_livekit_config, settings
):
"""Anonymous users with a cookie should be throttled after exceeding the rate limit."""
room = RoomFactory(access_level=RoomAccessLevel.RESTRICTED)
client = APIClient()
settings.LOBBY_COOKIE_NAME = "mocked-cookie"
settings.REST_FRAMEWORK["DEFAULT_THROTTLE_RATES"]["request_entry"] = "2/minute"
participant_id = str(uuid.uuid4())
client.cookies.load({"mocked-cookie": participant_id})
response = client.post(
f"/api/v1.0/rooms/{room.id}/request-entry/",
{"username": "test_user"},
)
assert response.status_code == 200
response = client.post(
f"/api/v1.0/rooms/{room.id}/request-entry/",
{"username": "test_user"},
)
assert response.status_code == 200
response = client.post(
f"/api/v1.0/rooms/{room.id}/request-entry/",
{"username": "test_user"},
)
assert response.status_code == 429
@mock.patch.object(utils, "notify_participants", return_value=None)
@mock.patch.object(
utils, "generate_livekit_config", return_value={"token": "test-token"}
)
def test_request_entry_throttling_authenticated_user(
mock_notify_participants, mock_generate_livekit_config, settings
):
"""Authenticated users should be throttled."""
room = RoomFactory(access_level=RoomAccessLevel.RESTRICTED)
user = UserFactory()
client = APIClient()
client.force_login(user)
settings.LOBBY_COOKIE_NAME = "mocked-cookie"
settings.REST_FRAMEWORK["DEFAULT_THROTTLE_RATES"]["request_entry"] = "2/minute"
response = client.post(
f"/api/v1.0/rooms/{room.id}/request-entry/",
{"username": "test_user"},
)
assert response.status_code == 200
response = client.post(
f"/api/v1.0/rooms/{room.id}/request-entry/",
{"username": "test_user"},
)
assert response.status_code == 200
response = client.post(
f"/api/v1.0/rooms/{room.id}/request-entry/",
{"username": "test_user"},
)
assert response.status_code == 429
+6 -4
View File
@@ -437,9 +437,11 @@ class Base(Configuration):
# Celery
# Defaults to False to avoid breaking change, async task will be run
# synchronously
CELERY_ENABLED = values.BooleanValue(False)
CELERY_BROKER_URL = values.Value("redis://redis:6379/0")
CELERY_BROKER_TRANSPORT_OPTIONS = values.DictValue({})
CELERY_ENABLED = values.BooleanValue(False, environ_prefix=None)
CELERY_TASK_ALWAYS_EAGER = values.BooleanValue(False, environ_prefix=None)
CELERY_TASK_DEFAULT_QUEUE = values.Value("meet-backend", environ_prefix=None)
CELERY_BROKER_URL = values.Value("redis://redis:6379/0", environ_prefix=None)
CELERY_BROKER_TRANSPORT_OPTIONS = values.DictValue({}, environ_prefix=None)
# Session
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
@@ -970,7 +972,7 @@ class Test(Base):
APPLICATION_JWT_SECRET_KEY = "devKey" # noqa:S105
APPLICATION_JWT_AUDIENCE = "Test inc."
CELERY_TASK_ALWAYS_EAGER = values.BooleanValue(True)
CELERY_TASK_ALWAYS_EAGER = True
def __init__(self):
# pylint: disable=invalid-name
+160 -85
View File
@@ -3959,9 +3959,9 @@
"license": "MIT"
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.44.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.2.tgz",
"integrity": "sha512-g0dF8P1e2QYPOj1gu7s/3LVP6kze9A7m6x0BZ9iTdXK8N5c2V7cpBKHV3/9A4Zd8xxavdhK0t4PnqjkqVmUc9Q==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz",
"integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==",
"cpu": [
"arm"
],
@@ -3973,9 +3973,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.44.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.2.tgz",
"integrity": "sha512-Yt5MKrOosSbSaAK5Y4J+vSiID57sOvpBNBR6K7xAaQvk3MkcNVV0f9fE20T+41WYN8hDn6SGFlFrKudtx4EoxA==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz",
"integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==",
"cpu": [
"arm64"
],
@@ -3987,9 +3987,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.44.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.2.tgz",
"integrity": "sha512-EsnFot9ZieM35YNA26nhbLTJBHD0jTwWpPwmRVDzjylQT6gkar+zenfb8mHxWpRrbn+WytRRjE0WKsfaxBkVUA==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz",
"integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==",
"cpu": [
"arm64"
],
@@ -4001,9 +4001,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.44.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.2.tgz",
"integrity": "sha512-dv/t1t1RkCvJdWWxQ2lWOO+b7cMsVw5YFaS04oHpZRWehI1h0fV1gF4wgGCTyQHHjJDfbNpwOi6PXEafRBBezw==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz",
"integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==",
"cpu": [
"x64"
],
@@ -4015,9 +4015,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.44.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.2.tgz",
"integrity": "sha512-W4tt4BLorKND4qeHElxDoim0+BsprFTwb+vriVQnFFtT/P6v/xO5I99xvYnVzKWrK6j7Hb0yp3x7V5LUbaeOMg==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz",
"integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==",
"cpu": [
"arm64"
],
@@ -4029,9 +4029,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.44.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.2.tgz",
"integrity": "sha512-tdT1PHopokkuBVyHjvYehnIe20fxibxFCEhQP/96MDSOcyjM/shlTkZZLOufV3qO6/FQOSiJTBebhVc12JyPTA==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz",
"integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==",
"cpu": [
"x64"
],
@@ -4043,9 +4043,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.44.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.2.tgz",
"integrity": "sha512-+xmiDGGaSfIIOXMzkhJ++Oa0Gwvl9oXUeIiwarsdRXSe27HUIvjbSIpPxvnNsRebsNdUo7uAiQVgBD1hVriwSQ==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz",
"integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==",
"cpu": [
"arm"
],
@@ -4057,9 +4057,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.44.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.2.tgz",
"integrity": "sha512-bDHvhzOfORk3wt8yxIra8N4k/N0MnKInCW5OGZaeDYa/hMrdPaJzo7CSkjKZqX4JFUWjUGm88lI6QJLCM7lDrA==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz",
"integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==",
"cpu": [
"arm"
],
@@ -4071,9 +4071,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.44.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.2.tgz",
"integrity": "sha512-NMsDEsDiYghTbeZWEGnNi4F0hSbGnsuOG+VnNvxkKg0IGDvFh7UVpM/14mnMwxRxUf9AdAVJgHPvKXf6FpMB7A==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz",
"integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==",
"cpu": [
"arm64"
],
@@ -4085,9 +4085,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.44.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.2.tgz",
"integrity": "sha512-lb5bxXnxXglVq+7imxykIp5xMq+idehfl+wOgiiix0191av84OqbjUED+PRC5OA8eFJYj5xAGcpAZ0pF2MnW+A==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz",
"integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==",
"cpu": [
"arm64"
],
@@ -4098,10 +4098,10 @@
"linux"
]
},
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
"version": "4.44.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.2.tgz",
"integrity": "sha512-Yl5Rdpf9pIc4GW1PmkUGHdMtbx0fBLE1//SxDmuf3X0dUC57+zMepow2LK0V21661cjXdTn8hO2tXDdAWAqE5g==",
"node_modules/@rollup/rollup-linux-loong64-gnu": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz",
"integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==",
"cpu": [
"loong64"
],
@@ -4112,10 +4112,38 @@
"linux"
]
},
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
"version": "4.44.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.2.tgz",
"integrity": "sha512-03vUDH+w55s680YYryyr78jsO1RWU9ocRMaeV2vMniJJW/6HhoTBwyyiiTPVHNWLnhsnwcQ0oH3S9JSBEKuyqw==",
"node_modules/@rollup/rollup-linux-loong64-musl": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz",
"integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==",
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz",
"integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-ppc64-musl": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz",
"integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==",
"cpu": [
"ppc64"
],
@@ -4127,9 +4155,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.44.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.2.tgz",
"integrity": "sha512-iYtAqBg5eEMG4dEfVlkqo05xMOk6y/JXIToRca2bAWuqjrJYJlx/I7+Z+4hSrsWU8GdJDFPL4ktV3dy4yBSrzg==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz",
"integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==",
"cpu": [
"riscv64"
],
@@ -4141,9 +4169,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.44.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.2.tgz",
"integrity": "sha512-e6vEbgaaqz2yEHqtkPXa28fFuBGmUJ0N2dOJK8YUfijejInt9gfCSA7YDdJ4nYlv67JfP3+PSWFX4IVw/xRIPg==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz",
"integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==",
"cpu": [
"riscv64"
],
@@ -4155,9 +4183,9 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.44.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.2.tgz",
"integrity": "sha512-evFOtkmVdY3udE+0QKrV5wBx7bKI0iHz5yEVx5WqDJkxp9YQefy4Mpx3RajIVcM6o7jxTvVd/qpC1IXUhGc1Mw==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz",
"integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==",
"cpu": [
"s390x"
],
@@ -4169,9 +4197,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.44.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.2.tgz",
"integrity": "sha512-/bXb0bEsWMyEkIsUL2Yt5nFB5naLAwyOWMEviQfQY1x3l5WsLKgvZf66TM7UTfED6erckUVUJQ/jJ1FSpm3pRQ==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz",
"integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==",
"cpu": [
"x64"
],
@@ -4183,9 +4211,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.44.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.2.tgz",
"integrity": "sha512-3D3OB1vSSBXmkGEZR27uiMRNiwN08/RVAcBKwhUYPaiZ8bcvdeEwWPvbnXvvXHY+A/7xluzcN+kaiOFNiOZwWg==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz",
"integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==",
"cpu": [
"x64"
],
@@ -4196,10 +4224,38 @@
"linux"
]
},
"node_modules/@rollup/rollup-openbsd-x64": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz",
"integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
]
},
"node_modules/@rollup/rollup-openharmony-arm64": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz",
"integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openharmony"
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.44.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.2.tgz",
"integrity": "sha512-VfU0fsMK+rwdK8mwODqYeM2hDrF2WiHaSmCBrS7gColkQft95/8tphyzv2EupVxn3iE0FI78wzffoULH1G+dkw==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz",
"integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==",
"cpu": [
"arm64"
],
@@ -4211,9 +4267,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.44.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.2.tgz",
"integrity": "sha512-+qMUrkbUurpE6DVRjiJCNGZBGo9xM4Y0FXU5cjgudWqIBWbcLkjE3XprJUsOFgC6xjBClwVa9k6O3A7K3vxb5Q==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz",
"integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==",
"cpu": [
"ia32"
],
@@ -4224,10 +4280,24 @@
"win32"
]
},
"node_modules/@rollup/rollup-win32-x64-gnu": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz",
"integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.44.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.2.tgz",
"integrity": "sha512-3+QZROYfJ25PDcxFF66UEk8jGWigHJeecZILvkPkyQN7oc5BvFo4YEXFkOs154j3FTMp9mn9Ky8RCOwastduEA==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz",
"integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==",
"cpu": [
"x64"
],
@@ -10061,9 +10131,9 @@
}
},
"node_modules/rollup": {
"version": "4.44.2",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.2.tgz",
"integrity": "sha512-PVoapzTwSEcelaWGth3uR66u7ZRo6qhPHc0f2uRO9fX6XDVNrIiGYS0Pj9+R8yIIYSD/mCx2b16Ws9itljKSPg==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz",
"integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -10077,26 +10147,31 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.44.2",
"@rollup/rollup-android-arm64": "4.44.2",
"@rollup/rollup-darwin-arm64": "4.44.2",
"@rollup/rollup-darwin-x64": "4.44.2",
"@rollup/rollup-freebsd-arm64": "4.44.2",
"@rollup/rollup-freebsd-x64": "4.44.2",
"@rollup/rollup-linux-arm-gnueabihf": "4.44.2",
"@rollup/rollup-linux-arm-musleabihf": "4.44.2",
"@rollup/rollup-linux-arm64-gnu": "4.44.2",
"@rollup/rollup-linux-arm64-musl": "4.44.2",
"@rollup/rollup-linux-loongarch64-gnu": "4.44.2",
"@rollup/rollup-linux-powerpc64le-gnu": "4.44.2",
"@rollup/rollup-linux-riscv64-gnu": "4.44.2",
"@rollup/rollup-linux-riscv64-musl": "4.44.2",
"@rollup/rollup-linux-s390x-gnu": "4.44.2",
"@rollup/rollup-linux-x64-gnu": "4.44.2",
"@rollup/rollup-linux-x64-musl": "4.44.2",
"@rollup/rollup-win32-arm64-msvc": "4.44.2",
"@rollup/rollup-win32-ia32-msvc": "4.44.2",
"@rollup/rollup-win32-x64-msvc": "4.44.2",
"@rollup/rollup-android-arm-eabi": "4.59.0",
"@rollup/rollup-android-arm64": "4.59.0",
"@rollup/rollup-darwin-arm64": "4.59.0",
"@rollup/rollup-darwin-x64": "4.59.0",
"@rollup/rollup-freebsd-arm64": "4.59.0",
"@rollup/rollup-freebsd-x64": "4.59.0",
"@rollup/rollup-linux-arm-gnueabihf": "4.59.0",
"@rollup/rollup-linux-arm-musleabihf": "4.59.0",
"@rollup/rollup-linux-arm64-gnu": "4.59.0",
"@rollup/rollup-linux-arm64-musl": "4.59.0",
"@rollup/rollup-linux-loong64-gnu": "4.59.0",
"@rollup/rollup-linux-loong64-musl": "4.59.0",
"@rollup/rollup-linux-ppc64-gnu": "4.59.0",
"@rollup/rollup-linux-ppc64-musl": "4.59.0",
"@rollup/rollup-linux-riscv64-gnu": "4.59.0",
"@rollup/rollup-linux-riscv64-musl": "4.59.0",
"@rollup/rollup-linux-s390x-gnu": "4.59.0",
"@rollup/rollup-linux-x64-gnu": "4.59.0",
"@rollup/rollup-linux-x64-musl": "4.59.0",
"@rollup/rollup-openbsd-x64": "4.59.0",
"@rollup/rollup-openharmony-arm64": "4.59.0",
"@rollup/rollup-win32-arm64-msvc": "4.59.0",
"@rollup/rollup-win32-ia32-msvc": "4.59.0",
"@rollup/rollup-win32-x64-gnu": "4.59.0",
"@rollup/rollup-win32-x64-msvc": "4.59.0",
"fsevents": "~2.3.2"
}
},
+2 -2
View File
@@ -4,7 +4,7 @@ import { Suspense } from 'react'
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
import { QueryClientProvider } from '@tanstack/react-query'
import { useTranslation } from 'react-i18next'
import { useLang } from 'hoofd'
import { useTitle } from 'hoofd'
import { Switch, Route } from 'wouter'
import { I18nProvider } from 'react-aria-components'
import { Layout } from './layout/Layout'
@@ -17,7 +17,7 @@ import { useIsSdkContext } from '@/features/sdk/hooks/useIsSdkContext'
function App() {
const { i18n } = useTranslation()
useLang(i18n.language)
useTitle(import.meta.env.VITE_APP_TITLE ?? '')
const isSDKContext = useIsSdkContext()
@@ -2,23 +2,25 @@ import { A, Text } from '@/primitives'
import { useTranslation } from 'react-i18next'
import { useConfig } from '@/api/useConfig'
const appTitle = import.meta.env.VITE_APP_TITLE ?? 'LaSuite Meet'
export const MoreLink = () => {
const { t } = useTranslation('home')
const { data } = useConfig()
if (!data?.manifest_link) return
if (!data?.manifest_link) return null
return (
<Text as={'p'} variant={'sm'} style={{ padding: '1rem 0' }}>
<Text as="p" variant="sm" style={{ padding: '1rem 0' }}>
<A
href={data?.manifest_link}
target="_blank"
rel="noopener noreferrer"
aria-label={t('moreLinkLabel')}
externalIcon
aria-label={t('moreLinkLabel', { appTitle })}
>
{t('moreLink')}
</A>{' '}
{t('moreAbout', { appTitle: `${import.meta.env.VITE_APP_TITLE}` })}
{t('moreLink')} {t('moreAbout', { appTitle })}
</A>
</Text>
)
}
+1 -14
View File
@@ -258,23 +258,10 @@ export const Home = () => {
</DialogTrigger>
</div>
<Separator />
<div
className={css({
display: { base: 'none', lg: 'inline' },
})}
>
<MoreLink />
</div>
<MoreLink />
</LeftColumn>
<RightColumn>
<IntroSlider />
<div
className={css({
display: { base: 'inline', lg: 'none' },
})}
>
<MoreLink />
</div>
</RightColumn>
</Columns>
<LaterMeetingDialog
@@ -18,10 +18,15 @@ import {
ANIMATION_DURATION,
ReactionPortals,
} from '@/features/rooms/livekit/components/ReactionPortal'
import { layoutStore } from '@/stores/layout'
import { PanelId } from '@/features/rooms/livekit/hooks/useSidePanel'
import { useScreenReaderAnnounce } from '@/hooks/useScreenReaderAnnounce'
export const MainNotificationToast = () => {
const room = useRoomContext()
const { triggerNotificationSound } = useNotificationSound()
const { t } = useTranslation('notifications')
const announce = useScreenReaderAnnounce()
const [reactions, setReactions] = useState<Reaction[]>([])
const instanceIdRef = useRef(0)
@@ -41,12 +46,21 @@ export const MainNotificationToast = () => {
},
{ timeout: NotificationDuration.MESSAGE }
)
if (layoutStore.activePanelId !== PanelId.CHAT) {
announce(
t('chatMessageReceived', {
name: participant.name || t('defaultName'),
message: chatMessage.message,
}),
'polite'
)
}
}
room.on(RoomEvent.ChatMessage, handleChatMessage)
return () => {
room.off(RoomEvent.ChatMessage, handleChatMessage)
}
}, [room, triggerNotificationSound])
}, [room, triggerNotificationSound, announce, t])
const handleEmoji = (emoji: string, participant: Participant) => {
if (!emoji || !Object.values(Emoji).includes(emoji as Emoji)) return
@@ -79,11 +79,20 @@ export const NoAccessView = ({
})}
>
{t(`${i18nKey}.body`)}
<br />
{helpArticle && (
<A href={helpArticle} target="_blank">
{t(`${i18nKey}.linkMore`)}
</A>
<>
{' '}
<A
href={helpArticle}
target="_blank"
rel="noopener noreferrer"
externalIcon
color="note"
aria-label={t(`${i18nKey}.linkAriaLabel`)}
>
{t(`${i18nKey}.linkMore`)}
</A>
</>
)}
</Text>
</VStack>
@@ -151,12 +151,16 @@ export const ScreenRecordingSidePanel = () => {
</H>
<Text variant="body" fullWidth>
{recordingMaxDuration
? t('body', {
max_duration: recordingMaxDuration,
})
? t('body', { max_duration: recordingMaxDuration })
: t('bodyWithoutMaxDuration')}{' '}
{data?.support?.help_article_recording && (
<A href={data.support.help_article_recording} target="_blank">
<A
href={data.support.help_article_recording}
target="_blank"
rel="noopener noreferrer"
externalIcon
aria-label={t('linkAriaLabel')}
>
{t('linkMore')}
</A>
)}
@@ -187,12 +187,16 @@ export const TranscriptSidePanel = () => {
</H>
<Text variant="body" fullWidth>
{recordingMaxDuration
? t('body', {
max_duration: recordingMaxDuration,
})
? t('body', { max_duration: recordingMaxDuration })
: t('bodyWithoutMaxDuration')}{' '}
{data?.support?.help_article_transcript && (
<A href={data.support.help_article_transcript} target="_blank">
<A
href={data.support.help_article_transcript}
target="_blank"
rel="noopener noreferrer"
externalIcon
aria-label={t('linkAriaLabel')}
>
{t('linkMore')}
</A>
)}
@@ -208,6 +212,7 @@ export const TranscriptSidePanel = () => {
href={data.transcription_destination}
target="_blank"
rel="noopener noreferrer"
externalIcon
>
{data.transcription_destination.replace('https://', '')}
</A>
@@ -183,7 +183,13 @@ export const ParticipantTile: (
}}
>
{isHandRaised && !isScreenShare && (
<span>
<span
style={{
display: 'inline-flex',
alignItems: 'center',
gap: '0.1rem',
}}
>
<span>{positionInQueue}</span>
<RiHand
color="black"
@@ -148,17 +148,22 @@ export const Tools = () => {
className={css({
textStyle: 'sm',
paddingX: '0.75rem',
paddingTop: '0.25rem',
marginBottom: '1rem',
})}
>
{t('body')}{' '}
{data?.support?.help_article_more_tools && (
<>
<A href={data?.support?.help_article_more_tools} target="_blank">
{t('moreLink')}
</A>
.
</>
<A
href={data.support.help_article_more_tools}
target="_blank"
rel="noopener noreferrer"
externalIcon
color="note"
aria-label={t('linkAriaLabel')}
>
{t('moreLink')}
</A>
)}
</Text>
{isTranscriptEnabled && (
@@ -22,7 +22,7 @@ import { useSize } from '@/features/rooms/livekit/hooks/useResizeObserver'
export function useGridLayout(
/** HTML element that contains the grid. */
gridElement: React.RefObject<HTMLDivElement>,
/** Count of tracks that should get layed out */
/** Count of tracks that should get laid out */
trackCount: number,
options: {
gridLayouts?: GridLayoutDefinition[]
@@ -0,0 +1,28 @@
import { useTitle } from 'hoofd'
import { useRoomData } from './useRoomData'
import { useMemo } from 'react'
const APP_TITLE = import.meta.env.VITE_APP_TITLE ?? ''
/**
* Updates the browser tab title with the room name to help users easily find
* the meeting tab among many open tabs. Works on both the join screen and
* once connected.
*/
export const useRoomPageTitle = () => {
const roomData = useRoomData()
const pageTitle = useMemo(() => {
if (!roomData) {
return APP_TITLE
}
const roomLabel = roomData.name || roomData.slug || ''
if (!roomLabel) return APP_TITLE
return `${APP_TITLE} - ${roomLabel} `
}, [roomData])
useTitle(pageTitle)
}
@@ -151,7 +151,12 @@ export function Chat({ ...props }: ChatProps) {
minHeight={0}
overflowY="scroll"
>
<ul className="lk-list lk-chat-messages" ref={ulRef}>
<ul
className="lk-list lk-chat-messages"
ref={ulRef}
role="log"
aria-relevant="additions"
>
{renderedMessages}
</ul>
</Div>
@@ -30,6 +30,7 @@ import { useSidePanel } from '../hooks/useSidePanel'
import { RecordingProvider } from '@/features/recording'
import { ScreenShareErrorModal } from '../components/ScreenShareErrorModal'
import { useConnectionObserver } from '../hooks/useConnectionObserver'
import { useRoomPageTitle } from '../hooks/useRoomPageTitle'
import { useNoiseReduction } from '../hooks/useNoiseReduction'
import { useRegisterKeyboardShortcut } from '@/features/shortcuts/useRegisterKeyboardShortcut'
import { useSettingsDialog } from '@/features/settings'
@@ -113,6 +114,7 @@ export function VideoConference({ ...props }: VideoConferenceProps) {
)
useConnectionObserver()
useRoomPageTitle()
useVideoResolutionSubscription()
useRegisterKeyboardShortcut({
@@ -4,6 +4,7 @@ import { css } from '@/styled-system/css'
import { useTranslation } from 'react-i18next'
import { useSnapshot } from 'valtio'
import { accessibilityStore } from '@/stores/accessibility'
import { CaptionsSettings } from '@/features/subtitle/component/CaptionsSettings'
export type AccessibilityTabProps = Pick<TabPanelProps, 'id'>
@@ -32,6 +33,7 @@ export const AccessibilityTab = ({ id }: AccessibilityTabProps) => {
wrapperProps={{ noMargin: true, fullWidth: true }}
/>
</li>
<CaptionsSettings />
</ul>
</TabPanel>
)
@@ -0,0 +1,49 @@
import { Field, H } from '@/primitives'
import { css } from '@/styled-system/css'
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { useSnapshot } from 'valtio'
import {
accessibilityStore,
type CaptionTextSize,
CAPTION_TEXT_SIZE_OPTIONS,
} from '@/stores/accessibility'
export const CaptionsSettings = () => {
const { t } = useTranslation('settings', {
keyPrefix: 'accessibility.captions',
})
const snap = useSnapshot(accessibilityStore)
const captionTextSizeItems = useMemo(
() =>
CAPTION_TEXT_SIZE_OPTIONS.map((size) => ({
value: size,
label: t(`textSize.options.${size}`),
})),
[t]
)
return (
<li>
<H
lvl={3}
className={css({
marginBottom: '0.5rem',
})}
>
{t('heading')}
</H>
<Field
type="select"
label={t('textSize.label')}
items={captionTextSizeItems}
selectedKey={snap.captionTextSize}
onSelectionChange={(key) => {
accessibilityStore.captionTextSize = key as CaptionTextSize
}}
wrapperProps={{ noMargin: true, fullWidth: true }}
/>
</li>
)
}
@@ -8,6 +8,25 @@ import { useRoomContext } from '@livekit/components-react'
import { getParticipantColor } from '@/features/rooms/utils/getParticipantColor'
import { getParticipantName } from '@/features/rooms/utils/getParticipantName'
import { Participant, RoomEvent } from 'livekit-client'
import { useSnapshot } from 'valtio'
import {
accessibilityStore,
CAPTION_TEXT_SIZE_OPTIONS,
type CaptionTextSize,
} from '@/stores/accessibility'
const FONT_SIZE_CONFIG: Record<
CaptionTextSize,
{ fontSize: string; lineHeight: string }
> = {
small: { fontSize: '0.875rem', lineHeight: '1.2rem' },
medium: { fontSize: '1.5rem', lineHeight: '1.7rem' },
large: { fontSize: '2.25rem', lineHeight: '2.5rem' },
}
const CAPTION_FONT_SIZES = Object.fromEntries(
CAPTION_TEXT_SIZE_OPTIONS.map((size) => [size, FONT_SIZE_CONFIG[size]])
) as Record<CaptionTextSize, { fontSize: string; lineHeight: string }>
export interface TranscriptionSegment {
id: string
@@ -73,8 +92,10 @@ const useTranscriptionState = () => {
}
const Transcription = ({ row }: { row: TranscriptionRow }) => {
const { captionTextSize } = useSnapshot(accessibilityStore)
const participantColor = getParticipantColor(row.participant)
const participantName = getParticipantName(row.participant)
const { fontSize, lineHeight } = CAPTION_FONT_SIZES[captionTextSize]
const getDisplayText = (row: TranscriptionRow): string => {
return row.segments
@@ -116,10 +137,9 @@ const Transcription = ({ row }: { row: TranscriptionRow }) => {
</Text>
<p
className={css({
fontSize: '1.5rem',
lineHeight: '1.7rem',
fontWeight: '400',
})}
style={{ fontSize, lineHeight }}
>
{displayText}
</p>
@@ -198,7 +218,6 @@ export const Subtitles = () => {
)
}
}
return rows
}, [transcriptionSegments])
+9 -1
View File
@@ -3,6 +3,7 @@ import resourcesToBackend from 'i18next-resources-to-backend'
import { initReactI18next } from 'react-i18next'
import LanguageDetector from 'i18next-browser-languagedetector'
const i18nDefaultNamespace = 'global'
const fallbackLng = 'fr'
i18n.setDefaultNamespace(i18nDefaultNamespace)
i18n
@@ -15,7 +16,7 @@ i18n
.use(LanguageDetector)
.init({
supportedLngs: ['en', 'fr', 'nl', 'de'],
fallbackLng: 'fr',
fallbackLng,
ns: i18nDefaultNamespace,
detection: {
order: ['localStorage', 'navigator'],
@@ -24,3 +25,10 @@ i18n
escapeValue: false,
},
})
.then(() => {
document.documentElement.setAttribute('lang', i18n.language || fallbackLng)
})
i18n.on('languageChanged', (lang) => {
document.documentElement.setAttribute('lang', lang)
})
@@ -8,12 +8,12 @@
},
"scope": "Diese Erklärung zur Barrierefreiheit gilt für die Website visio.numerique.gouv.fr",
"complianceStatus": {
"title": "Stand der Vereinbarkeit",
"title": "Stand der Gesetzeskonformität",
"body": "visio.numerique.gouv.fr ist nicht konform mit RGAA 4.1. Die Website wurde noch nicht geprüft. Das Team bemüht sich jedoch, eine für alle zugängliche Seite gemäß den Empfehlungen des RGAA zu erstellen."
},
"improvement": {
"title": "Verbesserung und Kontakt",
"body": "Wenn Sie auf Inhalte oder Dienste nicht zugreifen können, können Sie die verantwortliche Person von lasuite.numerique.gouv.fr kontaktieren, um eine barrierefreie Alternative zu erhalten oder die Inhalte in einer anderen Form zu bekommen.",
"body": "Wenn du auf Inhalte oder Dienste nicht zugreifen können, kannst du die verantwortliche Person von lasuite.numerique.gouv.fr kontaktieren, um eine barrierefreie Alternative zu erhalten oder die Inhalte in einer anderen Form zu bekommen.",
"contact": {
"email": "E-Mail: visio@numerique.gouv.fr",
"address": "Adresse: DINUM, 20 avenue de Ségur, 75007 Paris"
@@ -22,11 +22,11 @@
},
"recourse": {
"title": "Rechtsbehelfe",
"introduction": "Dieses Verfahren ist in folgendem Fall zu verwenden: Sie haben der für die Website zuständigen Stelle ein Problem mit der Barrierefreiheit gemeldet, das Sie daran hindert, auf Inhalte oder Dienste des Portals zuzugreifen, und keine zufriedenstellende Antwort erhalten.",
"introduction": "Dieses Verfahren ist in folgendem Fall zu verwenden: Du hast der für die Website zuständigen Stelle ein Problem mit der Barrierefreiheit gemeldet, das dich daran hindert, auf Inhalte oder Dienste des Portals zuzugreifen, und keine zufriedenstellende Antwort erhalten.",
"options": {
"intro": "Sie können:",
"option1": "Dem*der Beauftragten für Rechte eine Nachricht schreiben",
"option2": "Den*die Vertreter*in des*der Beauftragten für Rechte in Ihrer Region kontaktieren",
"intro": "Du kannst:",
"option1": "Dem*der Rechtsbeauftragten eine Nachricht schreiben",
"option2": "Den*die Vertreter*in des*der Rechtsbeauftragten in Ihrer Region kontaktieren",
"option3": "Einen Brief per Post senden (kostenfrei, ohne Briefmarke): </br> Défenseur des droits, Libre réponse 71120, 75342 Paris CEDEX 07"
}
}
+11 -11
View File
@@ -6,8 +6,8 @@
"heading": "Beim Laden der Seite ist ein Fehler aufgetreten"
},
"feedback": {
"context": "Ihr Feedback prägt das Produkt!",
"cta": "Teilen Sie uns Ihre Meinung mit - neues Fenster"
"context": "Dein Feedback gestaltet das Produkt!",
"cta": "Teile uns deine Meinung mit neuer Tab"
},
"forbidden": {
"heading": "Zugriff verweigert"
@@ -17,13 +17,13 @@
"login": {
"buttonLabel": "Anmelden",
"proconnectButtonLabel": "Mit ProConnect anmelden",
"proconnectLinkLabel": "Was ist ProConnect? neues Fenster",
"proconnectLinkLabel": "Was ist ProConnect? neuer Tab",
"proconnectLink": "Was ist ProConnect?"
},
"logout": "Abmelden",
"notFound": {
"heading": "Überprüfen Sie Ihren Meeting-Code",
"body": "Stellen Sie sicher, dass Sie den richtigen Meeting-Code in der URL eingegeben haben. Beispiel:"
"heading": "Überprüfe deinen Meeting-Code",
"body": "Stelle sicher, dass du den richtigen Meeting-Code in der URL eingegeben haben. Beispiel:"
},
"selected": "ausgewählt",
"submit": "OK",
@@ -38,16 +38,16 @@
"accessibility": "Barrierefreiheit: nicht konform",
"ariaLabel": "neues Fenster",
"codeAnnotation": "Unser Code ist offen und verfügbar in diesem",
"code": "Open-Source-Code-Repository",
"technicalDetails": "Technisches Datenblatt",
"code": "Open-Source-Repository",
"technicalDetails": "Technische Angaben",
"termsOfService": "Nutzungsbedingungen"
},
"mentions": "Sofern nicht anders angegeben, sind die Inhalte dieser Website verfügbar unter der",
"license": "Etalab 2.0 Lizenz"
},
"loginHint": {
"title": "Melden Sie sich mit Ihrem Konto an",
"body": "Statt zu warten, melden Sie sich mit Ihrem ProConnect-Konto an.",
"title": "Melde dich mit deinem Konto an",
"body": "Melde dich mit deinem Konto an, statt zu warten.",
"button": {
"ariaLabel": "Hinweis schließen",
"label": "OK"
@@ -55,7 +55,7 @@
},
"skipLink": "Zum Hauptinhalt springen",
"clipboardContent": {
"url": "Um an der Videokonferenz teilzunehmen, klicken Sie auf diesen Link: {{roomUrl}}",
"numberAndPin": "Um telefonisch teilzunehmen, wählen Sie {{phoneNumber}} und geben Sie diesen Code ein: {{pinCode}}"
"url": "Um an der Videokonferenz teilzunehmen, folge diesem Link: {{roomUrl}}",
"numberAndPin": "Um per Telefon teilzunehmen, wähle {{phoneNumber}} und gib diesen Code ein: {{pinCode}}"
}
}
+16 -16
View File
@@ -1,31 +1,31 @@
{
"createMeeting": "Meeting erstellen",
"heading": "Einfache und sichere Videokonferenzen",
"intro": "Kommunizieren und arbeiten Sie mühelos, ohne Ihre digitale Souveränität zu gefährden",
"joinInputError": "Verwenden Sie einen Meeting-Link oder -Code. Beispiele:",
"intro": "Kommuniziere und arbeite mühelos, ohne deine digitale Souveränität zu gefährden",
"joinInputError": "Verwende einen Meeting-Link oder -Code. Beispiele:",
"joinInputExample": "URL oder 10-stelliger Code",
"joinInputLabel": "Meeting-Link",
"joinInputSubmit": "Meeting beitreten",
"joinMeeting": "Meeting beitreten",
"joinMeetingTipContent": "Sie können einem Meeting beitreten, indem Sie den vollständigen Link in die Adressleiste Ihres Browsers einfügen.",
"joinMeetingTipHeading": "Wussten Sie schon?",
"loginToCreateMeeting": "Melden Sie sich an, um ein Meeting zu erstellen",
"moreLinkLabel": "Mehr erfahren neues Tab",
"joinMeetingTipContent": "Du kannst einem Meeting beitreten, indem du den vollständigen Link in die Adressleiste deines Browsers einfügst.",
"joinMeetingTipHeading": "Wusstest du schon?",
"loginToCreateMeeting": "Melde dich an, um ein Meeting zu erstellen",
"moreLinkLabel": "Mehr über {{appTitle}} erfahren neuer Tab",
"moreLink": "Mehr erfahren",
"moreAbout": "über {{appTitle}}",
"createMenu": {
"laterOption": "Meeting für später planen",
"instantOption": "Sofort-Meeting starten"
"instantOption": "Meeting sofort starten"
},
"laterMeetingDialog": {
"heading": "Ihre Zugangsdaten",
"description": "Teilen Sie diese Informationen mit den Gästen. Sie können dem Meeting beitreten, ohne sich anmelden zu müssen. Dieses Meeting ist dauerhaft und kann wiederverwendet werden.",
"heading": "Zugangsdaten",
"description": "Teile diese Informationen mit den Gästen. Sie können dem Meeting beitreten, ohne sich anmelden zu müssen. Dieses Meeting ist dauerhaft und kann wiederverwendet werden.",
"permissions": "Personen mit diesem Link benötigen keine Genehmigung, um diesem Meeting beizutreten.",
"copy": "Informationen kopieren",
"copied": "Informationen kopiert",
"copyUrl": "Meeting-Link kopieren",
"phone": {
"call": "Rufen Sie an:",
"call": "Ruf an:",
"pinCode": "Code:"
}
},
@@ -45,16 +45,16 @@
"tooltip": "Formular ausfüllen"
},
"slide1": {
"title": "Wechseln Sie zur Einfachheit. Testen Sie uns jetzt!",
"body": "Entdecken Sie eine intuitive und zugängliche Lösung, entwickelt für alle Mitarbeitenden im öffentlichen Dienst, ihre Partner und viele weitere."
"title": "Mach Schluss mit kompliziert probier uns jetzt aus!",
"body": "Entdecke eine intuitive und leicht zugängliche Lösung, entwickelt für alle Mitarbeitenden im öffentlichen Dienst, ihre Partner und viele weitere."
},
"slide2": {
"title": "Gruppenanrufe ohne Einschränkungen",
"body": "Unbegrenzte Meeting-Dauer mit flüssiger, hochwertiger Kommunikation unabhängig von der Gruppengröße."
"title": "Gruppenmeetings ohne Einschränkungen",
"body": "Unbegrenzte Meeting-Dauer mit flüssiger Kommunikation in hoher Qualität unabhängig von der Gruppengröße."
},
"slide3": {
"title": "Verwandeln Sie Ihre Meetings mit KI",
"body": "Erhalten Sie präzise und verwertbare Transkripte zur Steigerung Ihrer Produktivität. Funktion in der Beta jetzt testen!"
"title": "Verwandele deine Meetings mit KI",
"body": "Erhalte präzise und verwertbare Transkripte zur Steigerung deiner Produktivität. Beta-Funktion jetzt testen!"
},
"carouselLabel": "Einführungs-Diashow",
"slidePosition": "Folie {{current}} von {{total}}"
+4 -4
View File
@@ -1,8 +1,8 @@
{
"title": "Rechtliche Hinweise",
"creator": {
"title": "Herausgeber",
"body": "Der Visio-Dienst wird herausgegeben von der Interministeriellen Digitaldirektion des Staates (DINUM) mit Sitz:",
"title": "Herausgebende",
"body": "Der Visio-Dienst wird herausgegeben von der Interministeriellen Digitaldirektion Frankreichs (DINUM) mit Sitz:",
"contact": {
"title": "Kontaktdaten",
"address": "20 avenue de Ségur",
@@ -31,7 +31,7 @@
"title": "Wiederverwendung von Inhalten und Links",
"body1": "Sofern nicht ausdrücklich anders angegeben und geistige Eigentumsrechte Dritter betroffen sind, werden die Inhalte dieser Website unter der ",
"license": "offenen Lizenz Etalab 2.0",
"body2": "bereitgestellt. Insbesondere dürfen Sie diese Inhalte frei vervielfältigen, kopieren, ändern, extrahieren, umwandeln, verbreiten, weiterleiten, veröffentlichen, übertragen und nutzen unter der Bedingung, dass die Quelle und das Datum der letzten Aktualisierung genannt werden und Dritte nicht durch die Informationen in die Irre geführt werden.",
"body3": "Jede öffentliche oder private Website ist berechtigt, ohne vorherige Genehmigung, einen Link (einschließlich eines Deep Links) zu den auf dieser Website veröffentlichten Informationen zu setzen."
"body2": "bereitgestellt. Insbesondere dürfen diese Inhalte frei vervielfältigt, kopiert, geändert, extrahiert, umgewandelt, verbreitet, weitergeleitet, veröffentlicht, übertragen und genutzt werden unter der Bedingung, dass die Quelle und das Datum der letzten Aktualisierung genannt werden und Dritte nicht durch die Informationen in die Irre geführt werden.",
"body3": "Jede öffentliche oder private Website ist berechtigt, ohne vorherige Genehmigung, Links (einschließlich Deep Links) zu den auf dieser Website veröffentlichten Informationen zu setzen."
}
}
+20 -19
View File
@@ -1,5 +1,5 @@
{
"defaultName": "Ein Mitwirkender",
"defaultName": "Eine Person",
"joined": {
"description": "{{name}} ist dem Raum beigetreten"
},
@@ -7,46 +7,47 @@
"description": "{{name}} hat die Hand gehoben.",
"cta": "Warteliste öffnen"
},
"muted": "{{name}} hat dein Mikrofon stummgeschaltet. Kein Teilnehmer kann dich hören.",
"muted": "{{name}} hat dein Mikrofon stummgeschaltet.",
"openChat": "Chat öffnen",
"chatMessageReceived": "{{name}} sagt: {{message}}",
"lowerHand": {
"auto": "Es scheint, dass du angefangen hast zu sprechen, daher wird deine Hand gesenkt.",
"auto": "Du hast begonnen zu sprechen deine Hand wird daher automatisch gesenkt.",
"dismiss": "Hand oben lassen"
},
"reaction": {
"description": "{{name}} hat mit {{emoji}} reagiert"
},
"permissionsRemoved": {
"camera": "Der Organisator hat das Video für alle Teilnehmer deaktiviert.",
"microphone": "Der Organisator hat das Mikrofon für alle Teilnehmer deaktiviert.",
"screen_share": "Der Organisator hat die Bildschirmfreigabe für alle Teilnehmer deaktiviert."
"camera": "Die Moderation hat das Video für alle Teilnehmenden deaktiviert.",
"microphone": "Die Moderation hat das Mikrofon für alle Teilnehmenden deaktiviert.",
"screen_share": "Die Moderation hat die Bildschirmfreigabe für alle Teilnehmenden deaktiviert."
},
"waitingParticipants": {
"one": "Eine Person möchte diesem Anruf beitreten.",
"several": "Mehrere Personen möchten diesem Anruf beitreten.",
"one": "Eine Person möchte diesem Meeting beitreten.",
"several": "Mehrere Personen möchten diesem Meeting beitreten.",
"open": "Öffnen",
"accept": "Annehmen"
"accept": "Hereinlassen"
},
"transcript": {
"started": "{{name}} hat die Transkription des Treffens gestartet.",
"stopped": "{{name}} hat die Transkription des Treffens gestoppt.",
"started": "{{name}} hat die Meeting-Transkription gestartet.",
"stopped": "{{name}} hat die Meeting-Transkription gestoppt.",
"limitReached": "Die Transkription hat die maximal zulässige Dauer überschritten und wird automatisch gespeichert.",
"requested": "{{name}} will die Transkription starten."
"requested": "{{name}} möchte die Meeting-Transkription starten."
},
"screenRecording": {
"started": "{{name}} hat die Aufzeichnung des Treffens gestartet.",
"stopped": "{{name}} hat die Aufzeichnung des Treffens gestoppt.",
"started": "{{name}} hat die Meeting-Aufzeichnung gestartet.",
"stopped": "{{name}} hat die Meeting-Aufzeichnung gestoppt.",
"limitReached": "Die Aufzeichnung hat die maximal zulässige Dauer überschritten und wird automatisch gespeichert.",
"requested": "{{name}} will die Aufnahme starten."
"requested": "{{name}} möchte die Meeting-Aufzeichnung starten."
},
"recordingSave": {
"transcript": {
"message": "Wir finalisieren Ihre Aufnahme! Sie erhalten eine E-Mail an <strong>{{email}}</strong>, sobald die Transkription fertig ist.",
"default": "Wir finalisieren Ihre Aufnahme! Sie erhalten eine E-Mail, sobald die Transkription fertig ist."
"message": "Deine Transkription wird gespeichert! Du erhältst eine E-Mail auf <strong>{{email}}</strong>, sobald die Transkription bereit ist.",
"default": "Deine Transkription wird gespeichert! Du erhältst das Transkript per E-Mail, sobald es bereit ist."
},
"screenRecording": {
"message": "Wir finalisieren Ihre Aufnahme! Sie erhalten eine E-Mail an <strong>{{email}}</strong>, sobald sie fertig ist.",
"default": "Wir finalisieren Ihre Aufnahme! Sie erhalten eine E-Mail, sobald sie fertig ist."
"message": "Deine Aufzeichnung wird gespeichert! Du erhältst eine E-Mail auf <strong>{{email}}</strong>, sobald sie bereit ist.",
"default": "Deine Aufzeichnung wird gespeichert! Du erhältst eine E-Mail, sobald sie bereit ist."
}
},
"openMenu": "Menü öffnen"
+6 -6
View File
@@ -1,7 +1,7 @@
{
"error": {
"title": "Aufzeichnung nicht verfügbar",
"body": "Die Aufzeichnung ist nicht verfügbar oder wurde möglicherweise gelöscht. Nur der Organisator der Besprechung hat Zugriff darauf. Wenden Sie sich bei Bedarf gerne an ihn."
"body": "Die Aufzeichnung ist nicht verfügbar oder wurde möglicherweise gelöscht. Nur die*der Organisator:in der Besprechung hat Zugriff darauf. Wende dich bei Bedarf gerne an die Person."
},
"expired": {
"title": "Aufzeichnung abgelaufen",
@@ -9,20 +9,20 @@
},
"authentication": {
"title": "Authentifizierung erforderlich",
"body": "Bitte melden Sie sich an, um auf diese Aufzeichnung zuzugreifen."
"body": "Bitte melde dich sich an, um auf diese Aufzeichnung zuzugreifen."
},
"unsaved": {
"title": "Download nicht verfügbar",
"body": "Diese Aufzeichnung ist noch nicht zum Herunterladen bereit. Bitte versuchen Sie es später erneut."
"body": "Diese Aufzeichnung ist noch nicht zum Herunterladen bereit. Bitte versuche es später erneut."
},
"success": {
"title": "Ihre Aufzeichnung ist bereit!",
"body": "Aufzeichnung des Treffens {{room}} vom {{created_at}}.",
"title": "Deine Aufzeichnung ist bereit!",
"body": "Aufzeichnung des Meetings {{room}} vom {{created_at}}.",
"expiration": "Achtung, diese Aufzeichnung wird nach {{expiration_days}} Tag(en) gelöscht.",
"button": "Herunterladen",
"warning": {
"title": "Linkfreigabe deaktiviert",
"body": "Die Freigabe der Aufzeichnung per Link ist noch nicht verfügbar. Nur Organisatoren können sie herunterladen."
"body": "Die Freigabe der Aufzeichnung per Link ist noch nicht verfügbar. Nur Organisator:innen können sie herunterladen."
}
}
}
+124 -117
View File
@@ -1,17 +1,17 @@
{
"feedback": {
"heading": {
"normal": "Sie haben das Meeting verlassen",
"duplicateIdentity": "Sie haben dem Meeting von einem anderen Gerät aus beigetreten",
"participantRemoved": "Sie wurden von einem Administrator aus dem Anruf entfernt"
"normal": "Du hast das Meeting verlassen",
"duplicateIdentity": "Du bist dem Meeting von einem anderen Gerät aus beigetreten",
"participantRemoved": "Du wurdest von der*dem Organisator:in aus dem Meeting entfernt"
},
"home": "Zur Startseite zurückkehren",
"back": "Dem Meeting erneut beitreten"
},
"selectDevice": {
"loading": "Laden…",
"select": "Wählen Sie einen Wert",
"permissionsNeeded": "Genehmigung erforderlich",
"select": "Wähle einen Wert",
"permissionsNeeded": "Berechtigung erforderlich",
"settings": {
"audio": "Audioeinstellungen",
"video": "Videoeinstellungen"
@@ -19,7 +19,7 @@
"effects": "Effekte anwenden",
"videoinput": {
"choose": "Kamera auswählen",
"permissionsNeeded": "Kamera auswählen - genehmigung erforderlich",
"permissionsNeeded": "Kamera auswählen Berechtigung erforderlich",
"disable": "Kamera deaktivieren",
"enable": "Kamera aktivieren",
"turnedOff": "Kamera deaktiviert",
@@ -29,7 +29,7 @@
},
"audioinput": {
"choose": "Mikrofon auswählen",
"permissionsNeeded": "Mikrofon auswählen - genehmigung erforderlich",
"permissionsNeeded": "Mikrofon auswählen Berechtigung erforderlich",
"disable": "Mikrofon deaktivieren",
"enable": "Mikrofon aktivieren",
"turnedOff": "Mikrofon deaktiviert",
@@ -37,30 +37,30 @@
"label": "Mikrofon"
},
"audiooutput": {
"choose": "Lautsprecher auswählen",
"permissionsNeeded": "Lautsprecher auswählen - genehmigung erforderlich"
"choose": "Audioausgabe auswählen",
"permissionsNeeded": "Audioausgabe auswählen Berechtigung erforderlich"
}
},
"join": {
"effects": {
"description": "Effekte anwenden",
"title": "Effekte",
"subTitle": "Konfigurieren Sie die Effekte Ihrer Kamera."
"subTitle": "Konfiguriere die Effekte deiner Kamera."
},
"heading": "Dem Meeting beitreten?",
"joinLabel": "Beitreten",
"joinMeeting": "Meeting beitreten",
"toggleOff": "Klicken, um auszuschalten",
"toggleOn": "Klicken, um einzuschalten",
"usernameHint": "Wird anderen Teilnehmern angezeigt",
"usernameLabel": "Ihr Name",
"usernameHint": "Wird anderen Teilnehmenden angezeigt",
"usernameLabel": "Dein Name",
"errors": {
"usernameEmpty": "Ihr Name darf nicht leer sein"
"usernameEmpty": "Dein Name darf nicht leer sein"
},
"cameraDisabled": "Kamera ist deaktiviert.",
"cameraStarting": "Kamera wird gestartet…",
"cameraNotGranted": "Möchten Sie, dass andere Sie während der Besprechung sehen können?",
"cameraAndMicNotGranted": "Möchten Sie, dass andere Sie während der Besprechung sehen und hören können?",
"cameraNotGranted": "Möchtest du, dass andere dich während des Meetings sehen können?",
"cameraAndMicNotGranted": "Möchtest du, dass andere dich während des Meetings sehen und hören können?",
"permissionsButton": {
"cameraAndMicNotGranted": "Zugriff auf Kamera und Mikrofon erlauben",
"cameraNotGranted": "Zugriff auf Kamera erlauben"
@@ -71,28 +71,28 @@
},
"waiting": {
"title": "Beitrittsanfrage wird gesendet...",
"body": "Sie können diesem Anruf beitreten, sobald jemand Sie autorisiert"
"body": "Du kannst diesem Meeting beitreten, sobald deine Anfrage angenommen wird"
},
"denied": {
"title": "Sie können diesem Anruf nicht beitreten",
"body": "Ihre Beitrittsanfrage wurde abgelehnt."
"title": "Du kannst diesem Meeting nicht beitreten",
"body": "Deine Beitrittsanfrage wurde abgelehnt."
},
"timeoutInvite": {
"title": "Sie können diesem Anruf nicht beitreten",
"body": "Niemand hat auf Ihre Anfrage reagiert"
"title": "Du kannst diesem Meeting nicht beitreten",
"body": "Niemand hat auf deine Anfrage reagiert"
}
},
"leaveRoomPrompt": "Dadurch verlassen Sie das Meeting.",
"leaveRoomPrompt": "Hiermit verlässt du das Meeting.",
"shareDialog": {
"copy": "Informationen kopieren",
"copy": "Infos kopieren",
"copyUrl": "Link kopieren",
"copied": "Informationen kopiert",
"heading": "Ihr Meeting ist bereit",
"description": "Teilen Sie diesen Link mit Personen, die Sie zum Meeting einladen möchten.",
"copied": "Infos kopiert",
"heading": "Dein Meeting ist bereit",
"description": "Teile diesen Link mit Personen, die du zum Meeting einladen möchten.",
"permissions": "Personen mit diesem Link benötigen keine Erlaubnis, um diesem Meeting beizutreten.",
"closeDialog": "Dialogfenster schließen",
"phone": {
"call": "Rufen Sie an:",
"call": "Ruf an:",
"pinCode": "Code:"
}
},
@@ -108,62 +108,62 @@
"videoinput": "Kamera konnte nicht aktiviert werden"
},
"body": {
"audioinput": "Ihr Mikrofon wird bereits in einem anderen Tab oder von einer anderen Anwendung verwendet, was die Aktivierung in diesem Videoanruf verhindert. Wenn Sie möchten, schließen Sie den anderen Tab oder die Anwendung, um es hier zu verwenden.",
"videoinput": "Ihre Kamera wird bereits in einem anderen Tab oder von einer anderen Anwendung verwendet, was die Aktivierung in diesem Videoanruf verhindert. Wenn Sie möchten, schließen Sie den anderen Tab oder die Anwendung, um sie hier zu verwenden."
"audioinput": "Dein Mikrofon wird bereits in einem anderen Tab oder von einer anderen App verwendet. Dies verhindert die Aktivierung in diesem Meeting. Wenn du dein Mikrofon hier verwenden möchtest, schließe den anderen Tab oder die App.",
"videoinput": "Deine Kamera wird bereits in einem anderen Tab oder von einer anderen App verwendet. Dies verhindert die Aktivierung in diesem Meeting. Wenn du deine Kamera hier verwenden möchtest, schließe den anderen Tab oder die App."
}
},
"close": "OK"
},
"permissionErrorDialog": {
"heading": {
"camera": "{{appTitle}} darf Ihre Kamera nicht verwenden",
"microphone": "{{appTitle}} darf Ihr Mikrofon nicht verwenden",
"cameraAndMicrophone": "{{appTitle}} darf weder Ihr Mikrofon noch Ihre Kamera verwenden",
"camera": "{{appTitle}} hat keine Berechtigung, um deine Kamera zu verwenden",
"microphone": "{{appTitle}} hat keine Berechtigung, um dein Mikrofon zu verwenden",
"cameraAndMicrophone": "{{appTitle}} hat keine Berechtigung, um dein Mikrofon und deine Kamera zu verwenden",
"default": "{{appTitle}} hat keine Berechtigung für bestimmte Zugriffe"
},
"body": {
"openMenu": {
"others": "Klicken Sie auf das Einstellungen-Symbol ICON_PLACEHOLDER in der Adressleiste Ihres Browsers",
"safari": "Klicken Sie auf das Menü 'Safari' und öffnen Sie 'Einstellungen für {{appDomain}}'."
"others": "Klicke das Einstellungs-Symbol ICON_PLACEHOLDER in der Adressleiste deines Browsers",
"safari": "Klicke auf das Menü 'Safari' und öffne 'Einstellungen für {{appDomain}}'."
},
"details": {
"camera": "Zugriff auf die Kamera erlauben",
"microphone": "Zugriff auf das Mikrofon erlauben",
"cameraAndMicrophone": "Zugriff auf Kamera und Mikrofon erlauben",
"default": "Aktivieren Sie die erforderlichen Berechtigungen."
"default": "Erlaube die erforderlichen Berechtigungen."
}
}
},
"permissionsButton": {
"tooltip": "Mehr Infos",
"ariaLabel": "Berechtigungsproblem. Mehr Infos anzeigen"
"ariaLabel": "Problem mit Berechtigungen. Mehr Infos anzeigen"
},
"error": {
"createRoom": {
"heading": "Authentifizierung erforderlich",
"body": "Dieser Raum wurde noch nicht erstellt. Bitte authentifizieren Sie sich, um ihn zu erstellen, oder warten Sie, bis ein authentifizierter Benutzer dies tut."
"body": "Dieser Raum wurde noch nicht erstellt. Bitte authentifiziere dich, um ihn zu erstellen, oder warte, bis eine authentifizierte Person dies tut."
},
"screenShare": {
"title": "Bildschirmfreigabe nicht möglich",
"ariaLabel": "Bildschirmfreigabe nicht möglich",
"message": "Ihr Browser darf möglicherweise den Bildschirm Ihres Computers nicht aufzeichnen.",
"macInstructions": "Gehen Sie zu Ihren",
"message": "Deinem Browser fehlt möglicherweise die Berechtigung, den Bildschirm deines Geräts abzugreifen.",
"macInstructions": "Gehe zu den",
"macSystemPreferences": "Systemeinstellungen",
"helpLinkText": "Weitere Informationen finden Sie unter",
"helpLinkLabel": "Präsentationsproblem",
"helpLinkText": "Weitere Informationen findest du unter",
"helpLinkLabel": "Bildschirmfreigabeproblem",
"closeButton": "Schließen",
"newTab": "Neues Fenster"
"newTab": "Neuer Tab"
}
},
"isIdleDisconnectModal": {
"title": "Bist du noch da?",
"body": "Du bist der einzige Teilnehmer. Dieses Gespräch endet in {{duration}}. Möchtest du das Gespräch fortsetzen?",
"body": "Du bist die*der einzige Teilnehmende. Diese Meeting endet in {{duration}}. Möchtest du das Meeting fortsetzen?",
"settingsPrefix": "Um diese Nachricht nicht mehr zu sehen, gehe zu",
"settingsLink": "Einstellungen > Allgemein",
"settingsSuffix": ".",
"stayButton": "Gespräch fortsetzen",
"stayButton": "Meeting fortsetzen",
"leaveButton": "Jetzt verlassen",
"countdownAnnouncement": "Das Gespräch endet in {{duration}}."
"countdownAnnouncement": "Das Meeting endet in {{duration}}."
},
"controls": {
"microphone": "Mikrofon",
@@ -201,8 +201,8 @@
"participants": {
"open": "Alle ausblenden",
"closed": "Alle anzeigen",
"count_one": "{{count}} Teilnehmer",
"count_other": "{{count}} Teilnehmer"
"count_one": "{{count}} Teilnehmende",
"count_other": "{{count}} Teilnehmende"
},
"tools": {
"open": "Weitere Tools ausblenden",
@@ -217,16 +217,16 @@
"button": "Reaktion senden",
"send": "Reaktion {{emoji}} senden",
"announce": "{{name}} : {{emoji}}",
"you": "Sie",
"you": "du",
"emojis": {
"thumbs-up": "Daumen hoch",
"thumbs-down": "Daumen runter",
"clapping-hands": "Klatschende Hände",
"red-heart": "rotes Herz",
"red-heart": "Rotes Herz",
"face-with-tears-of-joy": "Gesicht mit Freudentränen",
"face-with-open-mouth": "überraschter Gesichtsausdruck",
"party-popper": "Party-Popper",
"folded-hands": "gefaltete Hände"
"face-with-open-mouth": "Überraschter Gesichtsausdruck",
"party-popper": "Konfettikanone",
"folded-hands": "Gefaltete Hände"
}
}
},
@@ -237,7 +237,7 @@
"settings": "Einstellungen",
"screenRecording": "Aufzeichnung",
"transcript": "Transkription",
"username": "Ihren Namen aktualisieren",
"username": "Deinen Namen aktualisieren",
"effects": "Effekte anwenden",
"switchCamera": "Kamera wechseln",
"fullscreen": {
@@ -248,9 +248,9 @@
}
},
"effects": {
"activateCamera": "Ihre Kamera ist deaktiviert. Wählen Sie eine Option, um sie zu aktivieren.",
"cameraDisabled": "Ihre Kamera ist deaktiviert.",
"notAvailable": "Videoeffekte werden bald in Ihrem Browser verfügbar sein. Wir arbeiten daran! In der Zwischenzeit können Sie Google Chrome für die beste Leistung oder Firefox verwenden :(",
"activateCamera": "Deine Kamera ist deaktiviert. Wähle eine Option, um sie zu aktivieren.",
"cameraDisabled": "Deine Kamera ist deaktiviert.",
"notAvailable": "Videoeffekte werden bald in deinem Browser verfügbar sein. Wir arbeiten daran! In der Zwischenzeit kannst du Google Chrome für die beste Leistung oder Firefox verwenden :(",
"heading": "Unschärfe",
"clear": "Effekt deaktivieren",
"blur": {
@@ -300,8 +300,8 @@
"ariaLabel": "Seitenleiste",
"backToTools": "Zurück zu den Meeting-Tools",
"heading": {
"participants": "Teilnehmer",
"effects": "Effekte",
"participants": "Teilnehmende",
"effects": "Hintergrund & Effekte",
"chat": "Nachrichten im Chat",
"transcript": "Transkribieren",
"screenRecording": "Aufzeichnen",
@@ -310,8 +310,8 @@
"info": "Meeting-Informationen"
},
"content": {
"participants": "Teilnehmer",
"effects": "Effekte",
"participants": "Teilnehmende",
"effects": "Hintergrund & Effekte",
"chat": "Nachrichten",
"transcript": "transkribieren",
"screenRecording": "aufzeichnen",
@@ -322,11 +322,13 @@
"closeButton": "{{content}} ausblenden"
},
"chat": {
"disclaimer": "Die Nachrichten sind nur für Teilnehmer zum Zeitpunkt des Sendens sichtbar. Alle Nachrichten werden am Ende des Anrufs gelöscht."
"disclaimer": "Die Nachrichten sind nur für Teilnehmende zum Zeitpunkt des Sendens sichtbar. Alle Nachrichten werden am Ende des Meetings gelöscht.",
"messagesLog": "Chat-Nachrichten"
},
"moreTools": {
"body": "Greifen Sie auf weitere Tools zu, um Ihre Meetings zu verbessern.",
"moreLink": "Mehr erfahren",
"body": "Nutze weitere Tools, um deine Meetings zu verbessern.",
"linkAriaLabel": "Dokumentation zu den Tools öffnen öffnet in neuem Tab",
"moreLink": "Dokumentation öffnen",
"tools": {
"transcript": {
"title": "Transkribieren",
@@ -342,73 +344,77 @@
"roomInformation": {
"title": "Verbindungsinformationen",
"button": {
"ariaLabel": "Kopieren Sie die Informationen aus Ihrer Besprechung",
"ariaLabel": "Kopiere die Informationen aus dem Meeting",
"copy": "Informationen kopieren",
"copied": "Informationen kopiert"
},
"phone": {
"call": "Rufen Sie an:",
"call": "Ruf an:",
"pinCode": "Code:"
}
}
},
"transcript": {
"heading": "Transkribieren Sie Ihr Meeting mit dem KI-Assistenten",
"body": "Transkribieren Sie bis zu {{max_duration}} Meetingdauer.",
"bodyWithoutMaxDuration": "Transkribieren Sie Ihr Meeting ohne Begrenzung.",
"heading": "Transkribiere dein Meeting mithilfe von KI",
"body": "Transkribiere bis zu {{max_duration}} Meetingdauer.",
"bodyWithoutMaxDuration": "Transkribieren dein Meeting ohne Zeitbegrenzung.",
"details": {
"receiver": "Das Transkript wird an den Organisator und die Mitorganisatoren gesendet.",
"receiver": "Das Transkript wird an die*den Organisator:in und die Mitorganisierenden gesendet.",
"destination": "Ein neues Dokument wird erstellt auf",
"destinationUnknown": "Ein neues Dokument wird erstellt",
"language": "Meeting-Sprache:",
"recording": "Auch eine Aufzeichnung starten"
},
"button": {
"start": "Meeting transkribieren starten",
"stop": "Meeting transkribieren beenden",
"start": "Meeting-Transkription starten",
"stop": "Meeting-Transkription beenden",
"saving": "Speichern…",
"starting": "Wird gestartet…",
"anotherModeStarted": "Eine Aufnahme läuft. <br/> Wechseln Sie das Menü und stoppen Sie sie."
"anotherModeStarted": "Eine Aufnahme läuft. <br/> Wechsele das Menü und stoppe sie."
},
"linkMore": "Mehr erfahren",
"linkMore": "Dokumentation öffnen",
"linkAriaLabel": "Dokumentation zur Transkription öffnen öffnet in neuem Tab",
"notAdminOrOwner": {
"heading": "Zugriff eingeschränkt",
"body": "Aus Sicherheitsgründen kann nur der Ersteller oder ein Administrator des Meetings eine Transkription (Beta) starten.",
"linkMore": "Mehr erfahren",
"body": "Aus Sicherheitsgründen kann nur die*der Ersteller:in bzw. Admin des Meetings eine Transkription (Beta) starten.",
"linkMore": "Dokumentation öffnen",
"linkAriaLabel": "Dokumentation zum Transkriptionszugang öffnen öffnet in neuem Tab",
"dividerLabel": "ODER",
"login": {
"heading": "Anmeldung erforderlich",
"body": "Nur der Ersteller des Meetings oder ein Administrator kann die Transkription starten. Melden Sie sich an, um Ihre Berechtigungen zu überprüfen."
"body": "Nur die*der Ersteller:in bzw. Admin kann die Transkription starten. Melde dich an, um deine Berechtigungen zu überprüfen."
},
"request": {
"heading": "Anfrage an Moderator",
"body": "Der Moderator wird benachrichtigt und kann die Transkription starten.",
"buttonLabel": "Anfordern"
"heading": "Transkription anfragen",
"body": "Die Moderation wird benachrichtigt und kann die Transkription starten.",
"buttonLabel": "Anfragen"
}
},
"premium": {
"heading": "Premium-Funktion",
"body": "Diese Funktion ist öffentlichen Bediensteten vorbehalten. Wenn Ihre E-Mail-Adresse nicht autorisiert ist, kontaktieren Sie bitte den Support, um Zugriff zu erhalten.",
"linkMore": "Mehr erfahren",
"body": "Diese Funktion ist öffentlichen Bediensteten vorbehalten. Wenn deine E-Mail-Adresse nicht autorisiert ist, kontaktiere bitte den Support, um Zugriff zu erhalten.",
"linkMore": "Dokumentation öffnen",
"linkAriaLabel": "Dokumentation zum Premium-Zugang öffnen öffnet in neuem Tab",
"dividerLabel": "ODER",
"login": {
"heading": "Anmeldung erforderlich",
"body": "Nur der Ersteller des Meetings oder ein Administrator kann die Transkription starten. Melden Sie sich an, um Ihre Berechtigungen zu überprüfen."
"body": "Nur die*der Ersteller:in bzw. Admin kann die Transkription starten. Melde dich an, um deine Berechtigungen zu überprüfen."
},
"request": {
"heading": "Anfrage an Moderator",
"body": "Der Moderator wird benachrichtigt und kann die Transkription starten.",
"heading": "Transkription anfragen",
"body": "Die Moderation wird benachrichtigt und kann die Transkription starten.",
"buttonLabel": "Anfordern"
}
}
},
"screenRecording": {
"heading": "Diesen Anruf für später aufzeichnen",
"body": "Zeichnen Sie bis zu {{max_duration}} des Meetings auf.",
"bodyWithoutMaxDuration": "Zeichnen Sie Ihr Meeting unbegrenzt auf.",
"linkMore": "Mehr erfahren",
"heading": "Dieses Meeting für später aufzeichnen",
"body": "Zeichne bis zu {{max_duration}} des Meetings auf.",
"bodyWithoutMaxDuration": "Zeichne dein Meeting ohne Zeitbegrenzung auf.",
"linkMore": "Dokumentation öffnen",
"linkAriaLabel": "Dokumentation zur Aufzeichnung öffnen öffnet in neuem Tab",
"details": {
"receiver": "Die Aufzeichnung wird an den Organisator und die Mitorganisatoren gesendet.",
"receiver": "Die Aufzeichnung wird an die*den Organisator:in und die Mitorganisierenden gesendet.",
"destination": "Diese Aufzeichnung wird vorübergehend auf unseren Servern gespeichert",
"loading": "Aufzeichnung wird gestartet",
"linkMore": "Mehr erfahren",
@@ -419,20 +425,21 @@
"stop": "Meeting-Aufzeichnung beenden",
"saving": "Wird gespeichert…",
"starting": "Wird gestartet…",
"anotherModeStarted": "Eine Transkription läuft. <br/> Wechseln Sie das Menü und stoppen Sie sie."
"anotherModeStarted": "Eine Transkription läuft. <br/> Wechsele das Menü und stoppen sie."
},
"notAdminOrOwner": {
"heading": "Zugriff eingeschränkt",
"body": "Aus Sicherheitsgründen kann nur der Ersteller oder ein Administrator des Meetings eine Videoaufnahme (Beta) starten.",
"linkMore": "Mehr erfahren",
"body": "Aus Sicherheitsgründen kann nur die*der Ersteller:in bzw. Admin des Meetings eine Videoaufnahme (Beta) starten.",
"linkMore": "Dokumentation öffnen",
"linkAriaLabel": "Dokumentation zum Aufzeichnungszugang öffnen öffnet in neuem Tab",
"dividerLabel": "ODER",
"login": {
"heading": "Anmeldung erforderlich",
"body": "Nur der Ersteller der Besprechung oder ein Administrator kann die Aufzeichnung starten. Melden Sie sich an, um Ihre Berechtigungen zu überprüfen."
"body": "Nur die*der Ersteller:in bzw. Admin kann die Aufzeichnung starten. Melde dich an, um deine Berechtigungen zu überprüfen."
},
"request": {
"heading": "Aufnahme anfordern",
"body": "Der Moderator wird benachrichtigt und kann die Aufnahme für Sie starten.",
"heading": "Aufnahme anfragen",
"body": "Die Moderation wird benachrichtigt und kann die Aufnahme starten.",
"buttonLabel": "Anfrage senden"
}
},
@@ -447,10 +454,10 @@
"button": "OK"
},
"admin": {
"description": "Diese Einstellungen für Organisatoren ermöglichen dir die Kontrolle über dein Meeting. Nur Organisatoren haben Zugriff auf diese Optionen.",
"description": "Diese Einstellungen für Organisierende ermöglichen dir die Kontrolle über dein Meeting. Nur Organisierende haben Zugriff auf diese Optionen.",
"access": {
"title": "Raumzugang",
"description": "Diese Einstellungen gelten auch für zukünftige Ausgaben dieses Meetings.",
"description": "Diese Einstellungen gelten auch für zukünftige Meetings in diesem Raum.",
"type": "Zugangsarten für Meetings",
"levels": {
"public": {
@@ -468,28 +475,28 @@
}
},
"moderation": {
"title": "Moderation des Meetings",
"description": "Diese Einstellungen beschränken die Aktionen, die Mitwirkende während des Meetings durchführen können.",
"title": "Meeting-Moderation",
"description": "Diese Einstellungen beschränken die Aktionen, die Teilnehmende während des Meetings durchführen können.",
"microphone": {
"label": "Mikrofon einschalten",
"description": ""
},
"camera": {
"label": "Video aktivieren",
"label": "Video einschalten",
"description": ""
},
"screenshare": {
"label": "Ihren Bildschirm teilen",
"description": "Wenn Sie diese Option deaktivieren, können Teilnehmer ihren Bildschirm nicht mehr teilen, und laufende Bildschirmfreigaben werden sofort beendet."
"label": "Bildschirm teilen",
"description": "Wenn du diese Option deaktivierst, können Teilnehmende ihren Bildschirm nicht mehr teilen. Laufende Bildschirmfreigaben werden sofort beendet."
}
}
},
"rating": {
"submit": "Absenden",
"question": "Wie bewertest du die Qualität deines Gesprächs?",
"question": "Wie bewertest du die Qualität dieses Meetings?",
"levels": {
"min": "sehr schlecht",
"max": "ausgezeichnet"
"max": "hervorragend"
}
},
"openFeedback": {
@@ -499,7 +506,7 @@
"skip": "Überspringen"
},
"confirmationMessage": {
"heading": "Vielen Dank für deine Rückmeldung",
"heading": "Vielen Dank für dein Feedback",
"body": "Unser Produktteam nimmt sich Zeit, dein Feedback sorgfältig zu prüfen. Wir melden uns so bald wie möglich bei dir."
},
"authenticationMessage": {
@@ -511,9 +518,9 @@
"participants": {
"subheading": "Im Raum",
"you": "Du",
"unknown": "Unbekannter Teilnehmer",
"unknown": "Unbekannte Person",
"host": "Host",
"contributors": "Mitwirkende",
"contributors": "Teilnehmende",
"collapsable": {
"open": "{{name}}-Liste öffnen",
"close": "{{name}}-Liste schließen"
@@ -522,7 +529,7 @@
"muteParticipant": "{{name}}s Mikrofon ausschalten",
"muteParticipantAlert": {
"heading": "{{name}} stummschalten",
"description": "{{name}} für alle Teilnehmer stummschalten? {{name}} kann sich nur selbst wieder einschalten.",
"description": "{{name}} für alle Teilnehmenden stummschalten? {{name}} kann sich nur selbst wieder einschalten.",
"confirm": "Stummschalten",
"cancel": "Abbrechen"
},
@@ -547,8 +554,8 @@
},
"participantMenu": {
"remove": {
"label": "Vom Anruf ausschließen",
"ariaLabel": "{{name}} vom Anruf ausschließen"
"label": "Vom Meeting ausschließen",
"ariaLabel": "{{name}} vom Meeting ausschließen"
},
"unpin": {
"label": "Lösen",
@@ -582,8 +589,8 @@
"started": "Aufzeichnung läuft"
},
"limitReachedAlert": {
"title": "Aufnahmelimit überschritten",
"description": "Die Aufnahme hat die zulässige Höchstdauer{{duration_message}} überschritten. Sie wird jetzt automatisch gespeichert.",
"title": "Aufzeichnungslimit überschritten",
"description": "Die Aufzeichnung hat die zulässige Höchstdauer{{duration_message}} überschritten. Sie wird jetzt automatisch gespeichert.",
"durationMessage": " von {{duration}}",
"button": "OK"
}
@@ -595,12 +602,12 @@
"enable": "Anheften",
"disable": "Lösen"
},
"effects": "Visuelle Effekte anwenden",
"effects": "Hintergründe und Effekte anwenden",
"muteParticipant": "{{name}} stummschalten",
"fullScreen": "Vollbild"
},
"shortcutsPanel": {
"title": "Tastenkombinationen",
"title": "Tastenkürzel",
"categories": {
"navigation": "Navigation",
"media": "Medien",
@@ -611,13 +618,13 @@
"focus-toolbar": "Fokus auf die untere Symbolleiste",
"toggle-microphone": "Mikrofon umschalten",
"toggle-camera": "Kamera umschalten",
"push-to-talk": "Push-to-talk (gedrückt halten zum Einschalten)",
"push-to-talk": "Push-to-talk",
"reaction": "Reaktionspanel",
"fullscreen": "Vollbild umschalten",
"recording": "Aufnahmepanel umschalten",
"raise-hand": "Hand heben oder senken",
"toggle-chat": "Chat anzeigen/ausblenden",
"toggle-participants": "Teilnehmer anzeigen/ausblenden",
"toggle-participants": "Teilnehmende anzeigen/ausblenden",
"open-shortcuts-settings": "Tastenkürzel-Einstellungen öffnen"
},
"sr": {
@@ -635,8 +642,8 @@
}
},
"fullScreenWarning": {
"message": "Um eine Endlosschleife zu vermeiden, teile nicht deinen gesamten Bildschirm. Teile stattdessen einen Tab oder ein anderes Fenster.",
"stop": "Präsentation beenden",
"message": "Damit dein Bildschirm nicht unendlich gespiegelt wird, teile nicht deinen gesamten Bildschirm. Teile stattdessen einen Tab oder ein anderes Fenster.",
"stop": "Teilen beenden",
"ignore": "Ignorieren"
},
"participantTile": {
+2 -2
View File
@@ -4,7 +4,7 @@
"joinButton": "Mit Visio beitreten",
"copyLinkTooltip": "Link kopieren",
"resetLabel": "Zurücksetzen",
"participantLimit": "Bis zu 150 Teilnehmer.",
"popupBlocked": "Popup wurde blockiert. Bitte erlauben Sie Popups für diese Website."
"participantLimit": "Bis zu 150 Teilnehmende.",
"popupBlocked": "Popup wurde blockiert. Bitte erlaube Popups für diese Website."
}
}
+32 -21
View File
@@ -1,46 +1,46 @@
{
"account": {
"currentlyLoggedAs": "Sie sind derzeit angemeldet als <0>{{user}}</0>",
"currentlyLoggedAs": "Du bist derzeit angemeldet als <0>{{user}}</0>",
"heading": "Konto",
"youAreNotLoggedIn": "Sie sind nicht angemeldet.",
"nameLabel": "Ihr Name",
"youAreNotLoggedIn": "Du bist nicht angemeldet.",
"nameLabel": "Dein Name",
"authentication": "Authentifizierung",
"nameError": "Ihr Name darf nicht leer sein"
"nameError": "Dein Name darf nicht leer sein"
},
"preferences": {
"title": "Einstellungen",
"idleDisconnectModal": {
"label": "Anrufe ohne Teilnehmer verlassen",
"description": "Verlässt automatisch einen Anruf nach einigen Minuten, wenn kein anderer Teilnehmer beitritt"
"label": "Meetings ohne Teilnehmende verlassen",
"description": "Meetings nach einigen Minuten automatisch verlassen, wenn keine andere Person beitritt"
}
},
"audio": {
"microphone": {
"heading": "Mikrofon",
"label": "Wählen Sie Ihre Audioeingabe",
"label": "Audioeingabe wählen",
"disabled": "Mikrofon deaktiviert"
},
"noiseReduction": {
"label": "Rauschunterdrückung",
"heading": "Rauschunterdrückung",
"label": "Geräuschunterdrückung",
"heading": "Geräuschunterdrückung",
"ariaLabel": {
"enable": "Rauschunterdrückung aktivieren",
"disable": "Rauschunterdrückung deaktivieren"
"enable": "Geräuschunterdrückung aktivieren",
"disable": "Geräuschunterdrückung deaktivieren"
}
},
"speakers": {
"heading": "Lautsprecher",
"label": "Wählen Sie Ihre Audioausgabe",
"label": "Audioausgabe wählen",
"test": "Testen",
"ongoingTest": "Soundtest läuft…",
"safariWarning": "Die Lautsprecherauswahl ist auf Safari aufgrund von Browser-Einschränkungen noch nicht verfügbar."
"safariWarning": "Die Audioausgabeauswahl ist auf Safari aufgrund von Browser-Einschränkungen noch nicht verfügbar."
},
"permissionsRequired": "Berechtigungen erforderlich"
},
"video": {
"camera": {
"heading": "Kamera",
"label": "Wählen Sie Ihre Videoeingabe aus",
"label": "Videoeingabe wählen",
"disabled": "Kamera deaktiviert",
"previewAriaLabel": {
"enabled": "Videovorschau aktiviert",
@@ -50,18 +50,18 @@
"resolution": {
"heading": "Auflösung",
"publish": {
"label": "Wählen Sie Ihre maximale Sendeauflösung (max.)",
"label": "Wähle die maximale Auflösung beim Senden",
"items": {
"high": "Hohe Auflösung",
"medium": "Standardauflösung",
"medium": "Mittlere Auflösung",
"low": "Niedrige Auflösung"
}
},
"subscribe": {
"label": "Wählen Sie Ihre Empfangsauflösung (max.)",
"label": "Wähle die maximale Auflösung beim Empfangen",
"items": {
"high": "Hohe Auflösung (automatisch)",
"medium": "Standardauflösung",
"medium": "Mittlere Auflösung",
"low": "Niedrige Auflösung"
}
}
@@ -82,15 +82,15 @@
}
},
"notifications": {
"heading": "Tonbenachrichtigungen",
"label": "Tonbenachrichtigungen für",
"heading": "Benachrichtigungen mit Ton",
"label": "Benachrichtigungen mit Ton für",
"actions": {
"disable": "Deaktivieren",
"enable": "Aktivieren"
},
"items": {
"participantJoined": {
"label": "Teilnehmer beigetreten"
"label": "Person beigetreten"
},
"handRaised": {
"label": "Hand gehoben"
@@ -116,6 +116,17 @@
"accessibility": {
"announceReactions": {
"label": "Reaktionen vorlesen"
},
"captions": {
"heading": "Untertitel",
"textSize": {
"label": "Untertitel-Schriftgröße",
"options": {
"small": "Klein",
"medium": "Mittel",
"large": "Groß"
}
}
}
},
"tabs": {
+1 -1
View File
@@ -47,7 +47,7 @@
},
"loginHint": {
"title": "Log in with your account",
"body": "Instead of waiting, log in with your ProConnect account.",
"body": "Instead of waiting, log in with your account.",
"button": {
"ariaLabel": "Close the suggestion",
"label": "OK"
+1 -1
View File
@@ -10,7 +10,7 @@
"joinMeetingTipContent": "You can join a meeting by pasting its full link in the browser's address bar.",
"joinMeetingTipHeading": "Did you know?",
"loginToCreateMeeting": "Login to create a meeting",
"moreLinkLabel": "Learn more - new tab",
"moreLinkLabel": "Learn more about {{appTitle}} - new tab",
"moreLink": "Learn more",
"moreAbout": "about {{appTitle}}",
"createMenu": {
@@ -9,6 +9,7 @@
},
"muted": "{{name}} has muted your microphone. No participant can hear you.",
"openChat": "Open chat",
"chatMessageReceived": "{{name}} says: {{message}}",
"lowerHand": {
"auto": "It seems you have started speaking, so your hand will be lowered.",
"dismiss": "Keep hand raised"
+14 -7
View File
@@ -322,11 +322,13 @@
"closeButton": "Hide {{content}}"
},
"chat": {
"disclaimer": "The messages are visible to participants only at the time they are sent. All messages are deleted at the end of the call."
"disclaimer": "The messages are visible to participants only at the time they are sent. All messages are deleted at the end of the call.",
"messagesLog": "Chat messages"
},
"moreTools": {
"body": "Access more tools to enhance your meetings.",
"moreLink": "Learn more",
"linkAriaLabel": "Open documentation about tools - opens in new window",
"moreLink": "Open documentation",
"tools": {
"transcript": {
"title": "Transcribe",
@@ -370,11 +372,13 @@
"starting": "Starting…",
"anotherModeStarted": "Screen recording is running. Switch panels and stop it."
},
"linkMore": "Learn more",
"linkMore": "Open documentation",
"linkAriaLabel": "Open documentation about transcription - opens in new window",
"notAdminOrOwner": {
"heading": "Restricted Access",
"body": "For security reasons, only the meeting creator or an admin can start a transcription (beta).",
"linkMore": "Learn more",
"linkMore": "Open documentation",
"linkAriaLabel": "Open documentation about transcription access - opens in new window",
"dividerLabel": "OR",
"login": {
"heading": "Login Required",
@@ -389,7 +393,8 @@
"premium": {
"heading": "Premium feature",
"body": "This feature is reserved for public agents. If your email address is not authorized, please contact support to get access.",
"linkMore": "Learn more",
"linkMore": "Open documentation",
"linkAriaLabel": "Open documentation about premium access - opens in new window",
"dividerLabel": "OR",
"login": {
"heading": "You are not logged in!",
@@ -406,7 +411,8 @@
"heading": "Record this call for later",
"body": "Record up to {{max_duration}} of meeting.",
"bodyWithoutMaxDuration": "Record your meeting without limit.",
"linkMore": "Learn more",
"linkMore": "Open documentation",
"linkAriaLabel": "Open documentation about recording - opens in new window",
"details": {
"receiver": "The recording will be sent to the host and co-hosts.",
"destination": "This recording will be temporarily stored on our servers",
@@ -424,7 +430,8 @@
"notAdminOrOwner": {
"heading": "Restricted Access",
"body": "For security reasons, only the meeting creator or an admin can start a recording (beta).",
"linkMore": "Learn more",
"linkMore": "Open documentation",
"linkAriaLabel": "Open documentation about recording access - opens in new window",
"dividerLabel": "OR",
"login": {
"heading": "Login Required",
+11
View File
@@ -116,6 +116,17 @@
"accessibility": {
"announceReactions": {
"label": "Announce reactions aloud"
},
"captions": {
"heading": "Captions",
"textSize": {
"label": "Caption text size",
"options": {
"small": "Small",
"medium": "Medium",
"large": "Large"
}
}
}
},
"tabs": {
+1 -1
View File
@@ -47,7 +47,7 @@
},
"loginHint": {
"title": "Connectez-vous avec votre compte",
"body": "Au lieu de patienter, connectez-vous avec votre compte ProConnect.",
"body": "Au lieu de patienter, connectez-vous avec votre compte.",
"button": {
"ariaLabel": "Fermer la suggestion",
"label": "OK"
+1 -1
View File
@@ -10,7 +10,7 @@
"joinMeetingTipContent": "Vous pouvez rejoindre une réunion en copiant directement son lien complet dans la barre d'adresse du navigateur.",
"joinMeetingTipHeading": "Astuce",
"loginToCreateMeeting": "Connectez-vous pour créer une réunion",
"moreLinkLabel": "En savoir plus - nouvelle fenêtre",
"moreLinkLabel": "En savoir plus sur {{appTitle}} - nouvelle fenêtre",
"moreLink": "En savoir plus",
"moreAbout": "sur {{appTitle}}",
"createMenu": {
@@ -9,6 +9,7 @@
},
"muted": "{{name}} a coupé votre micro. Aucun participant ne peut l'entendre.",
"openChat": "Ouvrir le chat",
"chatMessageReceived": "{{name}} dit : {{message}}",
"lowerHand": {
"auto": "Il semblerait que vous ayez pris la parole, donc la main va être baissée.",
"dismiss": "Laisser la main levée"
+16 -9
View File
@@ -322,11 +322,13 @@
"closeButton": "Masquer {{content}}"
},
"chat": {
"disclaimer": "Les messages sont visibles par les participants uniquement au moment de\nleur envoi. Tous les messages sont supprimés à la fin de l'appel."
"disclaimer": "Les messages sont visibles par les participants uniquement au moment de\nleur envoi. Tous les messages sont supprimés à la fin de l'appel.",
"messagesLog": "Messages du chat"
},
"moreTools": {
"body": "Accèder à d'avantage d'outils pour améliorer vos réunions.",
"moreLink": "En savoir plus",
"body": "Accéder à davantage d'outils pour améliorer vos réunions.",
"linkAriaLabel": "Ouvrir la documentation sur les outils - ouvre dans une nouvelle fenêtre",
"moreLink": "Ouvrir la documentation",
"tools": {
"transcript": {
"title": "Transcrire",
@@ -370,11 +372,13 @@
"starting": "Démarrage…",
"anotherModeStarted": "Un enregistrement est en cours. <br/> Changez de menu et arrêtez le."
},
"linkMore": "En savoir plus",
"linkMore": "Ouvrir la documentation",
"linkAriaLabel": "Ouvrir la documentation sur la transcription - ouvre dans une nouvelle fenêtre",
"notAdminOrOwner": {
"heading": "Accès restreint",
"body": "Pour des raisons de sécurité, seul le créateur ou un administrateur de la réunion peut lancer une transcription (beta).",
"linkMore": "En savoir plus",
"linkMore": "Ouvrir la documentation",
"linkAriaLabel": "Ouvrir la documentation sur l'accès à la transcription - ouvre dans une nouvelle fenêtre",
"dividerLabel": "OU",
"login": {
"heading": "Connexion requise",
@@ -389,7 +393,7 @@
"premium": {
"heading": "Fonctionnalité premium",
"body": "Cette fonctionnalité est réservée aux agents publics. Si votre adresse email nest pas autorisée, contactez le support pour obtenir l'accès.",
"linkMore": "En savoir plus",
"linkMore": "Ouvrir la documentation",
"dividerLabel": "OU",
"login": {
"heading": "Vous n'êtes pas connecté !",
@@ -399,14 +403,16 @@
"heading": "Demander à l'organisateur",
"body": "L'hôte recevra une notification et pourra démarrer la transcription pour vous.",
"buttonLabel": "Demander"
}
},
"linkAriaLabel": "Ouvrir la documentation sur l'accès premium - ouvre dans une nouvelle fenêtre"
}
},
"screenRecording": {
"heading": "Enregistrez cet appel pour plus tard",
"body": "Enregistrez jusqu'à {{max_duration}} de réunion.",
"bodyWithoutMaxDuration": "Enregistrez votre réunion sans limite.",
"linkMore": "En savoir plus",
"linkMore": "Ouvrir la documentation",
"linkAriaLabel": "Ouvrir la documentation sur l'enregistrement - ouvre dans une nouvelle fenêtre",
"details": {
"receiver": "L'enregistrement sera envoyé à l'organisateur et aux coorganisateurs.",
"destination": "Cet enregistrement sera conservé temporairement sur nos serveurs",
@@ -424,7 +430,8 @@
"notAdminOrOwner": {
"heading": "Accès restreint",
"body": "Pour des raisons de sécurité, seul le créateur ou un administrateur de la réunion peut lancer un enregistrement (beta).",
"linkMore": "En savoir plus",
"linkMore": "Ouvrir la documentation",
"linkAriaLabel": "Ouvrir la documentation sur l'accès à l'enregistrement - ouvre dans une nouvelle fenêtre",
"dividerLabel": "OU",
"login": {
"heading": "Connexion requise",
+11
View File
@@ -116,6 +116,17 @@
"accessibility": {
"announceReactions": {
"label": "Vocaliser les réactions"
},
"captions": {
"heading": "Sous-titres",
"textSize": {
"label": "Taille du texte des sous-titres",
"options": {
"small": "Petit",
"medium": "Moyen",
"large": "Grand"
}
}
}
},
"tabs": {
+1 -1
View File
@@ -46,7 +46,7 @@
},
"loginHint": {
"title": "Log in met je account",
"body": "In plaats van te wachten, log in met je ProConnect-account.",
"body": "In plaats van te wachten, log in met je account.",
"button": {
"ariaLabel": "Sluit de suggestie",
"label": "OK"
+1 -1
View File
@@ -10,7 +10,7 @@
"joinMeetingTipContent": "U kunt deelnemen aan een vergadering door de volledige link in de adresbalk van de browser te plakken.",
"joinMeetingTipHeading": "Wist u dat?",
"loginToCreateMeeting": "Log in om een vergadering te maken",
"moreLinkLabel": "Meer informatie - nieuw tabblad",
"moreLinkLabel": "Meer informatie over {{appTitle}} - nieuw tabblad",
"moreLink": "Meer informatie",
"moreAbout": "over {{appTitle}}",
"createMenu": {
@@ -9,6 +9,7 @@
},
"muted": "{{name}} heeft uw microfoon gedempt. Deelnemers kunnen u niet horen.",
"openChat": "Open chat",
"chatMessageReceived": "{{name}} zegt: {{message}}",
"lowerHand": {
"auto": "Het lijkt erop dat u bent begonnen te spreken, dus we laten uw hand zakken.",
"dismiss": "Houdt uw hand opgestoken"
+14 -7
View File
@@ -322,11 +322,13 @@
"closeButton": "Verberg {{content}}"
},
"chat": {
"disclaimer": "De berichten zijn alleen voor de deelnemers zichtbaar op het moment dat ze worden verzonden. Alle berichten worden verwijderd aan het einde van het gesprek."
"disclaimer": "De berichten zijn alleen voor de deelnemers zichtbaar op het moment dat ze worden verzonden. Alle berichten worden verwijderd aan het einde van het gesprek.",
"messagesLog": "Chatberichten"
},
"moreTools": {
"body": "Je krijgt toegang tot meer tools om je vergaderingen te verbeteren.",
"moreLink": "Lees meer",
"linkAriaLabel": "Documentatie over tools openen - opent in nieuw venster",
"moreLink": "Documentatie openen",
"tools": {
"transcript": {
"title": "Transcriberen",
@@ -370,11 +372,13 @@
"starting": "Wordt gestart…",
"anotherModeStarted": "Er loopt een opname. <br/> Ga naar het menu en stop deze."
},
"linkMore": "Meer informatie",
"linkMore": "Documentatie openen",
"linkAriaLabel": "Documentatie over transcriptie openen - opent in nieuw venster",
"notAdminOrOwner": {
"heading": "Toegang beperkt",
"body": "Om veiligheidsredenen kan alleen de maker of een beheerder van de vergadering een transcriptie starten (beta).",
"linkMore": "Meer informatie",
"linkMore": "Documentatie openen",
"linkAriaLabel": "Documentatie over transcriptietoegang openen - opent in nieuw venster",
"dividerLabel": "OF",
"login": {
"heading": "Inloggen vereist",
@@ -389,7 +393,8 @@
"premium": {
"heading": "Premiumfunctie",
"body": "Deze functie is voorbehouden aan openbare medewerkers. Als uw e-mailadres niet is toegestaan, neem dan contact op met de support om toegang te krijgen.",
"linkMore": "Meer informatie",
"linkMore": "Documentatie openen",
"linkAriaLabel": "Documentatie over premiumtoegang openen - opent in nieuw venster",
"dividerLabel": "OF",
"login": {
"heading": "Inloggen vereist",
@@ -406,7 +411,8 @@
"heading": "Neem dit gesprek op voor later",
"body": "Neem tot {{max_duration}} van de vergadering op.",
"bodyWithoutMaxDuration": "Neem je vergadering onbeperkt op.",
"linkMore": "Meer informatie",
"linkMore": "Documentatie openen",
"linkAriaLabel": "Documentatie over opname openen - opent in nieuw venster",
"details": {
"receiver": "De opname wordt verzonden naar de organisator en co-organisatoren.",
"destination": "Deze opname wordt tijdelijk op onze servers bewaard",
@@ -424,7 +430,8 @@
"notAdminOrOwner": {
"heading": "Toegang beperkt",
"body": "Om veiligheidsredenen kan alleen de maker of een beheerder van de vergadering een opname starten (beta).",
"linkMore": "Meer informatie",
"linkMore": "Documentatie openen",
"linkAriaLabel": "Documentatie over opnametoegang openen - opent in nieuw venster",
"dividerLabel": "OF",
"login": {
"heading": "Inloggen vereist",
+17
View File
@@ -113,12 +113,29 @@
"label": "Taal"
},
"settingsButtonLabel": "Instellingen",
"accessibility": {
"announceReactions": {
"label": "Reacties hardop aankondigen"
},
"captions": {
"heading": "Ondertitels",
"textSize": {
"label": "Ondertiteltekstgrootte",
"options": {
"small": "Klein",
"medium": "Gemiddeld",
"large": "Groot"
}
}
}
},
"tabs": {
"account": "Profiel",
"audio": "Audio",
"video": "Video",
"general": "Algemeen",
"notifications": "Meldingen",
"accessibility": "Toegankelijkheid",
"transcription": "Transcriptie",
"shortcuts": "Sneltoetsen"
}
+15
View File
@@ -2,12 +2,22 @@ import { proxy, subscribe } from 'valtio'
import { STORAGE_KEYS } from '@/utils/storageKeys'
import { deserializeToProxyMap } from '@/utils/valtio'
export type CaptionTextSize = 'small' | 'medium' | 'large'
export const CAPTION_TEXT_SIZE_OPTIONS: CaptionTextSize[] = [
'small',
'medium',
'large',
]
type AccessibilityState = {
announceReactions: boolean
captionTextSize: CaptionTextSize
}
const DEFAULT_STATE: AccessibilityState = {
announceReactions: false,
captionTextSize: 'medium',
}
function getAccessibilityState(): AccessibilityState {
@@ -15,6 +25,10 @@ function getAccessibilityState(): AccessibilityState {
const stored = localStorage.getItem(STORAGE_KEYS.ACCESSIBILITY)
if (stored) {
const parsed = JSON.parse(stored)
const validCaptionSizes = CAPTION_TEXT_SIZE_OPTIONS
const captionTextSize = validCaptionSizes.includes(parsed.captionTextSize)
? parsed.captionTextSize
: DEFAULT_STATE.captionTextSize
return {
...DEFAULT_STATE,
...parsed,
@@ -22,6 +36,7 @@ function getAccessibilityState(): AccessibilityState {
typeof parsed.announceReactions === 'boolean'
? parsed.announceReactions
: DEFAULT_STATE.announceReactions,
captionTextSize,
}
}
@@ -64,6 +64,7 @@ backend:
AWS_S3_SECRET_ACCESS_KEY: password
AWS_STORAGE_BUCKET_NAME: meet-media-storage
AWS_S3_REGION_NAME: local
AWS_S3_SIGNATURE_VERSION: s3v4
MEDIA_BASE_URL: https://meet.127.0.0.1.nip.io
RECORDING_ENABLE: True
RECORDING_STORAGE_EVENT_ENABLE: True
@@ -80,6 +81,10 @@ backend:
APPLICATION_JWT_AUDIENCE: https://meet.127.0.0.1.nip.io/external-api/v1.0/
APPLICATION_JWT_SECRET_KEY: devKeyApplication
APPLICATION_BASE_URL: https://meet.127.0.0.1.nip.io
FILE_UPLOAD_ENABLED: True
AWS_S3_DOMAIN_REPLACE: https://minio.127.0.0.1.nip.io
CELERY_ENABLED: True
CELERY_BROKER_URL: redis://default:pass@redis-master:6379/1
migrate:
@@ -311,3 +316,18 @@ ingressMedia:
serviceMedia:
host: minio.meet.svc.cluster.local
port: 9000
ingressMediaFiles:
enabled: true
host: meet.127.0.0.1.nip.io
annotations:
nginx.ingress.kubernetes.io/auth-url: https://meet.127.0.0.1.nip.io/api/v1.0/files/media-auth/
nginx.ingress.kubernetes.io/auth-response-headers: "Authorization, X-Amz-Date, X-Amz-Content-SHA256"
nginx.ingress.kubernetes.io/upstream-vhost: minio.meet.svc.cluster.local:9000
nginx.ingress.kubernetes.io/rewrite-target: /meet-media-storage/files/$1
serviceMediaFiles:
host: minio.meet.svc.cluster.local
port: 9000
+4 -2
View File
@@ -3,6 +3,8 @@ apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: minio
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: 10m
spec:
rules:
- host: "minio.127.0.0.1.nip.io"
@@ -14,7 +16,7 @@ spec:
service:
name: minio
port:
number: 9001
number: 9000
tls:
- hosts:
- minio.127.0.0.1.nip.io
@@ -130,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 service restart meet --wait --json && \
sleep 15 && \
/usr/bin/mc event add meet/meet-media-storage arn:minio:sqs::meet-webhook:webhook --event put && \
/usr/bin/mc event add meet/meet-media-storage arn:minio:sqs::meet-webhook:webhook --event put --prefix "recordings" && \
exit 0
restartPolicy: Never
backoffLimit: 1
+1 -1
View File
@@ -1,4 +1,4 @@
apiVersion: v2
type: application
name: meet
version: 0.0.16
version: 0.0.17
+9
View File
@@ -175,6 +175,15 @@ Requires top level scope
{{ include "meet.fullname" . }}-summary
{{- end }}
{{/*
Full name for the Celery Backend
Requires top level scope
*/}}
{{- define "meet.celeryBackend.fullname" -}}
{{ include "meet.fullname" . }}-celery-backend
{{- end }}
{{/*
Full name for the Celery Transcribe
@@ -0,0 +1,153 @@
{{- $envVars := include "meet.env.transformDict" (mergeOverwrite (default dict .Values.backend.envVars) (default dict .Values.celeryBackend.envVars)) -}}
{{- $fullName := include "meet.celeryBackend.fullname" . -}}
{{- $component := "celery-backend" -}}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ $fullName }}
annotations:
{{- with .Values.celeryBackend.dpAnnotations }}
{{- toYaml . | nindent 4 }}
{{- end }}
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "meet.common.labels" (list . $component) | nindent 4 }}
spec:
replicas: {{ .Values.celeryBackend.replicas }}
selector:
matchLabels:
{{- include "meet.common.selectorLabels" (list . $component) | nindent 6 }}
template:
metadata:
annotations:
{{- with .Values.celeryBackend.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "meet.common.selectorLabels" (list . $component) | nindent 8 }}
spec:
{{- if $.Values.image.credentials }}
imagePullSecrets:
- name: {{ include "meet.secret.dockerconfigjson.name" (dict "fullname" (include "meet.fullname" .) "imageCredentials" $.Values.image.credentials) }}
{{- end }}
shareProcessNamespace: {{ .Values.celeryBackend.shareProcessNamespace }}
containers:
{{- with .Values.celeryBackend.sidecars }}
{{- toYaml . | nindent 8 }}
{{- end }}
- name: {{ .Chart.Name }}
image: "{{ (.Values.backend.image | default dict).repository | default .Values.image.repository }}:{{ (.Values.backend.image | default dict).tag | default .Values.image.tag }}"
imagePullPolicy: {{ (.Values.backend.image | default dict).pullPolicy | default .Values.image.pullPolicy }}
{{- with .Values.celeryBackend.command }}
command:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.celeryBackend.args }}
args:
{{- toYaml . | nindent 12 }}
{{- end }}
env:
{{- if $envVars }}
{{- $envVars | indent 12 }}
{{- end }}
{{- with .Values.celeryBackend.securityContext }}
securityContext:
{{- toYaml . | nindent 12 }}
{{- end }}
ports:
- name: http
containerPort: {{ .Values.celeryBackend.service.targetPort }}
protocol: TCP
{{- if .Values.celeryBackend.probes.liveness }}
livenessProbe:
{{- include "meet.probes.abstract" (merge .Values.celeryBackend.probes.liveness (dict "targetPort" .Values.celeryBackend.service.targetPort )) | nindent 12 }}
{{- end }}
{{- if .Values.celeryBackend.probes.readiness }}
readinessProbe:
{{- include "meet.probes.abstract" (merge .Values.celeryBackend.probes.readiness (dict "targetPort" .Values.celeryBackend.service.targetPort )) | nindent 12 }}
{{- end }}
{{- if .Values.celeryBackend.probes.startup }}
startupProbe:
{{- include "meet.probes.abstract" (merge .Values.celeryBackend.probes.startup (dict "targetPort" .Values.celeryBackend.service.targetPort )) | nindent 12 }}
{{- end }}
{{- with .Values.celeryBackend.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
volumeMounts:
{{- range $index, $value := .Values.mountFiles }}
- name: "files-{{ $index }}"
mountPath: {{ $value.path }}
subPath: content
{{- end }}
{{- range $name, $volume := .Values.celeryBackend.persistence }}
- name: "{{ $name }}"
mountPath: "{{ $volume.mountPath }}"
{{- end }}
{{- range .Values.celeryBackend.extraVolumeMounts }}
- name: {{ .name }}
mountPath: {{ .mountPath }}
subPath: {{ .subPath | default "" }}
readOnly: {{ .readOnly }}
{{- end }}
{{- with .Values.celeryBackend.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.celeryBackend.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.celeryBackend.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
volumes:
{{- range $index, $value := .Values.mountFiles }}
- name: "files-{{ $index }}"
configMap:
name: "{{ include "meet.fullname" $ }}-files-{{ $index }}"
{{- end }}
{{- range $name, $volume := .Values.celeryBackend.persistence }}
- name: "{{ $name }}"
{{- if eq $volume.type "emptyDir" }}
emptyDir: {}
{{- else }}
persistentVolumeClaim:
claimName: "{{ $fullName }}-{{ $name }}"
{{- end }}
{{- end }}
{{- range .Values.celeryBackend.extraVolumes }}
- name: {{ .name }}
{{- if .existingClaim }}
persistentVolumeClaim:
claimName: {{ .existingClaim }}
{{- else if .hostPath }}
hostPath:
{{ toYaml .hostPath | nindent 12 }}
{{- else if .csi }}
csi:
{{- toYaml .csi | nindent 12 }}
{{- else if .configMap }}
configMap:
{{- toYaml .configMap | nindent 12 }}
{{- else if .emptyDir }}
emptyDir:
{{- toYaml .emptyDir | nindent 12 }}
{{- else }}
emptyDir: {}
{{- end }}
{{- end }}
---
{{ if .Values.celeryBackend.pdb.enabled }}
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: {{ $fullName }}
namespace: {{ .Release.Namespace | quote }}
spec:
maxUnavailable: 1
selector:
matchLabels:
{{- include "meet.common.selectorLabels" (list . $component) | nindent 6 }}
{{ end }}
@@ -0,0 +1,83 @@
{{- if .Values.ingressMediaFiles.enabled -}}
{{- $fullName := include "meet.fullname" . -}}
{{- if and .Values.ingressMediaFiles.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }}
{{- if not (hasKey .Values.ingressMediaFiles.annotations "kubernetes.io/ingress.class") }}
{{- $_ := set .Values.ingressMediaFiles.annotations "kubernetes.io/ingress.class" .Values.ingressMediaFiles.className }}
{{- end }}
{{- end }}
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1beta1
{{- else -}}
apiVersion: extensions/v1beta1
{{- end }}
kind: Ingress
metadata:
name: {{ $fullName }}-media-files
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "meet.labels" . | nindent 4 }}
{{- with .Values.ingressMediaFiles.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if and .Values.ingressMediaFiles.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
ingressClassName: {{ .Values.ingressMediaFiles.className }}
{{- end }}
{{- if .Values.ingressMediaFiles.tls.enabled }}
tls:
{{- if .Values.ingressMediaFiles.host }}
- secretName: {{ .Values.ingressMediaFiles.tls.secretName | default (printf "%s-tls" $fullName) | quote }}
hosts:
- {{ .Values.ingressMediaFiles.host | quote }}
{{- end }}
{{- range .Values.ingressMediaFiles.tls.additional }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- if .Values.ingressMediaFiles.host }}
- host: {{ .Values.ingressMediaFiles.host | quote }}
http:
paths:
- path: {{ .Values.ingressMediaFiles.path | quote }}
{{- if semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion }}
pathType: ImplementationSpecific
{{- end }}
backend:
{{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
service:
name: {{ $fullName }}-media-files
port:
number: {{ .Values.serviceMediaFiles.port }}
{{- else }}
serviceName: {{ $fullName }}-media-files
servicePort: {{ .Values.serviceMediaFiles.port }}
{{- end }}
{{- end }}
{{- range .Values.ingressMediaFiles.hosts }}
- host: {{ . | quote }}
http:
paths:
- path: {{ $.Values.ingressMediaFiles.path | quote }}
{{- if semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion }}
pathType: ImplementationSpecific
{{- end }}
backend:
{{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
service:
name: {{ $fullName }}-media-files
port:
number: {{ .Values.serviceMediaFiles.port }}
{{- else }}
serviceName: {{ $fullName }}-media-files
servicePort: {{ .Values.serviceMediaFiles.port }}
{{- end }}
{{- end }}
{{- end }}
@@ -0,0 +1,14 @@
{{- $fullName := include "meet.fullname" . -}}
{{- $component := "media-files" -}}
apiVersion: v1
kind: Service
metadata:
name: {{ $fullName }}-media-files
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "meet.common.labels" (list . $component) | nindent 4 }}
annotations:
{{- toYaml $.Values.serviceMediaFiles.annotations | nindent 4 }}
spec:
type: ExternalName
externalName: {{ $.Values.serviceMediaFiles.host }}
+152 -5
View File
@@ -37,7 +37,7 @@ ingress:
## @param ingress.hosts Additional host to configure for the Ingress
hosts: []
# - chart-example.local
## @param ingress.tls.enabled Weather to enable TLS for the Ingress
## @param ingress.tls.enabled Whether to enable TLS for the Ingress
## @param ingress.tls.secretName Secret name for TLS config
## @skip ingress.tls.additional
## @extra ingress.tls.additional[].secretName Secret name for additional TLS config
@@ -62,7 +62,7 @@ ingressWebhook:
## @param ingressWebhook.hosts Additional host to configure for the Ingress
hosts: []
# - chart-example.local
## @param ingressWebhook.tls.enabled Weather to enable TLS for the Ingress
## @param ingressWebhook.tls.enabled Whether to enable TLS for the Ingress
## @param ingressWebhook.tls.secretName Secret name for TLS config
## @skip ingressWebhook.tls.additional
## @extra ingressWebhook.tls.additional[].secretName Secret name for additional TLS config
@@ -88,7 +88,7 @@ ingressAdmin:
## @param ingressAdmin.hosts Additional host to configure for the Ingress
hosts: [ ]
# - chart-example.local
## @param ingressAdmin.tls.enabled Weather to enable TLS for the Ingress
## @param ingressAdmin.tls.enabled Whether to enable TLS for the Ingress
## @param ingressAdmin.tls.secretName Secret name for TLS config
## @skip ingressAdmin.tls.additional
## @extra ingressAdmin.tls.additional[].secretName Secret name for additional TLS config
@@ -110,7 +110,7 @@ ingressMedia:
## @param ingressMedia.hosts Additional host to configure for the Ingress
hosts: [ ]
# - chart-example.local
## @param ingressMedia.tls.enabled Weather to enable TLS for the Ingress
## @param ingressMedia.tls.enabled Whether to enable TLS for the Ingress
## @param ingressMedia.tls.secretName Secret name for TLS config
## @skip ingressMedia.tls.additional
## @extra ingressMedia.tls.additional[].secretName Secret name for additional TLS config
@@ -125,6 +125,7 @@ ingressMedia:
## @param ingressMedia.annotations.nginx.ingress.kubernetes.io/upstream-vhost
## @param ingressMedia.annotations.nginx.ingress.kubernetes.io/configuration-snippet
annotations:
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/auth-url: https://meet.example.com/api/v1.0/recordings/media-auth/
nginx.ingress.kubernetes.io/auth-response-headers: "Authorization, X-Amz-Date, X-Amz-Content-SHA256"
nginx.ingress.kubernetes.io/upstream-vhost: minio.meet.svc.cluster.local:9000
@@ -140,6 +141,50 @@ serviceMedia:
annotations: {}
## @param ingressMediaFiles.enabled whether to enable the Ingress or not
## @param ingressMediaFiles.className IngressClass to use for the Ingress
## @param ingressMediaFiles.host Host for the Ingress
## @param ingressMediaFiles.path Path to use for the Ingress
ingressMediaFiles:
enabled: false
className: null
host: meet.example.com
path: /media/files/(.*)
## @param ingressMediaFiles.hosts Additional host to configure for the Ingress
hosts: [ ]
# - chart-example.local
## @param ingressMediaFiles.tls.enabled Weather to enable TLS for the Ingress
## @param ingressMediaFiles.tls.secretName Secret name for TLS config
## @skip ingressMediaFiles.tls.additional
## @extra ingressMediaFiles.tls.additional[].secretName Secret name for additional TLS config
## @extra ingressMediaFiles.tls.additional[].hosts[] Hosts for additional TLS config
tls:
secretName: null
enabled: true
additional: []
## @param ingressMediaFiles.annotations.nginx.ingress.kubernetes.io/auth-url
## @param ingressMediaFiles.annotations.nginx.ingress.kubernetes.io/auth-response-headers
## @param ingressMediaFiles.annotations.nginx.ingress.kubernetes.io/upstream-vhost
## @param ingressMediaFiles.annotations.nginx.ingress.kubernetes.io/configuration-snippet
annotations:
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/auth-url: https://meet.example.com/api/v1.0/files/media-auth/
nginx.ingress.kubernetes.io/auth-response-headers: "Authorization, X-Amz-Date, X-Amz-Content-SHA256"
nginx.ingress.kubernetes.io/upstream-vhost: minio.meet.svc.cluster.local:9000
nginx.ingress.kubernetes.io/configuration-snippet: |
add_header Content-Security-Policy "default-src 'none'" always;
add_header Content-Disposition "attachment";
## @param serviceMediaFiles.host
## @param serviceMediaFiles.port
## @param serviceMediaFiles.annotations
serviceMediaFiles:
host: minio.meet.svc.cluster.local
port: 9000
annotations: {}
## @section backend
@@ -302,7 +347,7 @@ frontend:
## @param frontend.replicas Amount of frontend replicas
replicas: 3
## @param frontend.shareProcessNamespace Enable share process namefrontend between containers
## @param frontend.shareProcessNamespace Enable share process namespace between containers
shareProcessNamespace: false
## @param frontend.sidecars Add sidecars containers to frontend deployment
@@ -556,6 +601,108 @@ summary:
pdb:
enabled: true
## @section celeryBackend
celeryBackend:
## @param celeryBackend.dpAnnotations Annotations to add to the celeryBackend Deployment
dpAnnotations: {}
## @param celeryBackend.command Override the celeryBackend container command
command:
- "celery"
- "-A"
- "meet.celery_app"
- "worker"
- "--pool=solo"
- "--loglevel=info"
- "-Q"
- "meet-backend"
## @param celeryBackend.args Override the celeryBackend container args
args: []
## @param celeryBackend.replicas Amount of celeryBackend replicas
replicas: 1
## @param celeryBackend.shareProcessNamespace Enable share process namespace between containers
shareProcessNamespace: false
## @param celeryBackend.sidecars Add sidecars containers to celeryBackend deployment
sidecars: []
## @param celeryBackend.migrateJobAnnotations Annotations for the migrate job
migrateJobAnnotations: {}
## @param celeryBackend.securityContext Configure celeryBackend Pod security context
securityContext: null
## @param celeryBackend.envVars Configure celeryBackend container environment variables
## @extra celeryBackend.envVars.BY_VALUE Example environment variable by setting value directly
## @extra celeryBackend.envVars.FROM_CONFIGMAP.configMapKeyRef.name Name of a ConfigMap when configuring env vars from a ConfigMap
## @extra celeryBackend.envVars.FROM_CONFIGMAP.configMapKeyRef.key Key within a ConfigMap when configuring env vars from a ConfigMap
## @extra celeryBackend.envVars.FROM_SECRET.secretKeyRef.name Name of a Secret when configuring env vars from a Secret
## @extra celeryBackend.envVars.FROM_SECRET.secretKeyRef.key Key within a Secret when configuring env vars from a Secret
## @skip celeryBackend.envVars
envVars:
<<: *commonEnvVars
## @param celeryBackend.podAnnotations Annotations to add to the celeryBackend Pod
podAnnotations: {}
## @param celeryBackend.service.type celeryBackend Service type
## @param celeryBackend.service.port celeryBackend Service listening port
## @param celeryBackend.service.targetPort celeryBackend container listening port
## @param celeryBackend.service.annotations Annotations to add to the celeryBackend Service
service:
type: ClusterIP
port: 80
targetPort: 8000
annotations: {}
## @param celeryBackend.probes Configure celeryBackend probes
## @param celeryBackend.probes.liveness.path [nullable] Configure path for celeryBackend HTTP liveness probe
## @param celeryBackend.probes.liveness.targetPort [nullable] Configure port for celeryBackend HTTP liveness probe
## @param celeryBackend.probes.liveness.initialDelaySeconds [nullable] Configure initial delay for celeryBackend liveness probe
## @param celeryBackend.probes.liveness.initialDelaySeconds [nullable] Configure timeout for celeryBackend liveness probe
## @param celeryBackend.probes.startup.path [nullable] Configure path for celeryBackend HTTP startup probe
## @param celeryBackend.probes.startup.targetPort [nullable] Configure port for celeryBackend HTTP startup probe
## @param celeryBackend.probes.startup.initialDelaySeconds [nullable] Configure initial delay for celeryBackend startup probe
## @param celeryBackend.probes.startup.initialDelaySeconds [nullable] Configure timeout for celeryBackend startup probe
## @param celeryBackend.probes.readiness.path [nullable] Configure path for celeryBackend HTTP readiness probe
## @param celeryBackend.probes.readiness.targetPort [nullable] Configure port for celeryBackend HTTP readiness probe
## @param celeryBackend.probes.readiness.initialDelaySeconds [nullable] Configure initial delay for celeryBackend readiness probe
## @param celeryBackend.probes.readiness.initialDelaySeconds [nullable] Configure timeout for celeryBackend readiness probe
probes: {}
## @param celeryBackend.resources Resource requirements for the celeryBackend container
resources: {}
## @param celeryBackend.nodeSelector Node selector for the celeryBackend Pod
nodeSelector: {}
## @param celeryBackend.tolerations Tolerations for the celeryBackend Pod
tolerations: []
## @param celeryBackend.affinity Affinity for the celeryBackend Pod
affinity: {}
## @param celeryBackend.persistence Additional volumes to create and mount on the celeryBackend. Used for debugging purposes
## @extra celeryBackend.persistence.volume-name.size Size of the additional volume
## @extra celeryBackend.persistence.volume-name.type Type of the additional volume, persistentVolumeClaim or emptyDir
## @extra celeryBackend.persistence.volume-name.mountPath Path where the volume should be mounted to
persistence: {}
## @param celeryBackend.extraVolumeMounts Additional volumes to mount on the celeryBackend.
extraVolumeMounts: []
## @param celeryBackend.extraVolumes Additional volumes to mount on the celeryBackend.
extraVolumes: []
## @param celeryBackend.pdb.enabled Enable pdb on celeryBackend
pdb:
enabled: false
## @section celeryTranscribe
celeryTranscribe: