diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ff3daa7..bafcbd2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ and this project adheres to - ⬆️(agents) upgrade to python 3.14 slim - ⬆️(dependencies) update python dependencies +- 💥(summary) remove v1 related code #1362 +- ✨(meet) use compatible with summary v2 #1362 ### Fixed diff --git a/env.d/development/common.dist b/env.d/development/common.dist index a045e99a..c48d903e 100644 --- a/env.d/development/common.dist +++ b/env.d/development/common.dist @@ -65,8 +65,9 @@ ALLOW_UNREGISTERED_ROOMS=False RECORDING_ENABLE=True RECORDING_STORAGE_EVENT_ENABLE=True RECORDING_STORAGE_EVENT_TOKEN=password -SUMMARY_SERVICE_ENDPOINT=http://app-summary-dev:8000/api/v1/tasks/ +SUMMARY_SERVICE_ENDPOINT=http://app-summary-dev:8000/api/v2/async-jobs/transcribe/ SUMMARY_SERVICE_API_TOKEN=password +SUMMARY_SERVICE_WEBHOOK_API_TOKEN=webhook-password RECORDING_DOWNLOAD_BASE_URL=http://localhost:3000/recording # Recording encoding (LiveKit Egress advanced options). diff --git a/env.d/development/summary.dist b/env.d/development/summary.dist index 5122504a..bfd56404 100644 --- a/env.d/development/summary.dist +++ b/env.d/development/summary.dist @@ -30,3 +30,4 @@ POSTHOG_ENABLED="False" # Transcription TRANSCRIPTION_SATISFACTION_FORM_BASE_URL= +AUTHORIZED_TENANTS='[{"id": "meet","api_key": "password","webhook_url": "https://configure-your-url.com/api/v1.0/recordings/external-process-hook/","webhook_api_key": "webhook-password","allowed_push_to_docs": true}]' diff --git a/src/backend/core/admin.py b/src/backend/core/admin.py index afcea086..0722c181 100644 --- a/src/backend/core/admin.py +++ b/src/backend/core/admin.py @@ -11,7 +11,7 @@ from core.recording.event import notification from . import models from .tasks.file import process_file_deletion -from .utils import generate_download_file_url +from .utils import generate_download_s3_url def hard_delete_file(file): @@ -242,7 +242,7 @@ class FileAdmin(admin.ModelAdmin): """Return a clickable preview URL for the file.""" if not obj.is_ready: return "-" - url = generate_download_file_url(obj, expires_in=60 * 60) + url = generate_download_s3_url(obj.key, expires_in=60 * 60) return format_html( 'Open File', url diff --git a/src/backend/core/api/serializers.py b/src/backend/core/api/serializers.py index 3379a550..4a7a5637 100644 --- a/src/backend/core/api/serializers.py +++ b/src/backend/core/api/serializers.py @@ -563,3 +563,13 @@ class RenameParticipantSerializer(BaseValidationOnlySerializer): """Serializer for renaming a participant in a room.""" name = serializers.CharField(min_length=1, max_length=255, allow_blank=False) + + +class ExternalProcessEventSerializer(BaseValidationOnlySerializer): + """Validate external process event data.""" + + job_id = serializers.CharField(required=True) + # We are not strict on purpose on those fields to avoid + # useless bad requests + type = serializers.CharField(required=False, allow_null=True, allow_blank=True) + status = serializers.CharField(required=False, allow_null=True, allow_blank=True) diff --git a/src/backend/core/api/viewsets.py b/src/backend/core/api/viewsets.py index ee25b82d..a8def5a5 100644 --- a/src/backend/core/api/viewsets.py +++ b/src/backend/core/api/viewsets.py @@ -39,7 +39,10 @@ from core import analytics, enums, models, utils from core.api.filters import ListFileFilter from core.enums import MEDIA_STORAGE_URL_PATTERN from core.recording.enums import FileExtension -from core.recording.event.authentication import StorageEventAuthentication +from core.recording.event.authentication import ( + RecordingProcessWebhookAuthentication, + StorageEventAuthentication, +) from core.recording.event.exceptions import ( InvalidBucketError, InvalidFilepathError, @@ -999,6 +1002,60 @@ class RecordingViewSet( {"message": "Event processed."}, ) + @decorators.action( + detail=False, + methods=["post"], + url_path="external-process-hook", + authentication_classes=[RecordingProcessWebhookAuthentication], + serializer_class=serializers.ExternalProcessEventSerializer, + ) + def on_external_process_event_received(self, request, pk=None): # pylint: disable=unused-argument + """Handle incoming external process events for recordings.""" + logger.debug("Processing external process event %s", request.data) + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + + ok_response = drf_response.Response( + {"message": "Event processed."}, + ) + + validated_data = serializer.validated_data + job_id = validated_data["job_id"] + try: + recording = models.Recording.objects.get(external_process_id=job_id) + except models.Recording.DoesNotExist as e: + logger.warning("No recording found for job_id %s: %s", job_id, e) + return ok_response + + if validated_data.get("type") == "transcript": + if validated_data.get("status") == "success": + logger.info( + "External process transcript success received for recording %s", + job_id, + ) + recording.status = ( + models.RecordingStatusChoices.EXTERNAL_PROCESS_SUCCESSFUL + ) + recording.save() + return ok_response + + if validated_data.get("status") == "failure": + logger.info( + "External process transcript failure received for recording %s", + job_id, + ) + recording.status = models.RecordingStatusChoices.EXTERNAL_PROCESS_FAILED + recording.save() + return ok_response + + logger.info( + "No changes to save for external process id %s and payload %s", + job_id, + validated_data, + ) + + return ok_response + def _auth_get_original_url(self, request): """ Extracts and parses the original URL from the "HTTP_X_ORIGINAL_URL" header. diff --git a/src/backend/core/migrations/0021_recording_external_process_id_alter_recording_status.py b/src/backend/core/migrations/0021_recording_external_process_id_alter_recording_status.py new file mode 100644 index 00000000..62a7fbd0 --- /dev/null +++ b/src/backend/core/migrations/0021_recording_external_process_id_alter_recording_status.py @@ -0,0 +1,23 @@ +# Generated by Django 5.2.14 on 2026-06-22 08:26 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0020_alter_file_upload_state'), + ] + + operations = [ + migrations.AddField( + model_name='recording', + name='external_process_id', + field=models.CharField(blank=True, help_text='ID of the external process associated with the recording.', max_length=255, null=True, unique=True, verbose_name='External Process ID'), + ), + migrations.AlterField( + model_name='recording', + name='status', + field=models.CharField(choices=[('initiated', 'Initiated'), ('active', 'Active'), ('stopped', 'Stopped'), ('saved', 'Saved'), ('aborted', 'Aborted'), ('failed_to_start', 'Failed to Start'), ('failed_to_stop', 'Failed to Stop'), ('notification_succeeded', 'Notification succeeded'), ('external_process_successful', 'External process successful'), ('external_process_failed', 'External process failed')], default='initiated', max_length=50), + ), + ] diff --git a/src/backend/core/models.py b/src/backend/core/models.py index 1a7ec3c9..fbfcf6be 100644 --- a/src/backend/core/models.py +++ b/src/backend/core/models.py @@ -60,6 +60,11 @@ class RecordingStatusChoices(models.TextChoices): FAILED_TO_START = "failed_to_start", _("Failed to Start") FAILED_TO_STOP = "failed_to_stop", _("Failed to Stop") NOTIFICATION_SUCCEEDED = "notification_succeeded", _("Notification succeeded") + EXTERNAL_PROCESS_SUCCESSFUL = ( + "external_process_successful", + _("External process successful"), + ) + EXTERNAL_PROCESS_FAILED = "external_process_failed", _("External process failed") @classmethod def is_final(cls, status): @@ -73,6 +78,8 @@ class RecordingStatusChoices(models.TextChoices): cls.STOPPED, cls.SAVED, cls.ABORTED, + cls.EXTERNAL_PROCESS_SUCCESSFUL, + cls.EXTERNAL_PROCESS_FAILED, cls.FAILED_TO_START, cls.FAILED_TO_STOP, } @@ -598,6 +605,14 @@ class Recording(BaseModel): verbose_name=_("Recording options"), help_text=_("Recording options"), ) + external_process_id = models.CharField( + max_length=255, + null=True, + blank=True, + unique=True, + verbose_name=_("External Process ID"), + help_text=_("ID of the external process associated with the recording."), + ) class Meta: db_table = "meet_recording" @@ -653,6 +668,8 @@ class Recording(BaseModel): return self.status in { RecordingStatusChoices.NOTIFICATION_SUCCEEDED, RecordingStatusChoices.SAVED, + RecordingStatusChoices.EXTERNAL_PROCESS_SUCCESSFUL, + RecordingStatusChoices.EXTERNAL_PROCESS_FAILED, } @property diff --git a/src/backend/core/recording/event/authentication.py b/src/backend/core/recording/event/authentication.py index 28551468..a7f08522 100644 --- a/src/backend/core/recording/event/authentication.py +++ b/src/backend/core/recording/event/authentication.py @@ -14,9 +14,9 @@ logger = logging.getLogger(__name__) class MachineUser: """Represent a non-interactive system user for automated storage operations.""" - def __init__(self) -> None: + def __init__(self, username: str = "storage_event_user") -> None: self.pk = None - self.username = "storage_event_user" + self.username = username self.is_active = True @property @@ -91,3 +91,46 @@ class StorageEventAuthentication(BaseAuthentication): def authenticate_header(self, request): """Return the WWW-Authenticate header value.""" return f"{self.TOKEN_TYPE} realm='Storage event API'" + + +class RecordingProcessWebhookAuthentication(BaseAuthentication): + """ + Custom authentication class for recording process webhook requests. + Validates the API key in the Authorization header. + """ + + AUTH_HEADER = "Authorization" + TOKEN_TYPE = "Bearer" # noqa S105 + + def authenticate(self, request): + """ + Authenticate the request and return a two-tuple of (user, token). + """ + required_token = settings.SUMMARY_SERVICE_WEBHOOK_API_TOKEN + if not required_token: + raise AuthenticationFailed("Webhook authentication is not configured.") + + auth_header: str = request.headers.get("Authorization") or "" + if not auth_header.startswith("Bearer "): + logger.warning( + "Authentication failed: Invalid authorization header format (ip: %s)", + request.META.get("REMOTE_ADDR"), + ) + raise AuthenticationFailed("Invalid authorization header format.") + token = auth_header[7:] # len("Bearer ") == 7 + + if not secrets.compare_digest( + token, + required_token, + ): + logger.warning( + "Authentication failed: Bad Authorization header (ip: %s)", + request.META.get("REMOTE_ADDR"), + ) + raise AuthenticationFailed() + + return MachineUser("external_process_user"), None + + def authenticate_header(self, request): + """Return the WWW-Authenticate header value.""" + return f"{self.TOKEN_TYPE} realm='External process webhook API'" diff --git a/src/backend/core/recording/event/notification.py b/src/backend/core/recording/event/notification.py index 2d389c4c..cdbc253d 100644 --- a/src/backend/core/recording/event/notification.py +++ b/src/backend/core/recording/event/notification.py @@ -4,6 +4,7 @@ import asyncio import logging import smtplib from datetime import datetime, timezone +from zoneinfo import ZoneInfo, ZoneInfoNotFoundError from django.conf import settings from django.core.mail import send_mail @@ -17,6 +18,7 @@ from asgiref.sync import async_to_sync from livekit import api as livekit_api from core import models, utils +from core.utils import generate_download_s3_url logger = logging.getLogger(__name__) @@ -178,8 +180,49 @@ class NotificationService: return _ns_to_utc(file_result.started_at), _ns_to_utc(file_result.ended_at) + @staticmethod + def _generate_title( + *, + locale: str, + room: str, + recording_datetime: datetime | None, + owner_timezone: str | None, + ) -> str: + """Generate title from context or return default.""" + if recording_datetime is None: + with override(locale): + return _("Transcription") + + dt = recording_datetime + if owner_timezone: + try: + dt = recording_datetime.astimezone(ZoneInfo(owner_timezone)) + except (KeyError, ZoneInfoNotFoundError): + pass # Keep the original UTC datetime + + with override(locale): + translated_template = _( + 'Meeting "{room}" on {room_recording_date} at {room_recording_time}' + ) + return translated_template.format( + room=room, + room_recording_date=dt.strftime("%Y-%m-%d"), + room_recording_time=dt.strftime("%H:%M"), + ) + @staticmethod def _notify_summary_service(recording: models.Recording): + if settings.SUMMARY_SERVICE_VERSION == 1: + return NotificationService._notify_summary_service_v1(recording) + if settings.SUMMARY_SERVICE_VERSION == 2: + return NotificationService._notify_summary_service_v2(recording) + + raise NotImplementedError( + f"Unknown summary service version: {settings.SUMMARY_SERVICE_VERSION}" + ) + + @staticmethod + def _notify_summary_service_v1(recording: models.Recording): """Notify summary service about a new recording.""" if ( @@ -253,5 +296,119 @@ class NotificationService: return True + @staticmethod + def _notify_summary_service_v2(recording: models.Recording): + """Notify summary service about a new recording.""" + + if ( + not settings.SUMMARY_SERVICE_ENDPOINT + or not settings.SUMMARY_SERVICE_API_TOKEN + ): + logger.error("Summary service not configured") + return False + + owner_access = ( + models.RecordingAccess.objects.select_related("user") + .filter( + role=models.RoleChoices.OWNER, + recording_id=recording.id, + ) + .first() + ) + metadata_filename: None | str = None + if settings.METADATA_COLLECTOR_ENABLED and recording.options.get( + "collect_metadata", False + ): + output_folder = settings.METADATA_COLLECTOR_OUTPUT_FOLDER + metadata_filename = f"{output_folder}/{recording.id}-metadata.json" + + if not owner_access: + logger.error("No owner found for recording %s", recording.id) + return False + + started_at, ended_at = async_to_sync( + NotificationService._get_recording_timestamps + )(recording.worker_id) + + form_base_url = settings.TRANSCRIPTION_SATISFACTION_FORM_BASE_URL + form_link = ( + f"{form_base_url}?room_id={recording.room.id}" + if (form_base_url and metadata_filename is not None) + else None + ) + metadata_payload = None + if started_at and ended_at and metadata_filename: + metadata_payload = { + "cloud_storage_url": generate_download_s3_url( + metadata_filename, + expires_in=settings.SUMMARY_SERVICE_CLOUD_STORAGE_SIGNED_URL_EXPIRY_SECONDS, + override_domain=False, + ), + "started_at": started_at.isoformat(), + "ended_at": ended_at.isoformat(), + } + + payload = { + "user_sub": owner_access.user.sub, + "user_email": owner_access.user.email, + "cloud_storage_url": generate_download_s3_url( + recording.key, + expires_in=settings.SUMMARY_SERVICE_CLOUD_STORAGE_SIGNED_URL_EXPIRY_SECONDS, + override_domain=False, + ), + "language": recording.options.get( + "language", get_language().split("-")[0].lower() + ), + "context_language": owner_access.user.language, + "push_to_docs_config": { + "user_email": owner_access.user.email, + "title": NotificationService._generate_title( + locale=owner_access.user.language + or recording.options.get("language", get_language()), + room=recording.room.name, + recording_datetime=started_at, + owner_timezone=str(owner_access.user.timezone), + ), + "download_link": f"{get_recording_download_base_url()}/{recording.id}", + "form_link": form_link, + # For now the feature flag logic is handled on summary side + "auto_create_summary": True, + }, + "metadata": metadata_payload, + } + + headers = { + "Content-Type": "application/json", + "Authorization": f"Bearer {settings.SUMMARY_SERVICE_API_TOKEN}", + } + + try: + response = requests.post( + settings.SUMMARY_SERVICE_ENDPOINT, + json=payload, + headers=headers, + timeout=30, + ) + response.raise_for_status() + response_json = response.json() + # We do not require a job_id to avoid a breaking change + job_id = response_json.get("job_id") + if not isinstance(job_id, str): + raise ValueError("job_id is not a string") + + recording.external_process_id = job_id + recording.save() + + except requests.RequestException as exc: + logger.exception( + "Summary service error for recording %s. URL: %s. Exception: %s", + recording.id, + settings.SUMMARY_SERVICE_ENDPOINT, + exc, + ) + return False + + return True + notification_service = NotificationService() diff --git a/src/backend/core/tests/recording/event/test_notification.py b/src/backend/core/tests/recording/event/test_notification.py index 4198f1e4..1d50a4f1 100644 --- a/src/backend/core/tests/recording/event/test_notification.py +++ b/src/backend/core/tests/recording/event/test_notification.py @@ -243,3 +243,168 @@ def test_notify_user_by_email_smtp_exception(mocked_current_site, caplog): assert result is False assert mock_send_mail.call_count == 2 assert "notification could not be sent:" in caplog.text + + +@mock.patch("core.recording.event.notification.requests.post") +@mock.patch("core.recording.event.notification.generate_download_s3_url") +@mock.patch.object( + NotificationService, "_get_recording_timestamps", new_callable=mock.AsyncMock +) +def test_notify_summary_service_post_args_with_metadata( + mock_get_recording_timestamps, + mock_generate_download_s3_url, + mock_post, + settings, +): + """Test summary notification computed request args when metadata is enabled.""" + settings.SUMMARY_SERVICE_VERSION = 2 + settings.SUMMARY_SERVICE_ENDPOINT = "https://summary.test/api/v2/tasks" + settings.SUMMARY_SERVICE_API_TOKEN = "summary-token" + settings.RECORDING_DOWNLOAD_BASE_URL = "https://app.test/recordings" + settings.SCREEN_RECORDING_BASE_URL = None + settings.METADATA_COLLECTOR_ENABLED = True + settings.METADATA_COLLECTOR_OUTPUT_FOLDER = "recordings-metadata" + + recording = factories.RecordingFactory( + room__name="Engineering Sync", + worker_id="egress-1", + options={"collect_metadata": True, "language": "en-us"}, + ) + owner = factories.UserFactory( + email="owner@test.com", + sub="owner-sub", + language="fr-fr", + timezone="Europe/Paris", + ) + factories.UserRecordingAccessFactory( + recording=recording, role=models.RoleChoices.OWNER, user=owner + ) + + started_at = datetime.datetime(2026, 1, 2, 10, 30, tzinfo=datetime.timezone.utc) + ended_at = datetime.datetime(2026, 1, 2, 11, 45, tzinfo=datetime.timezone.utc) + mock_get_recording_timestamps.return_value = (started_at, ended_at) + mock_generate_download_s3_url.side_effect = [ + "https://storage.test/metadata.json", + "https://storage.test/recording.ogg", + ] + + mock_response = mock.Mock() + mock_response.raise_for_status.return_value = None + mock_response.json.return_value = {"job_id": "job-42"} + mock_post.return_value = mock_response + + result = NotificationService._notify_summary_service(recording) + recording.refresh_from_db() + + assert result is True + assert recording.external_process_id == "job-42" + + metadata_filename = ( + f"{settings.METADATA_COLLECTOR_OUTPUT_FOLDER}/{recording.id}-metadata.json" + ) + expected_payload = { + "user_sub": owner.sub, + "user_email": owner.email, + "cloud_storage_url": "https://storage.test/recording.ogg", + "language": "en-us", + "context_language": owner.language, + "push_to_docs_config": { + "user_email": owner.email, + "title": 'Réunion "Engineering Sync" du 2026-01-02 à 11:30', + "download_link": f"{settings.RECORDING_DOWNLOAD_BASE_URL}/{recording.id}", + "auto_create_summary": True, + "form_link": None, + }, + "metadata": { + "cloud_storage_url": "https://storage.test/metadata.json", + "started_at": started_at.isoformat(), + "ended_at": ended_at.isoformat(), + }, + } + expected_headers = { + "Content-Type": "application/json", + "Authorization": "Bearer summary-token", + } + mock_post.assert_called_once_with( + "https://summary.test/api/v2/tasks", + json=expected_payload, + headers=expected_headers, + timeout=30, + ) + assert mock_generate_download_s3_url.call_args_list == [ + mock.call(metadata_filename, expires_in=60 * 60 * 24, override_domain=False), + mock.call(recording.key, expires_in=60 * 60 * 24, override_domain=False), + ] + mock_get_recording_timestamps.assert_awaited_once_with("egress-1") + + +@mock.patch("core.recording.event.notification.requests.post") +@mock.patch("core.recording.event.notification.generate_download_s3_url") +@mock.patch.object( + NotificationService, "_get_recording_timestamps", new_callable=mock.AsyncMock +) +def test_notify_summary_service_post_args_without_metadata( + mock_get_recording_timestamps, + mock_generate_download_s3_url, + mock_post, + settings, +): + """Test summary notification computed request args when metadata is not available.""" + settings.SUMMARY_SERVICE_VERSION = 2 + settings.SUMMARY_SERVICE_ENDPOINT = "https://summary.test/api/v2/tasks" + settings.SUMMARY_SERVICE_API_TOKEN = "summary-token" + settings.RECORDING_DOWNLOAD_BASE_URL = "https://app.test/recordings" + settings.SCREEN_RECORDING_BASE_URL = None + settings.METADATA_COLLECTOR_ENABLED = False + + recording = factories.RecordingFactory(room__name="Daily") + owner = factories.UserFactory( + email="owner@test.com", + sub="owner-sub", + language="en-us", + timezone="UTC", + ) + factories.UserRecordingAccessFactory( + recording=recording, role=models.RoleChoices.OWNER, user=owner + ) + + mock_get_recording_timestamps.return_value = (None, None) + mock_generate_download_s3_url.return_value = "https://storage.test/recording.mp4" + + mock_response = mock.Mock() + mock_response.raise_for_status.return_value = None + mock_response.json.return_value = {"job_id": "job-51"} + mock_post.return_value = mock_response + + result = NotificationService._notify_summary_service(recording) + + assert result is True + expected_payload = { + "user_sub": owner.sub, + "user_email": owner.email, + "cloud_storage_url": "https://storage.test/recording.mp4", + "language": "en", + "context_language": owner.language, + "push_to_docs_config": { + "user_email": owner.email, + "title": "Transcription", + "download_link": f"{settings.RECORDING_DOWNLOAD_BASE_URL}/{recording.id}", + "auto_create_summary": True, + "form_link": None, + }, + "metadata": None, + } + expected_headers = { + "Content-Type": "application/json", + "Authorization": "Bearer summary-token", + } + mock_post.assert_called_once_with( + "https://summary.test/api/v2/tasks", + json=expected_payload, + headers=expected_headers, + timeout=30, + ) + mock_generate_download_s3_url.assert_called_once_with( + recording.key, expires_in=60 * 60 * 24, override_domain=False + ) + mock_get_recording_timestamps.assert_awaited_once_with(recording.worker_id) diff --git a/src/backend/core/tests/recording/test_api_recordings_external_process_hook.py b/src/backend/core/tests/recording/test_api_recordings_external_process_hook.py new file mode 100644 index 00000000..71aae54d --- /dev/null +++ b/src/backend/core/tests/recording/test_api_recordings_external_process_hook.py @@ -0,0 +1,134 @@ +""" +Test recordings API endpoints: external process hook. +""" + +# pylint: disable=redefined-outer-name,unused-argument + +import pytest + +from ...factories import RecordingFactory +from ...models import RecordingStatusChoices + +pytestmark = pytest.mark.django_db + + +@pytest.fixture +def external_process_settings(settings): + """Configure authentication token for the external process webhook.""" + settings.SUMMARY_SERVICE_WEBHOOK_API_TOKEN = "testWebhookToken" + return settings + + +def test_external_process_event_missing_authorization_header( + external_process_settings, client +): + """Requests without authorization must be rejected.""" + response = client.post( + "/api/v1.0/recordings/external-process-hook/", + {"job_id": "job-1", "type": "transcript", "status": "success"}, + ) + + assert response.status_code == 401 + + +def test_external_process_event_wrong_bearer_token(external_process_settings, client): + """Requests with invalid bearer token must be rejected.""" + response = client.post( + "/api/v1.0/recordings/external-process-hook/", + {"job_id": "job-1", "type": "transcript", "status": "success"}, + HTTP_AUTHORIZATION="Bearer wrongToken", + ) + + assert response.status_code == 401 + + +def test_external_process_event_missing_job_id(external_process_settings, client): + """Payload without job_id must fail validation.""" + response = client.post( + "/api/v1.0/recordings/external-process-hook/", + {"type": "transcript", "status": "success"}, + HTTP_AUTHORIZATION="Bearer testWebhookToken", + ) + + assert response.status_code == 400 + assert response.json() == {"job_id": ["This field is required."]} + + +def test_external_process_event_success_updates_recording_status( + external_process_settings, client +): + """A successful transcript process should update recording status.""" + recording = RecordingFactory( + status=RecordingStatusChoices.SAVED, + external_process_id="job-123", + ) + + response = client.post( + "/api/v1.0/recordings/external-process-hook/", + {"job_id": "job-123", "type": "transcript", "status": "success"}, + HTTP_AUTHORIZATION="Bearer testWebhookToken", + ) + + assert response.status_code == 200 + assert response.json() == {"message": "Event processed."} + + recording.refresh_from_db() + assert recording.status == RecordingStatusChoices.EXTERNAL_PROCESS_SUCCESSFUL + + +def test_external_process_event_failure_updates_recording_status( + external_process_settings, client +): + """A failing transcript process should update recording status.""" + recording = RecordingFactory( + status=RecordingStatusChoices.SAVED, + external_process_id="job-456", + ) + + response = client.post( + "/api/v1.0/recordings/external-process-hook/", + {"job_id": "job-456", "type": "transcript", "status": "failure"}, + HTTP_AUTHORIZATION="Bearer testWebhookToken", + ) + + assert response.status_code == 200 + assert response.json() == {"message": "Event processed."} + + recording.refresh_from_db() + assert recording.status == RecordingStatusChoices.EXTERNAL_PROCESS_FAILED + + +def test_external_process_event_unknown_recording_is_ignored( + external_process_settings, client +): + """Unknown job_id should not fail the webhook processing.""" + response = client.post( + "/api/v1.0/recordings/external-process-hook/", + {"job_id": "missing-job", "type": "transcript", "status": "success"}, + HTTP_AUTHORIZATION="Bearer testWebhookToken", + ) + + assert response.status_code == 200 + assert response.json() == {"message": "Event processed."} + + +def test_external_process_event_non_transcript_event_does_not_change_status( + external_process_settings, client +): + """Only transcript events should update recording status.""" + recording = RecordingFactory( + status=RecordingStatusChoices.SAVED, + external_process_id="job-789", + ) + + response = client.post( + "/api/v1.0/recordings/external-process-hook/", + {"job_id": "job-789", "type": "thumbnail", "status": "success"}, + HTTP_AUTHORIZATION="Bearer testWebhookToken", + ) + + assert response.status_code == 200 + assert response.json() == {"message": "Event processed."} + + recording.refresh_from_db() + assert recording.status == RecordingStatusChoices.SAVED diff --git a/src/backend/core/utils.py b/src/backend/core/utils.py index 6951a99d..8a8e09fa 100644 --- a/src/backend/core/utils.py +++ b/src/backend/core/utils.py @@ -459,12 +459,14 @@ def generate_upload_policy(file): return policy -def generate_download_file_url(file, *, expires_in: int, override_domain: bool = True): +def generate_download_s3_url( + key: str, *, expires_in: int, override_domain: bool = True +): """ - Generate a S3 signed download url for a given file. + Generate a S3 signed download url for a given key. """ - - key = file.file_key + if not key: + raise ValueError("key cannot be empty") # This setting should be used if the backend application and the frontend application # can't connect to the object storage with the same domain. This is the case in the diff --git a/src/backend/locale/de_DE/LC_MESSAGES/django.po b/src/backend/locale/de_DE/LC_MESSAGES/django.po index 5ca75a87..ec7e4c55 100644 --- a/src/backend/locale/de_DE/LC_MESSAGES/django.po +++ b/src/backend/locale/de_DE/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-04-21 14:09+0000\n" +"POT-Creation-Date: 2026-07-02 10:47+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,70 +17,92 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: core/admin.py:29 +#: core/admin.py:67 msgid "Personal info" msgstr "Persönliche Informationen" -#: core/admin.py:42 +#: core/admin.py:80 msgid "Permissions" msgstr "Berechtigungen" -#: core/admin.py:54 +#: core/admin.py:92 msgid "Important dates" msgstr "Wichtige Daten" -#: core/admin.py:132 core/admin.py:275 +#: core/admin.py:206 +msgid "Content" +msgstr "" + +#: core/admin.py:217 +#, fuzzy +#| msgid "Delete rooms" +msgid "Deletion" +msgstr "Räume löschen" + +#: core/admin.py:226 +msgid "Derived info" +msgstr "" + +#: core/admin.py:237 +msgid "Timestamps" +msgstr "" + +#: core/admin.py:240 +msgid "File preview" +msgstr "" + +#: core/admin.py:300 core/admin.py:443 msgid "No owner" msgstr "Kein Eigentümer" -#: core/admin.py:135 core/admin.py:278 +#: core/admin.py:303 core/admin.py:446 msgid "Multiple owners" msgstr "Mehrere Eigentümer" -#: core/admin.py:148 +#: core/admin.py:316 msgid "Resend notification to external service" msgstr "Benachrichtigung erneut an externen Dienst senden" -#: core/admin.py:171 +#: core/admin.py:339 #, python-format msgid "Failed to notify for recording %(id)s" msgstr "Benachrichtigung für Aufnahme %(id)s fehlgeschlagen" -#: core/admin.py:179 +#: core/admin.py:347 #, python-format msgid "Failed to notify for recording %(id)s: %(error)s" msgstr "Benachrichtigung für Aufnahme %(id)s fehlgeschlagen: %(error)s" -#: core/admin.py:187 +#: core/admin.py:355 #, python-format msgid "Successfully sent notifications for %(count)s recording(s)." msgstr "Benachrichtigungen für %(count)s Aufnahme(n) erfolgreich gesendet." -#: core/admin.py:195 +#: core/admin.py:363 #, python-format msgid "Skipped %(count)s expired recording(s)." msgstr "%(count)s abgelaufene Aufnahme(n) übersprungen." -#: core/admin.py:200 +#: core/admin.py:368 msgid "Mark selected recordings as 'Failed to Stop'" msgstr "Ausgewählte Aufnahmen als ‚Fehler beim Stoppen‘ markieren" -#: core/admin.py:218 +#: core/admin.py:386 #, python-format msgid "%(count)s recording(s) successfully marked as 'Failed to Stop'." msgstr "%(count)s Aufnahme(n) erfolgreich als ‚Fehler beim Stoppen‘ markiert." -#: core/admin.py:226 +#: core/admin.py:394 #, fuzzy, python-format #| msgid "Skipped %(count)s expired recording(s)." msgid "Skipped %(count)s recording(s) with an ineligible status." msgstr "%(count)s abgelaufene Aufnahme(n) übersprungen." -#: core/admin.py:342 +#: core/admin.py:510 msgid "No scopes" msgstr "Keine Scopes" -#: core/admin.py:344 +#: core/admin.py:512 msgid "Scopes" msgstr "Scopes" @@ -88,17 +110,17 @@ msgstr "Scopes" msgid "Creator is me" msgstr "Ersteller bin ich" -#: core/api/serializers.py:88 +#: core/api/serializers.py:89 msgid "You must be administrator or owner of a room to add accesses to it." msgstr "" "Sie müssen Administrator oder Eigentümer eines Raums sein, um Zugriffe " "hinzuzufügen." -#: core/api/serializers.py:509 +#: core/api/serializers.py:534 msgid "This file extension is not allowed." msgstr "Diese Dateiendung ist nicht erlaubt." -#: core/api/viewsets.py:1090 +#: core/api/viewsets.py:1222 msgid "You have reached the maximum number of files for this type." msgstr "Sie haben die maximale Anzahl an Dateien dieses Typs erreicht." @@ -146,51 +168,59 @@ msgstr "Stopp fehlgeschlagen" msgid "Notification succeeded" msgstr "Benachrichtigung erfolgreich" -#: core/models.py:89 +#: core/models.py:65 +msgid "External process successful" +msgstr "Externer Prozess erfolgreich" + +#: core/models.py:67 +msgid "External process failed" +msgstr "Externer Prozess fehlgeschlagen" + +#: core/models.py:96 msgid "SCREEN_RECORDING" msgstr "BILDSCHIRMAUFZEICHNUNG" -#: core/models.py:90 +#: core/models.py:97 msgid "TRANSCRIPT" msgstr "TRANSKRIPT" -#: core/models.py:96 +#: core/models.py:103 msgid "Public Access" msgstr "Öffentlicher Zugriff" -#: core/models.py:97 +#: core/models.py:104 msgid "Trusted Access" msgstr "Vertrauenswürdiger Zugriff" -#: core/models.py:98 +#: core/models.py:105 msgid "Restricted Access" msgstr "Eingeschränkter Zugriff" -#: core/models.py:110 +#: core/models.py:117 msgid "id" msgstr "ID" -#: core/models.py:111 +#: core/models.py:118 msgid "primary key for the record as UUID" msgstr "Primärschlüssel des Eintrags als UUID" -#: core/models.py:117 +#: core/models.py:124 msgid "created on" msgstr "erstellt am" -#: core/models.py:118 +#: core/models.py:125 msgid "date and time at which a record was created" msgstr "Datum und Uhrzeit der Erstellung eines Eintrags" -#: core/models.py:123 +#: core/models.py:130 msgid "updated on" msgstr "aktualisiert am" -#: core/models.py:124 +#: core/models.py:131 msgid "date and time at which a record was last updated" msgstr "Datum und Uhrzeit der letzten Aktualisierung eines Eintrags" -#: core/models.py:144 +#: core/models.py:151 msgid "" "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/" "_ characters." @@ -198,11 +228,11 @@ msgstr "" "Geben Sie einen gültigen Sub ein. Dieser Wert darf nur Buchstaben, Zahlen " "und die Zeichen @/./+/-/_ enthalten." -#: core/models.py:150 +#: core/models.py:157 msgid "sub" msgstr "Sub" -#: core/models.py:152 +#: core/models.py:159 msgid "" "Optional for pending users; required upon account activation. 255 characters " "or fewer. Letters, numbers, and @/./+/-/_ characters only." @@ -210,55 +240,55 @@ msgstr "" "Optional für ausstehende Benutzer; erforderlich nach Kontoaktivierung. " "Maximal 255 Zeichen. Nur Buchstaben, Zahlen und @/./+/-/_ Zeichen erlaubt." -#: core/models.py:161 +#: core/models.py:168 msgid "identity email address" msgstr "Identitäts-E-Mail-Adresse" -#: core/models.py:166 +#: core/models.py:173 msgid "admin email address" msgstr "Administrator-E-Mail-Adresse" -#: core/models.py:168 +#: core/models.py:175 msgid "full name" msgstr "Vollständiger Name" -#: core/models.py:170 +#: core/models.py:177 msgid "short name" msgstr "Kurzname" -#: core/models.py:176 +#: core/models.py:183 msgid "language" msgstr "Sprache" -#: core/models.py:177 +#: core/models.py:184 msgid "The language in which the user wants to see the interface." msgstr "Die Sprache, in der der Benutzer die Oberfläche sehen möchte." -#: core/models.py:183 +#: core/models.py:190 msgid "The timezone in which the user wants to see times." msgstr "Die Zeitzone, in der der Benutzer die Zeiten sehen möchte." -#: core/models.py:186 +#: core/models.py:193 msgid "device" msgstr "Gerät" -#: core/models.py:188 +#: core/models.py:195 msgid "Whether the user is a device or a real user." msgstr "Ob es sich um ein Gerät oder einen echten Benutzer handelt." -#: core/models.py:191 +#: core/models.py:198 msgid "staff status" msgstr "Mitarbeiterstatus" -#: core/models.py:193 +#: core/models.py:200 msgid "Whether the user can log into this admin site." msgstr "Ob der Benutzer sich bei dieser Admin-Seite anmelden kann." -#: core/models.py:196 +#: core/models.py:203 msgid "active" msgstr "aktiv" -#: core/models.py:199 +#: core/models.py:206 msgid "" "Whether this user should be treated as active. Unselect this instead of " "deleting accounts." @@ -266,66 +296,66 @@ msgstr "" "Ob dieser Benutzer als aktiv behandelt werden soll. Deaktivieren Sie dies " "anstelle des Löschens des Kontos." -#: core/models.py:212 +#: core/models.py:219 msgid "user" msgstr "Benutzer" -#: core/models.py:213 +#: core/models.py:220 msgid "users" msgstr "Benutzer" -#: core/models.py:272 +#: core/models.py:286 msgid "Resource" msgstr "Ressource" -#: core/models.py:273 +#: core/models.py:287 msgid "Resources" msgstr "Ressourcen" -#: core/models.py:331 +#: core/models.py:345 msgid "Resource access" msgstr "Ressourcenzugriff" -#: core/models.py:332 +#: core/models.py:346 msgid "Resource accesses" msgstr "Ressourcenzugriffe" -#: core/models.py:338 +#: core/models.py:352 msgid "Resource access with this User and Resource already exists." msgstr "" "Ein Ressourcenzugriff mit diesem Benutzer und dieser Ressource existiert " "bereits." -#: core/models.py:394 +#: core/models.py:409 msgid "Visio room configuration" msgstr "Visio-Raumkonfiguration" -#: core/models.py:395 +#: core/models.py:410 msgid "Values for Visio parameters to configure the room." msgstr "Werte für Visio-Parameter zur Konfiguration des Raums." -#: core/models.py:402 +#: core/models.py:417 msgid "Room PIN code" msgstr "PIN-Code für den Raum" -#: core/models.py:403 +#: core/models.py:418 msgid "Unique n-digit code that identifies this room in telephony mode." msgstr "" "Eindeutiger n-stelliger Code, der diesen Raum im Telephonmodus identifiziert." -#: core/models.py:409 core/models.py:563 +#: core/models.py:424 core/models.py:578 msgid "Room" msgstr "Raum" -#: core/models.py:410 +#: core/models.py:425 msgid "Rooms" msgstr "Räume" -#: core/models.py:574 +#: core/models.py:589 msgid "Worker ID" msgstr "Worker-ID" -#: core/models.py:576 +#: core/models.py:591 msgid "" "Enter an identifier for the worker recording.This ID is retained even when " "the worker stops, allowing for easy tracking." @@ -334,141 +364,153 @@ msgstr "" "erhalten, auch wenn der Worker stoppt, was ein einfaches Nachverfolgen " "ermöglicht." -#: core/models.py:584 +#: core/models.py:599 msgid "Recording mode" msgstr "Aufzeichnungsmodus" -#: core/models.py:585 +#: core/models.py:600 msgid "Defines the mode of recording being called." msgstr "Definiert den aufgerufenen Aufzeichnungsmodus." -#: core/models.py:590 core/models.py:591 +#: core/models.py:605 core/models.py:606 msgid "Recording options" msgstr "Aufnahmeoptionen" -#: core/models.py:597 +#: core/models.py:613 +msgid "External Process ID" +msgstr "External Process ID" + +#: core/models.py:614 +msgid "ID of the external process associated with the recording." +msgstr "ID des externen Prozesses, der mit der Aufzeichnung verknüpft ist" + +#: core/models.py:620 msgid "Recording" msgstr "Aufzeichnung" -#: core/models.py:598 +#: core/models.py:621 msgid "Recordings" msgstr "Aufzeichnungen" -#: core/models.py:706 +#: core/models.py:731 msgid "Recording/user relation" msgstr "Beziehung Aufzeichnung/Benutzer" -#: core/models.py:707 +#: core/models.py:732 msgid "Recording/user relations" msgstr "Beziehungen Aufzeichnung/Benutzer" -#: core/models.py:713 +#: core/models.py:738 msgid "This user is already in this recording." msgstr "Dieser Benutzer ist bereits Teil dieser Aufzeichnung." -#: core/models.py:719 +#: core/models.py:744 msgid "This team is already in this recording." msgstr "Dieses Team ist bereits Teil dieser Aufzeichnung." -#: core/models.py:725 +#: core/models.py:750 msgid "Either user or team must be set, not both." msgstr "Entweder Benutzer oder Team muss festgelegt werden, nicht beides." -#: core/models.py:742 +#: core/models.py:767 msgid "Create rooms" msgstr "Räume erstellen" -#: core/models.py:743 +#: core/models.py:768 msgid "List rooms" msgstr "Räume auflisten" -#: core/models.py:744 +#: core/models.py:769 msgid "Retrieve room details" msgstr "Raumdetails abrufen" -#: core/models.py:745 +#: core/models.py:770 msgid "Update rooms" msgstr "Räume aktualisieren" -#: core/models.py:746 +#: core/models.py:771 msgid "Delete rooms" msgstr "Räume löschen" -#: core/models.py:759 +#: core/models.py:784 msgid "Application name" msgstr "Anwendungsname" -#: core/models.py:760 +#: core/models.py:785 msgid "Descriptive name for this application." msgstr "Beschreibender Name für diese Anwendung." -#: core/models.py:770 +#: core/models.py:795 msgid "Hashed on Save. Copy it now if this is a new secret." msgstr "" "Beim Speichern gehasht. Jetzt kopieren, wenn dies ein neues Geheimnis ist." -#: core/models.py:781 +#: core/models.py:806 msgid "Application" msgstr "Anwendung" -#: core/models.py:782 +#: core/models.py:807 msgid "Applications" msgstr "Anwendungen" -#: core/models.py:805 +#: core/models.py:830 msgid "Enter a valid domain" msgstr "Geben Sie eine gültige Domain ein" -#: core/models.py:808 +#: core/models.py:833 msgid "Domain" msgstr "Domain" -#: core/models.py:809 +#: core/models.py:834 msgid "Email domain this application can act on behalf of." msgstr "E-Mail-Domain, im Namen der diese Anwendung handeln kann." -#: core/models.py:821 +#: core/models.py:846 msgid "Application domain" msgstr "Anwendungsdomain" -#: core/models.py:822 +#: core/models.py:847 msgid "Application domains" msgstr "Anwendungsdomains" -#: core/models.py:840 +#: core/models.py:865 msgid "Pending" msgstr "Ausstehend" -#: core/models.py:848 +#: core/models.py:866 +msgid "Analyzing" +msgstr "" + +#: core/models.py:873 msgid "Ready" msgstr "Bereit" -#: core/models.py:854 +#: core/models.py:879 msgid "Background image" msgstr "Hintergrundbild" -#: core/models.py:866 +#: core/models.py:891 msgid "title" msgstr "Titel" -#: core/models.py:890 +#: core/models.py:915 msgid "Malware detection info when the analysis status is unsafe." msgstr "" "Informationen zur Malware-Erkennung, wenn der Analyse-Status unsicher ist." -#: core/models.py:895 +#: core/models.py:920 msgid "File" msgstr "Datei" -#: core/models.py:896 +#: core/models.py:921 msgid "Files" msgstr "Dateien" -#: core/models.py:1000 +#: core/models.py:1041 msgid "This file is already hard deleted." msgstr "Diese Datei wurde bereits endgültig gelöscht." -#: core/models.py:1010 +#: core/models.py:1051 #, fuzzy #| msgid "To hard delete a file, it must first be soft deleted." msgid "To hard delete a file, it must first be soft deleted." @@ -476,10 +518,20 @@ msgstr "" "Um eine Datei endgültig zu löschen, muss sie zuvor weich gelöscht worden " "sein." -#: core/recording/event/notification.py:116 +#: core/recording/event/notification.py:123 msgid "Your recording is ready" msgstr "Ihre Aufzeichnung ist bereit" +#: core/recording/event/notification.py:194 +msgid "Transcription" +msgstr "Transkription" + +#: core/recording/event/notification.py:204 +#, python-brace-format +msgid "Meeting \"{room}\" on {room_recording_date} at {room_recording_time}" +msgstr "" +"Besprechung \"{room}\" am {room_recording_date} um {room_recording_time}" + #: core/services/invitation.py:44 #, python-brace-format msgid "Video call in progress: {sender.email} is waiting for you to connect" @@ -575,7 +627,7 @@ msgid "To keep this recording permanently:" msgstr "So speichern Sie diese Aufzeichnung dauerhaft:" #: core/templates/mail/html/screen_recording.html:208 -#: core/templates/mail/text/screen_recording.txt:13 +#, python-format msgid "Click the \"Open\" link below " msgstr "Klicken Sie auf den Link „Öffnen\" unten " @@ -604,18 +656,24 @@ msgstr "" " Wenn Sie Fragen haben oder Unterstützung benötigen, wenden Sie sich bitte " "an unser Support-Team unter %(support_email)s. " -#: meet/settings.py:223 +#: core/templates/mail/text/screen_recording.txt:13 +#, fuzzy, python-format +#| msgid "Click the \"Open\" link below " +msgid "Click the \"Open [%(link)s]\" link below " +msgstr "Klicken Sie auf den Link „Öffnen\" unten " + +#: meet/settings.py:228 msgid "English" msgstr "Englisch" -#: meet/settings.py:224 +#: meet/settings.py:229 msgid "French" msgstr "Französisch" -#: meet/settings.py:225 +#: meet/settings.py:230 msgid "Dutch" msgstr "Niederländisch" -#: meet/settings.py:226 +#: meet/settings.py:231 msgid "German" msgstr "Deutsch" diff --git a/src/backend/locale/en_US/LC_MESSAGES/django.po b/src/backend/locale/en_US/LC_MESSAGES/django.po index 70c973b9..2f58f6d4 100644 --- a/src/backend/locale/en_US/LC_MESSAGES/django.po +++ b/src/backend/locale/en_US/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-04-21 14:09+0000\n" +"POT-Creation-Date: 2026-07-02 10:47+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,70 +17,92 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: core/admin.py:29 +#: core/admin.py:67 msgid "Personal info" msgstr "Personal info" -#: core/admin.py:42 +#: core/admin.py:80 msgid "Permissions" msgstr "Permissions" -#: core/admin.py:54 +#: core/admin.py:92 msgid "Important dates" msgstr "Important dates" -#: core/admin.py:132 core/admin.py:275 +#: core/admin.py:206 +msgid "Content" +msgstr "" + +#: core/admin.py:217 +#, fuzzy +#| msgid "Delete rooms" +msgid "Deletion" +msgstr "Delete rooms" + +#: core/admin.py:226 +msgid "Derived info" +msgstr "" + +#: core/admin.py:237 +msgid "Timestamps" +msgstr "" + +#: core/admin.py:240 +msgid "File preview" +msgstr "" + +#: core/admin.py:300 core/admin.py:443 msgid "No owner" msgstr "No owner" -#: core/admin.py:135 core/admin.py:278 +#: core/admin.py:303 core/admin.py:446 msgid "Multiple owners" msgstr "Multiple owners" -#: core/admin.py:148 +#: core/admin.py:316 msgid "Resend notification to external service" msgstr "Resend notification to external service" -#: core/admin.py:171 +#: core/admin.py:339 #, python-format msgid "Failed to notify for recording %(id)s" msgstr "Failed to notify for recording %(id)s" -#: core/admin.py:179 +#: core/admin.py:347 #, python-format msgid "Failed to notify for recording %(id)s: %(error)s" msgstr "Failed to notify for recording %(id)s: %(error)s" -#: core/admin.py:187 +#: core/admin.py:355 #, python-format msgid "Successfully sent notifications for %(count)s recording(s)." msgstr "Successfully sent notifications for %(count)s recording(s)." -#: core/admin.py:195 +#: core/admin.py:363 #, python-format msgid "Skipped %(count)s expired recording(s)." msgstr "Skipped %(count)s expired recording(s)." -#: core/admin.py:200 +#: core/admin.py:368 msgid "Mark selected recordings as 'Failed to Stop'" msgstr "Mark selected recordings as 'Failed to Stop'" -#: core/admin.py:218 +#: core/admin.py:386 #, python-format msgid "%(count)s recording(s) successfully marked as 'Failed to Stop'." msgstr "%(count)s recording(s) successfully marked as 'Failed to Stop'." -#: core/admin.py:226 +#: core/admin.py:394 #, fuzzy, python-format #| msgid "Skipped %(count)s expired recording(s)." msgid "Skipped %(count)s recording(s) with an ineligible status." msgstr "Skipped %(count)s expired recording(s)." -#: core/admin.py:342 +#: core/admin.py:510 msgid "No scopes" msgstr "No scopes" -#: core/admin.py:344 +#: core/admin.py:512 msgid "Scopes" msgstr "Scopes" @@ -88,15 +110,15 @@ msgstr "Scopes" msgid "Creator is me" msgstr "Creator is me" -#: core/api/serializers.py:88 +#: core/api/serializers.py:89 msgid "You must be administrator or owner of a room to add accesses to it." msgstr "You must be administrator or owner of a room to add accesses to it." -#: core/api/serializers.py:509 +#: core/api/serializers.py:534 msgid "This file extension is not allowed." msgstr "This file extension is not allowed." -#: core/api/viewsets.py:1090 +#: core/api/viewsets.py:1222 msgid "You have reached the maximum number of files for this type." msgstr "You have reached the maximum number of files for this type." @@ -144,51 +166,59 @@ msgstr "Failed to Stop" msgid "Notification succeeded" msgstr "Notification succeeded" -#: core/models.py:89 +#: core/models.py:65 +msgid "External process successful" +msgstr "External process successful" + +#: core/models.py:67 +msgid "External process failed" +msgstr "External process failed" + +#: core/models.py:96 msgid "SCREEN_RECORDING" msgstr "SCREEN_RECORDING" -#: core/models.py:90 +#: core/models.py:97 msgid "TRANSCRIPT" msgstr "TRANSCRIPT" -#: core/models.py:96 +#: core/models.py:103 msgid "Public Access" msgstr "Public Access" -#: core/models.py:97 +#: core/models.py:104 msgid "Trusted Access" msgstr "Trusted Access" -#: core/models.py:98 +#: core/models.py:105 msgid "Restricted Access" msgstr "Restricted Access" -#: core/models.py:110 +#: core/models.py:117 msgid "id" msgstr "id" -#: core/models.py:111 +#: core/models.py:118 msgid "primary key for the record as UUID" msgstr "primary key for the record as UUID" -#: core/models.py:117 +#: core/models.py:124 msgid "created on" msgstr "created on" -#: core/models.py:118 +#: core/models.py:125 msgid "date and time at which a record was created" msgstr "date and time at which a record was created" -#: core/models.py:123 +#: core/models.py:130 msgid "updated on" msgstr "updated on" -#: core/models.py:124 +#: core/models.py:131 msgid "date and time at which a record was last updated" msgstr "date and time at which a record was last updated" -#: core/models.py:144 +#: core/models.py:151 msgid "" "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/" "_ characters." @@ -196,11 +226,11 @@ msgstr "" "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/" "_ characters." -#: core/models.py:150 +#: core/models.py:157 msgid "sub" msgstr "sub" -#: core/models.py:152 +#: core/models.py:159 msgid "" "Optional for pending users; required upon account activation. 255 characters " "or fewer. Letters, numbers, and @/./+/-/_ characters only." @@ -208,55 +238,55 @@ msgstr "" "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_ " "characters only." -#: core/models.py:161 +#: core/models.py:168 msgid "identity email address" msgstr "identity email address" -#: core/models.py:166 +#: core/models.py:173 msgid "admin email address" msgstr "admin email address" -#: core/models.py:168 +#: core/models.py:175 msgid "full name" msgstr "full name" -#: core/models.py:170 +#: core/models.py:177 msgid "short name" msgstr "short name" -#: core/models.py:176 +#: core/models.py:183 msgid "language" msgstr "language" -#: core/models.py:177 +#: core/models.py:184 msgid "The language in which the user wants to see the interface." msgstr "The language in which the user wants to see the interface." -#: core/models.py:183 +#: core/models.py:190 msgid "The timezone in which the user wants to see times." msgstr "The timezone in which the user wants to see times." -#: core/models.py:186 +#: core/models.py:193 msgid "device" msgstr "device" -#: core/models.py:188 +#: core/models.py:195 msgid "Whether the user is a device or a real user." msgstr "Whether the user is a device or a real user." -#: core/models.py:191 +#: core/models.py:198 msgid "staff status" msgstr "staff status" -#: core/models.py:193 +#: core/models.py:200 msgid "Whether the user can log into this admin site." msgstr "Whether the user can log into this admin site." -#: core/models.py:196 +#: core/models.py:203 msgid "active" msgstr "active" -#: core/models.py:199 +#: core/models.py:206 msgid "" "Whether this user should be treated as active. Unselect this instead of " "deleting accounts." @@ -264,63 +294,63 @@ msgstr "" "Whether this user should be treated as active. Unselect this instead of " "deleting accounts." -#: core/models.py:212 +#: core/models.py:219 msgid "user" msgstr "user" -#: core/models.py:213 +#: core/models.py:220 msgid "users" msgstr "users" -#: core/models.py:272 +#: core/models.py:286 msgid "Resource" msgstr "Resource" -#: core/models.py:273 +#: core/models.py:287 msgid "Resources" msgstr "Resources" -#: core/models.py:331 +#: core/models.py:345 msgid "Resource access" msgstr "Resource access" -#: core/models.py:332 +#: core/models.py:346 msgid "Resource accesses" msgstr "Resource accesses" -#: core/models.py:338 +#: core/models.py:352 msgid "Resource access with this User and Resource already exists." msgstr "Resource access with this User and Resource already exists." -#: core/models.py:394 +#: core/models.py:409 msgid "Visio room configuration" msgstr "Visio room configuration" -#: core/models.py:395 +#: core/models.py:410 msgid "Values for Visio parameters to configure the room." msgstr "Values for Visio parameters to configure the room." -#: core/models.py:402 +#: core/models.py:417 msgid "Room PIN code" msgstr "Room PIN code" -#: core/models.py:403 +#: core/models.py:418 msgid "Unique n-digit code that identifies this room in telephony mode." msgstr "Unique n-digit code that identifies this room in telephony mode." -#: core/models.py:409 core/models.py:563 +#: core/models.py:424 core/models.py:578 msgid "Room" msgstr "Room" -#: core/models.py:410 +#: core/models.py:425 msgid "Rooms" msgstr "Rooms" -#: core/models.py:574 +#: core/models.py:589 msgid "Worker ID" msgstr "Worker ID" -#: core/models.py:576 +#: core/models.py:591 msgid "" "Enter an identifier for the worker recording.This ID is retained even when " "the worker stops, allowing for easy tracking." @@ -328,154 +358,175 @@ msgstr "" "Enter an identifier for the worker recording.This ID is retained even when " "the worker stops, allowing for easy tracking." -#: core/models.py:584 +#: core/models.py:599 msgid "Recording mode" msgstr "Recording mode" -#: core/models.py:585 +#: core/models.py:600 msgid "Defines the mode of recording being called." msgstr "Defines the mode of recording being called." -#: core/models.py:590 core/models.py:591 +#: core/models.py:605 core/models.py:606 msgid "Recording options" msgstr "Recording options" -#: core/models.py:597 +#: core/models.py:613 +msgid "External Process ID" +msgstr "External Process ID" + +#: core/models.py:614 +msgid "ID of the external process associated with the recording." +msgstr "ID of the external process associated with the recording." + +#: core/models.py:620 msgid "Recording" msgstr "Recording" -#: core/models.py:598 +#: core/models.py:621 msgid "Recordings" msgstr "Recordings" -#: core/models.py:706 +#: core/models.py:731 msgid "Recording/user relation" msgstr "Recording/user relation" -#: core/models.py:707 +#: core/models.py:732 msgid "Recording/user relations" msgstr "Recording/user relations" -#: core/models.py:713 +#: core/models.py:738 msgid "This user is already in this recording." msgstr "This user is already in this recording." -#: core/models.py:719 +#: core/models.py:744 msgid "This team is already in this recording." msgstr "This team is already in this recording." -#: core/models.py:725 +#: core/models.py:750 msgid "Either user or team must be set, not both." msgstr "Either user or team must be set, not both." -#: core/models.py:742 +#: core/models.py:767 #, fuzzy #| msgid "created on" msgid "Create rooms" msgstr "Create rooms" -#: core/models.py:743 +#: core/models.py:768 msgid "List rooms" msgstr "List rooms" -#: core/models.py:744 +#: core/models.py:769 msgid "Retrieve room details" msgstr "Retrieve room details" -#: core/models.py:745 +#: core/models.py:770 #, fuzzy #| msgid "updated on" msgid "Update rooms" msgstr "Update rooms" -#: core/models.py:746 +#: core/models.py:771 msgid "Delete rooms" msgstr "Delete rooms" -#: core/models.py:759 +#: core/models.py:784 msgid "Application name" msgstr "Application name" -#: core/models.py:760 +#: core/models.py:785 msgid "Descriptive name for this application." msgstr "Descriptive name for this application." -#: core/models.py:770 +#: core/models.py:795 msgid "Hashed on Save. Copy it now if this is a new secret." msgstr "Hashed on Save. Copy it now if this is a new secret." -#: core/models.py:781 +#: core/models.py:806 msgid "Application" msgstr "Application" -#: core/models.py:782 +#: core/models.py:807 msgid "Applications" msgstr "Applications" -#: core/models.py:805 +#: core/models.py:830 msgid "Enter a valid domain" msgstr "Enter a valid domain" -#: core/models.py:808 +#: core/models.py:833 msgid "Domain" msgstr "Domain" -#: core/models.py:809 +#: core/models.py:834 msgid "Email domain this application can act on behalf of." msgstr "Email domain this application can act on behalf of." -#: core/models.py:821 +#: core/models.py:846 msgid "Application domain" msgstr "Application domain" -#: core/models.py:822 +#: core/models.py:847 msgid "Application domains" msgstr "Application domains" -#: core/models.py:840 +#: core/models.py:865 #, fuzzy #| msgid "Recording" msgid "Pending" msgstr "Pending" -#: core/models.py:848 +#: core/models.py:866 +msgid "Analyzing" +msgstr "" + +#: core/models.py:873 msgid "Ready" msgstr "Ready" -#: core/models.py:854 +#: core/models.py:879 msgid "Background image" msgstr "Background image" -#: core/models.py:866 +#: core/models.py:891 msgid "title" msgstr "title" -#: core/models.py:890 +#: core/models.py:915 msgid "Malware detection info when the analysis status is unsafe." msgstr "Malware detection info when the analysis status is unsafe." -#: core/models.py:895 +#: core/models.py:920 msgid "File" msgstr "File" -#: core/models.py:896 +#: core/models.py:921 msgid "Files" msgstr "Files" -#: core/models.py:1000 +#: core/models.py:1041 #, fuzzy #| msgid "This user is already in this recording." msgid "This file is already hard deleted." msgstr "This file is already hard deleted." -#: core/models.py:1010 +#: core/models.py:1051 msgid "To hard delete a file, it must first be soft deleted." msgstr "To hard delete a file, it must first be soft deleted." -#: core/recording/event/notification.py:116 +#: core/recording/event/notification.py:123 msgid "Your recording is ready" msgstr "Your recording is ready" +#: core/recording/event/notification.py:194 +msgid "Transcription" +msgstr "Transcription" + +#: core/recording/event/notification.py:204 +#, python-brace-format +msgid "Meeting \"{room}\" on {room_recording_date} at {room_recording_time}" +msgstr "Meeting \"{room}\" on {room_recording_date} at {room_recording_time}" + #: core/services/invitation.py:44 #, python-brace-format msgid "Video call in progress: {sender.email} is waiting for you to connect" @@ -571,7 +622,7 @@ msgid "To keep this recording permanently:" msgstr "To keep this recording permanently:" #: core/templates/mail/html/screen_recording.html:208 -#: core/templates/mail/text/screen_recording.txt:13 +#, python-format msgid "Click the \"Open\" link below " msgstr "Click the \"Open\" link below " @@ -600,18 +651,24 @@ msgstr "" " If you have any questions or need assistance, please contact our support " "team at %(support_email)s. " -#: meet/settings.py:223 +#: core/templates/mail/text/screen_recording.txt:13 +#, fuzzy, python-format +#| msgid "Click the \"Open\" link below " +msgid "Click the \"Open [%(link)s]\" link below " +msgstr "Click the \"Open\" link below " + +#: meet/settings.py:228 msgid "English" msgstr "English" -#: meet/settings.py:224 +#: meet/settings.py:229 msgid "French" msgstr "French" -#: meet/settings.py:225 +#: meet/settings.py:230 msgid "Dutch" msgstr "Dutch" -#: meet/settings.py:226 +#: meet/settings.py:231 msgid "German" msgstr "German" diff --git a/src/backend/locale/fr_FR/LC_MESSAGES/django.po b/src/backend/locale/fr_FR/LC_MESSAGES/django.po index cff45d0a..d26a3db3 100644 --- a/src/backend/locale/fr_FR/LC_MESSAGES/django.po +++ b/src/backend/locale/fr_FR/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-04-21 14:09+0000\n" +"POT-Creation-Date: 2026-07-02 10:47+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: antoine.lebaud@mail.numerique.gouv.fr\n" "Language-Team: LANGUAGE \n" @@ -17,71 +17,93 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: core/admin.py:29 +#: core/admin.py:67 msgid "Personal info" msgstr "Informations personnelles" -#: core/admin.py:42 +#: core/admin.py:80 msgid "Permissions" msgstr "Permissions" -#: core/admin.py:54 +#: core/admin.py:92 msgid "Important dates" msgstr "Dates importantes" -#: core/admin.py:132 core/admin.py:275 +#: core/admin.py:206 +msgid "Content" +msgstr "" + +#: core/admin.py:217 +#, fuzzy +#| msgid "Delete rooms" +msgid "Deletion" +msgstr "Supprimer les salles" + +#: core/admin.py:226 +msgid "Derived info" +msgstr "" + +#: core/admin.py:237 +msgid "Timestamps" +msgstr "" + +#: core/admin.py:240 +msgid "File preview" +msgstr "" + +#: core/admin.py:300 core/admin.py:443 msgid "No owner" msgstr "Pas de propriétaire" -#: core/admin.py:135 core/admin.py:278 +#: core/admin.py:303 core/admin.py:446 msgid "Multiple owners" msgstr "Plusieurs propriétaires" -#: core/admin.py:148 +#: core/admin.py:316 msgid "Resend notification to external service" msgstr "Renvoyer la notification au service externe" -#: core/admin.py:171 +#: core/admin.py:339 #, python-format msgid "Failed to notify for recording %(id)s" msgstr "Échec de la notification pour l’enregistrement %(id)s" -#: core/admin.py:179 +#: core/admin.py:347 #, python-format msgid "Failed to notify for recording %(id)s: %(error)s" msgstr "Échec de la notification pour l’enregistrement %(id)s : %(error)s" -#: core/admin.py:187 +#: core/admin.py:355 #, python-format msgid "Successfully sent notifications for %(count)s recording(s)." msgstr "Notifications envoyées avec succès pour %(count)s enregistrement(s)." -#: core/admin.py:195 +#: core/admin.py:363 #, python-format msgid "Skipped %(count)s expired recording(s)." msgstr "%(count)s enregistrement(s) expiré(s) ignoré(s)." -#: core/admin.py:200 +#: core/admin.py:368 msgid "Mark selected recordings as 'Failed to Stop'" msgstr "Marquer les enregistrements sélectionnés comme « Échec d’arrêt »" -#: core/admin.py:218 +#: core/admin.py:386 #, python-format msgid "%(count)s recording(s) successfully marked as 'Failed to Stop'." msgstr "" "%(count)s enregistrement(s) marqué(s) avec succès comme « Échec d’arrêt »." -#: core/admin.py:226 +#: core/admin.py:394 #, fuzzy, python-format #| msgid "Skipped %(count)s expired recording(s)." msgid "Skipped %(count)s recording(s) with an ineligible status." msgstr "%(count)s enregistrement(s) avec un statut inéligible ignoré(s)." -#: core/admin.py:342 +#: core/admin.py:510 msgid "No scopes" msgstr "Aucun scopes" -#: core/admin.py:344 +#: core/admin.py:512 msgid "Scopes" msgstr "Scopes" @@ -89,17 +111,17 @@ msgstr "Scopes" msgid "Creator is me" msgstr "Je suis le créateur" -#: core/api/serializers.py:88 +#: core/api/serializers.py:89 msgid "You must be administrator or owner of a room to add accesses to it." msgstr "" "Vous devez être administrateur ou propriétaire d'une salle pour y ajouter " "des accès." -#: core/api/serializers.py:509 +#: core/api/serializers.py:534 msgid "This file extension is not allowed." msgstr "Cette extension n'est pas autorisée" -#: core/api/viewsets.py:1090 +#: core/api/viewsets.py:1222 msgid "You have reached the maximum number of files for this type." msgstr "Vous avez atteint le nombre maximum de fichiers de ce type" @@ -147,53 +169,61 @@ msgstr "Échec à l'arrêt" msgid "Notification succeeded" msgstr "Notification réussie" -#: core/models.py:89 +#: core/models.py:65 +msgid "External process successful" +msgstr "Traitement externe : succès" + +#: core/models.py:67 +msgid "External process failed" +msgstr "Traitement externe : erreur" + +#: core/models.py:96 msgid "SCREEN_RECORDING" msgstr "ENREGISTREMENT_ÉCRAN" -#: core/models.py:90 +#: core/models.py:97 msgid "TRANSCRIPT" msgstr "TRANSCRIPTION" -#: core/models.py:96 +#: core/models.py:103 msgid "Public Access" msgstr "Accès public" -#: core/models.py:97 +#: core/models.py:104 msgid "Trusted Access" msgstr "Accès de confiance" -#: core/models.py:98 +#: core/models.py:105 msgid "Restricted Access" msgstr "Accès restreint" -#: core/models.py:110 +#: core/models.py:117 msgid "id" msgstr "id" -#: core/models.py:111 +#: core/models.py:118 msgid "primary key for the record as UUID" msgstr "clé primaire pour l'enregistrement sous forme d'UUID" -#: core/models.py:117 +#: core/models.py:124 msgid "created on" msgstr "créé le" -#: core/models.py:118 +#: core/models.py:125 msgid "date and time at which a record was created" msgstr "date et heure auxquelles un enregistrement a été créé" -#: core/models.py:123 +#: core/models.py:130 msgid "updated on" msgstr "mis à jour le" -#: core/models.py:124 +#: core/models.py:131 msgid "date and time at which a record was last updated" msgstr "" "date et heure auxquelles un enregistrement a été mis à jour pour la dernière " "fois" -#: core/models.py:144 +#: core/models.py:151 msgid "" "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/" "_ characters." @@ -201,11 +231,11 @@ msgstr "" "Entrez un sub valide. Cette valeur ne peut contenir que des lettres, des " "chiffres et les caractères @/./+/-/_." -#: core/models.py:150 +#: core/models.py:157 msgid "sub" msgstr "sub" -#: core/models.py:152 +#: core/models.py:159 msgid "" "Optional for pending users; required upon account activation. 255 characters " "or fewer. Letters, numbers, and @/./+/-/_ characters only." @@ -213,55 +243,55 @@ msgstr "" "Optionnel pour les utilisateurs en attente ; requis lors de l'activation du " "compte. 255 caractères maximum. Lettres, chiffres et @/./+/-/_ uniquement." -#: core/models.py:161 +#: core/models.py:168 msgid "identity email address" msgstr "adresse e-mail d'identité" -#: core/models.py:166 +#: core/models.py:173 msgid "admin email address" msgstr "adresse e-mail d'administrateur" -#: core/models.py:168 +#: core/models.py:175 msgid "full name" msgstr "nom complet" -#: core/models.py:170 +#: core/models.py:177 msgid "short name" msgstr "nom court" -#: core/models.py:176 +#: core/models.py:183 msgid "language" msgstr "langue" -#: core/models.py:177 +#: core/models.py:184 msgid "The language in which the user wants to see the interface." msgstr "La langue dans laquelle l'utilisateur souhaite voir l'interface." -#: core/models.py:183 +#: core/models.py:190 msgid "The timezone in which the user wants to see times." msgstr "Le fuseau horaire dans lequel l'utilisateur souhaite voir les heures." -#: core/models.py:186 +#: core/models.py:193 msgid "device" msgstr "appareil" -#: core/models.py:188 +#: core/models.py:195 msgid "Whether the user is a device or a real user." msgstr "Si l'utilisateur est un appareil ou un utilisateur réel." -#: core/models.py:191 +#: core/models.py:198 msgid "staff status" msgstr "statut du personnel" -#: core/models.py:193 +#: core/models.py:200 msgid "Whether the user can log into this admin site." msgstr "Si l'utilisateur peut se connecter à ce site d'administration." -#: core/models.py:196 +#: core/models.py:203 msgid "active" msgstr "actif" -#: core/models.py:199 +#: core/models.py:206 msgid "" "Whether this user should be treated as active. Unselect this instead of " "deleting accounts." @@ -269,65 +299,65 @@ msgstr "" "Si cet utilisateur doit être traité comme actif. Désélectionnez cette option " "au lieu de supprimer des comptes." -#: core/models.py:212 +#: core/models.py:219 msgid "user" msgstr "utilisateur" -#: core/models.py:213 +#: core/models.py:220 msgid "users" msgstr "utilisateurs" -#: core/models.py:272 +#: core/models.py:286 msgid "Resource" msgstr "Ressource" -#: core/models.py:273 +#: core/models.py:287 msgid "Resources" msgstr "Ressources" -#: core/models.py:331 +#: core/models.py:345 msgid "Resource access" msgstr "Accès aux ressources" -#: core/models.py:332 +#: core/models.py:346 msgid "Resource accesses" msgstr "Accès aux ressources" -#: core/models.py:338 +#: core/models.py:352 msgid "Resource access with this User and Resource already exists." msgstr "" "L'accès à la ressource avec cet utilisateur et cette ressource existe déjà." -#: core/models.py:394 +#: core/models.py:409 msgid "Visio room configuration" msgstr "Configuration de la salle de visioconférence" -#: core/models.py:395 +#: core/models.py:410 msgid "Values for Visio parameters to configure the room." msgstr "Valeurs des paramètres de visioconférence pour configurer la salle." -#: core/models.py:402 +#: core/models.py:417 msgid "Room PIN code" msgstr "Code PIN de la salle" -#: core/models.py:403 +#: core/models.py:418 msgid "Unique n-digit code that identifies this room in telephony mode." msgstr "" "Code unique à n chiffres qui identifie cette salle en mode téléphonique." -#: core/models.py:409 core/models.py:563 +#: core/models.py:424 core/models.py:578 msgid "Room" msgstr "Salle" -#: core/models.py:410 +#: core/models.py:425 msgid "Rooms" msgstr "Salles" -#: core/models.py:574 +#: core/models.py:589 msgid "Worker ID" msgstr "ID du Worker" -#: core/models.py:576 +#: core/models.py:591 msgid "" "Enter an identifier for the worker recording.This ID is retained even when " "the worker stops, allowing for easy tracking." @@ -335,153 +365,174 @@ msgstr "" "Entrez un identifiant pour l'enregistrement du Worker. Cet identifiant est " "conservé même lorsque le Worker s'arrête, permettant un suivi facile." -#: core/models.py:584 +#: core/models.py:599 msgid "Recording mode" msgstr "Mode d'enregistrement" -#: core/models.py:585 +#: core/models.py:600 msgid "Defines the mode of recording being called." msgstr "Définit le mode d'enregistrement appelé." -#: core/models.py:590 core/models.py:591 +#: core/models.py:605 core/models.py:606 msgid "Recording options" msgstr "Options d'enregistrement" -#: core/models.py:597 +#: core/models.py:613 +msgid "External Process ID" +msgstr "ID Traitement externe" + +#: core/models.py:614 +msgid "ID of the external process associated with the recording." +msgstr "ID du traitement externe associé avec l'enregistrement." + +#: core/models.py:620 msgid "Recording" msgstr "Enregistrement" -#: core/models.py:598 +#: core/models.py:621 msgid "Recordings" msgstr "Enregistrements" -#: core/models.py:706 +#: core/models.py:731 msgid "Recording/user relation" msgstr "Relation enregistrement/utilisateur" -#: core/models.py:707 +#: core/models.py:732 msgid "Recording/user relations" msgstr "Relations enregistrement/utilisateur" -#: core/models.py:713 +#: core/models.py:738 msgid "This user is already in this recording." msgstr "Cet utilisateur est déjà dans cet enregistrement." -#: core/models.py:719 +#: core/models.py:744 msgid "This team is already in this recording." msgstr "Cette équipe est déjà dans cet enregistrement." -#: core/models.py:725 +#: core/models.py:750 msgid "Either user or team must be set, not both." msgstr "Soit l'utilisateur, soit l'équipe doit être défini, pas les deux." -#: core/models.py:742 +#: core/models.py:767 msgid "Create rooms" msgstr "Créer des salles" -#: core/models.py:743 +#: core/models.py:768 msgid "List rooms" msgstr "Lister les salles" -#: core/models.py:744 +#: core/models.py:769 msgid "Retrieve room details" msgstr "Afficher les détails d’une salle" -#: core/models.py:745 +#: core/models.py:770 msgid "Update rooms" msgstr "Mettre à jour les salles" -#: core/models.py:746 +#: core/models.py:771 msgid "Delete rooms" msgstr "Supprimer les salles" -#: core/models.py:759 +#: core/models.py:784 msgid "Application name" msgstr "Nom de l’application" -#: core/models.py:760 +#: core/models.py:785 msgid "Descriptive name for this application." msgstr "Nom descriptif de cette application." -#: core/models.py:770 +#: core/models.py:795 msgid "Hashed on Save. Copy it now if this is a new secret." msgstr "" "Haché lors de l’enregistrement. Copiez-le maintenant s’il s’agit d’un " "nouveau secret." -#: core/models.py:781 +#: core/models.py:806 msgid "Application" msgstr "Application" -#: core/models.py:782 +#: core/models.py:807 msgid "Applications" msgstr "Applications" -#: core/models.py:805 +#: core/models.py:830 msgid "Enter a valid domain" msgstr "Saisissez un domaine valide" -#: core/models.py:808 +#: core/models.py:833 msgid "Domain" msgstr "Domaine" -#: core/models.py:809 +#: core/models.py:834 msgid "Email domain this application can act on behalf of." msgstr "Domaine de messagerie au nom duquel cette application peut agir." -#: core/models.py:821 +#: core/models.py:846 msgid "Application domain" msgstr "Domaine d’application" -#: core/models.py:822 +#: core/models.py:847 msgid "Application domains" msgstr "Domaines d’application" -#: core/models.py:840 +#: core/models.py:865 msgid "Pending" msgstr "En attente" -#: core/models.py:848 +#: core/models.py:866 +msgid "Analyzing" +msgstr "" + +#: core/models.py:873 msgid "Ready" msgstr "Prêt" -#: core/models.py:854 +#: core/models.py:879 msgid "Background image" msgstr "Image de fond" -#: core/models.py:866 +#: core/models.py:891 msgid "title" msgstr "Titre" -#: core/models.py:890 +#: core/models.py:915 msgid "Malware detection info when the analysis status is unsafe." msgstr "" "Information concernant la détection de Malware cand le statut n'est pas sain" -#: core/models.py:895 +#: core/models.py:920 msgid "File" msgstr "Fichier" -#: core/models.py:896 +#: core/models.py:921 msgid "Files" msgstr "Fichiers" -#: core/models.py:1000 +#: core/models.py:1041 #, fuzzy #| msgid "This user is already in this recording." msgid "This file is already hard deleted." msgstr "Ce fichier a été supprimé." -#: core/models.py:1010 +#: core/models.py:1051 msgid "To hard delete a file, it must first be soft deleted." msgstr "" "Pour supprimer définitivement un fichier il doit d'abord avoir été marqué " "comme supprimé (soft delete)" -#: core/recording/event/notification.py:116 +#: core/recording/event/notification.py:123 msgid "Your recording is ready" msgstr "Votre enregistrement est prêt" +#: core/recording/event/notification.py:194 +msgid "Transcription" +msgstr "Transcription" + +#: core/recording/event/notification.py:204 +#, python-brace-format +msgid "Meeting \"{room}\" on {room_recording_date} at {room_recording_time}" +msgstr "Réunion \"{room}\" du {room_recording_date} à {room_recording_time}" + #: core/services/invitation.py:44 #, python-brace-format msgid "Video call in progress: {sender.email} is waiting for you to connect" @@ -577,7 +628,7 @@ msgid "To keep this recording permanently:" msgstr "Pour conserver cet enregistrement de façon permanente :" #: core/templates/mail/html/screen_recording.html:208 -#: core/templates/mail/text/screen_recording.txt:13 +#, python-format msgid "Click the \"Open\" link below " msgstr "Cliquez sur le lien \"Ouvrir\" ci-dessous " @@ -606,18 +657,24 @@ msgstr "" " Si vous avez des questions ou besoin d'assistance, veuillez contacter notre " "équipe d'assistance à %(support_email)s. " -#: meet/settings.py:223 +#: core/templates/mail/text/screen_recording.txt:13 +#, fuzzy, python-format +#| msgid "Click the \"Open\" link below " +msgid "Click the \"Open [%(link)s]\" link below " +msgstr "Cliquez sur le lien \"Ouvrir\" ci-dessous " + +#: meet/settings.py:228 msgid "English" msgstr "Anglais" -#: meet/settings.py:224 +#: meet/settings.py:229 msgid "French" msgstr "Français" -#: meet/settings.py:225 +#: meet/settings.py:230 msgid "Dutch" msgstr "Néerlandais" -#: meet/settings.py:226 +#: meet/settings.py:231 msgid "German" msgstr "Allemand" diff --git a/src/backend/locale/nl_NL/LC_MESSAGES/django.po b/src/backend/locale/nl_NL/LC_MESSAGES/django.po index 78328812..236a187d 100644 --- a/src/backend/locale/nl_NL/LC_MESSAGES/django.po +++ b/src/backend/locale/nl_NL/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-04-21 14:09+0000\n" +"POT-Creation-Date: 2026-07-02 10:47+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,70 +17,92 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: core/admin.py:29 +#: core/admin.py:67 msgid "Personal info" msgstr "Persoonlijke informatie" -#: core/admin.py:42 +#: core/admin.py:80 msgid "Permissions" msgstr "Rechten" -#: core/admin.py:54 +#: core/admin.py:92 msgid "Important dates" msgstr "Belangrijke datums" -#: core/admin.py:132 core/admin.py:275 +#: core/admin.py:206 +msgid "Content" +msgstr "" + +#: core/admin.py:217 +#, fuzzy +#| msgid "Delete rooms" +msgid "Deletion" +msgstr "Ruimtes verwijderen" + +#: core/admin.py:226 +msgid "Derived info" +msgstr "" + +#: core/admin.py:237 +msgid "Timestamps" +msgstr "" + +#: core/admin.py:240 +msgid "File preview" +msgstr "" + +#: core/admin.py:300 core/admin.py:443 msgid "No owner" msgstr "Geen eigenaar" -#: core/admin.py:135 core/admin.py:278 +#: core/admin.py:303 core/admin.py:446 msgid "Multiple owners" msgstr "Meerdere eigenaren" -#: core/admin.py:148 +#: core/admin.py:316 msgid "Resend notification to external service" msgstr "Melding opnieuw verzenden naar externe dienst" -#: core/admin.py:171 +#: core/admin.py:339 #, python-format msgid "Failed to notify for recording %(id)s" msgstr "Melding voor opname %(id)s mislukt" -#: core/admin.py:179 +#: core/admin.py:347 #, python-format msgid "Failed to notify for recording %(id)s: %(error)s" msgstr "Melding voor opname %(id)s mislukt: %(error)s" -#: core/admin.py:187 +#: core/admin.py:355 #, python-format msgid "Successfully sent notifications for %(count)s recording(s)." msgstr "Meldingen succesvol verzonden voor %(count)s opname(n)." -#: core/admin.py:195 +#: core/admin.py:363 #, python-format msgid "Skipped %(count)s expired recording(s)." msgstr "%(count)s verlopen opname(n) overgeslagen." -#: core/admin.py:200 +#: core/admin.py:368 msgid "Mark selected recordings as 'Failed to Stop'" msgstr "Geselecteerde opnames markeren als 'Mislukt bij stoppen'" -#: core/admin.py:218 +#: core/admin.py:386 #, python-format msgid "%(count)s recording(s) successfully marked as 'Failed to Stop'." msgstr "%(count)s opname(s) succesvol gemarkeerd als 'Mislukt bij stoppen'." -#: core/admin.py:226 +#: core/admin.py:394 #, fuzzy, python-format #| msgid "Skipped %(count)s expired recording(s)." msgid "Skipped %(count)s recording(s) with an ineligible status." msgstr "%(count)s opname(s) met een niet-toegestane status overgeslagen." -#: core/admin.py:342 +#: core/admin.py:510 msgid "No scopes" msgstr "Geen scopes" -#: core/admin.py:344 +#: core/admin.py:512 msgid "Scopes" msgstr "Scopes" @@ -88,16 +110,16 @@ msgstr "Scopes" msgid "Creator is me" msgstr "Maker ben ik" -#: core/api/serializers.py:88 +#: core/api/serializers.py:89 msgid "You must be administrator or owner of a room to add accesses to it." msgstr "" "Je moet beheerder of eigenaar van een ruimte zijn om toegang toe te voegen." -#: core/api/serializers.py:509 +#: core/api/serializers.py:534 msgid "This file extension is not allowed." msgstr "Deze bestandsextensie is niet toegestaan." -#: core/api/viewsets.py:1090 +#: core/api/viewsets.py:1222 msgid "You have reached the maximum number of files for this type." msgstr "Het maximale aantal bestanden voor dit type is bereikt." @@ -145,51 +167,59 @@ msgstr "Stoppen mislukt" msgid "Notification succeeded" msgstr "Notificatie geslaagd" -#: core/models.py:89 +#: core/models.py:65 +msgid "External process successful" +msgstr "Externe procedure succesvol" + +#: core/models.py:67 +msgid "External process failed" +msgstr "Het externe proces is mislukt." + +#: core/models.py:96 msgid "SCREEN_RECORDING" msgstr "SCHERM_OPNAME" -#: core/models.py:90 +#: core/models.py:97 msgid "TRANSCRIPT" msgstr "TRANSCRIPT" -#: core/models.py:96 +#: core/models.py:103 msgid "Public Access" msgstr "Openbare toegang" -#: core/models.py:97 +#: core/models.py:104 msgid "Trusted Access" msgstr "Vertrouwde toegang" -#: core/models.py:98 +#: core/models.py:105 msgid "Restricted Access" msgstr "Beperkte toegang" -#: core/models.py:110 +#: core/models.py:117 msgid "id" msgstr "id" -#: core/models.py:111 +#: core/models.py:118 msgid "primary key for the record as UUID" msgstr "primaire sleutel voor het record als UUID" -#: core/models.py:117 +#: core/models.py:124 msgid "created on" msgstr "aangemaakt op" -#: core/models.py:118 +#: core/models.py:125 msgid "date and time at which a record was created" msgstr "datum en tijd waarop een record werd aangemaakt" -#: core/models.py:123 +#: core/models.py:130 msgid "updated on" msgstr "bijgewerkt op" -#: core/models.py:124 +#: core/models.py:131 msgid "date and time at which a record was last updated" msgstr "datum en tijd waarop een record voor het laatst werd bijgewerkt" -#: core/models.py:144 +#: core/models.py:151 msgid "" "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/" "_ characters." @@ -197,11 +227,11 @@ msgstr "" "Voer een geldige sub in. Deze waarde mag alleen letters, cijfers en @/./+/-/" "_ tekens bevatten." -#: core/models.py:150 +#: core/models.py:157 msgid "sub" msgstr "sub" -#: core/models.py:152 +#: core/models.py:159 msgid "" "Optional for pending users; required upon account activation. 255 characters " "or fewer. Letters, numbers, and @/./+/-/_ characters only." @@ -209,55 +239,55 @@ msgstr "" "Optioneel voor gebruikers in afwachting; vereist bij accountactivering. " "Maximum 255 tekens. Alleen letters, cijfers en @/./+/-/_ toegestaan." -#: core/models.py:161 +#: core/models.py:168 msgid "identity email address" msgstr "identiteit e-mailadres" -#: core/models.py:166 +#: core/models.py:173 msgid "admin email address" msgstr "beheerder e-mailadres" -#: core/models.py:168 +#: core/models.py:175 msgid "full name" msgstr "volledige naam" -#: core/models.py:170 +#: core/models.py:177 msgid "short name" msgstr "korte naam" -#: core/models.py:176 +#: core/models.py:183 msgid "language" msgstr "taal" -#: core/models.py:177 +#: core/models.py:184 msgid "The language in which the user wants to see the interface." msgstr "De taal waarin de gebruiker de interface wil zien." -#: core/models.py:183 +#: core/models.py:190 msgid "The timezone in which the user wants to see times." msgstr "De tijdzone waarin de gebruiker tijden wil zien." -#: core/models.py:186 +#: core/models.py:193 msgid "device" msgstr "apparaat" -#: core/models.py:188 +#: core/models.py:195 msgid "Whether the user is a device or a real user." msgstr "Of de gebruiker een apparaat is of een echte gebruiker." -#: core/models.py:191 +#: core/models.py:198 msgid "staff status" msgstr "personeelsstatus" -#: core/models.py:193 +#: core/models.py:200 msgid "Whether the user can log into this admin site." msgstr "Of de gebruiker kan inloggen op deze beheersite." -#: core/models.py:196 +#: core/models.py:203 msgid "active" msgstr "actief" -#: core/models.py:199 +#: core/models.py:206 msgid "" "Whether this user should be treated as active. Unselect this instead of " "deleting accounts." @@ -265,64 +295,64 @@ msgstr "" "Of deze gebruiker als actief moet worden behandeld. Deselecteer dit in " "plaats van accounts te verwijderen." -#: core/models.py:212 +#: core/models.py:219 msgid "user" msgstr "gebruiker" -#: core/models.py:213 +#: core/models.py:220 msgid "users" msgstr "gebruikers" -#: core/models.py:272 +#: core/models.py:286 msgid "Resource" msgstr "Bron" -#: core/models.py:273 +#: core/models.py:287 msgid "Resources" msgstr "Bronnen" -#: core/models.py:331 +#: core/models.py:345 msgid "Resource access" msgstr "Brontoegang" -#: core/models.py:332 +#: core/models.py:346 msgid "Resource accesses" msgstr "Brontoegangsrechten" -#: core/models.py:338 +#: core/models.py:352 msgid "Resource access with this User and Resource already exists." msgstr "Brontoegang met deze gebruiker en bron bestaat al." -#: core/models.py:394 +#: core/models.py:409 msgid "Visio room configuration" msgstr "Visio-ruimteconfiguratie" -#: core/models.py:395 +#: core/models.py:410 msgid "Values for Visio parameters to configure the room." msgstr "Waarden voor Visio-parameters om de ruimte te configureren." -#: core/models.py:402 +#: core/models.py:417 msgid "Room PIN code" msgstr "Pincode van de kamer" -#: core/models.py:403 +#: core/models.py:418 msgid "Unique n-digit code that identifies this room in telephony mode." msgstr "" "Unieke n-cijferige code die deze kamer identificeert in telefonie-modus." -#: core/models.py:409 core/models.py:563 +#: core/models.py:424 core/models.py:578 msgid "Room" msgstr "Ruimte" -#: core/models.py:410 +#: core/models.py:425 msgid "Rooms" msgstr "Ruimtes" -#: core/models.py:574 +#: core/models.py:589 msgid "Worker ID" msgstr "Worker ID" -#: core/models.py:576 +#: core/models.py:591 msgid "" "Enter an identifier for the worker recording.This ID is retained even when " "the worker stops, allowing for easy tracking." @@ -330,140 +360,152 @@ msgstr "" "Voer een identificatie in voor de worker-opname. Deze ID blijft behouden, " "zelfs wanneer de worker stopt, waardoor eenvoudige tracking mogelijk is." -#: core/models.py:584 +#: core/models.py:599 msgid "Recording mode" msgstr "Opnamemodus" -#: core/models.py:585 +#: core/models.py:600 msgid "Defines the mode of recording being called." msgstr "Definieert de modus van opname die wordt aangeroepen." -#: core/models.py:590 core/models.py:591 +#: core/models.py:605 core/models.py:606 msgid "Recording options" msgstr "Opnameopties" -#: core/models.py:597 +#: core/models.py:613 +msgid "External Process ID" +msgstr "Externe proces-ID" + +#: core/models.py:614 +msgid "ID of the external process associated with the recording." +msgstr "ID van het externe proces dat aan de opname is gekoppeld." + +#: core/models.py:620 msgid "Recording" msgstr "Opname" -#: core/models.py:598 +#: core/models.py:621 msgid "Recordings" msgstr "Opnames" -#: core/models.py:706 +#: core/models.py:731 msgid "Recording/user relation" msgstr "Opname/gebruiker-relatie" -#: core/models.py:707 +#: core/models.py:732 msgid "Recording/user relations" msgstr "Opname/gebruiker-relaties" -#: core/models.py:713 +#: core/models.py:738 msgid "This user is already in this recording." msgstr "Deze gebruiker is al in deze opname." -#: core/models.py:719 +#: core/models.py:744 msgid "This team is already in this recording." msgstr "Dit team is al in deze opname." -#: core/models.py:725 +#: core/models.py:750 msgid "Either user or team must be set, not both." msgstr "Ofwel gebruiker of team moet worden ingesteld, niet beide." -#: core/models.py:742 +#: core/models.py:767 msgid "Create rooms" msgstr "Ruimtes aanmaken" -#: core/models.py:743 +#: core/models.py:768 msgid "List rooms" msgstr "Ruimtes weergeven" -#: core/models.py:744 +#: core/models.py:769 msgid "Retrieve room details" msgstr "Details van een ruimte ophalen" -#: core/models.py:745 +#: core/models.py:770 msgid "Update rooms" msgstr "Ruimtes bijwerken" -#: core/models.py:746 +#: core/models.py:771 msgid "Delete rooms" msgstr "Ruimtes verwijderen" -#: core/models.py:759 +#: core/models.py:784 msgid "Application name" msgstr "Naam van de applicatie" -#: core/models.py:760 +#: core/models.py:785 msgid "Descriptive name for this application." msgstr "Beschrijvende naam voor deze applicatie." -#: core/models.py:770 +#: core/models.py:795 msgid "Hashed on Save. Copy it now if this is a new secret." msgstr "" "Wordt gehasht bij het opslaan. Kopieer het nu als dit een nieuw geheim is." -#: core/models.py:781 +#: core/models.py:806 msgid "Application" msgstr "Applicatie" -#: core/models.py:782 +#: core/models.py:807 msgid "Applications" msgstr "Applicaties" -#: core/models.py:805 +#: core/models.py:830 msgid "Enter a valid domain" msgstr "Voer een geldig domein in" -#: core/models.py:808 +#: core/models.py:833 msgid "Domain" msgstr "Domein" -#: core/models.py:809 +#: core/models.py:834 msgid "Email domain this application can act on behalf of." msgstr "E-maildomein namens welke deze applicatie kan handelen." -#: core/models.py:821 +#: core/models.py:846 msgid "Application domain" msgstr "Applicatiedomein" -#: core/models.py:822 +#: core/models.py:847 msgid "Application domains" msgstr "Applicatiedomeinen" -#: core/models.py:840 +#: core/models.py:865 msgid "Pending" msgstr "In afwachting" -#: core/models.py:848 +#: core/models.py:866 +msgid "Analyzing" +msgstr "" + +#: core/models.py:873 msgid "Ready" msgstr "Klaar" -#: core/models.py:854 +#: core/models.py:879 msgid "Background image" msgstr "Achtergrondafbeelding" -#: core/models.py:866 +#: core/models.py:891 msgid "title" msgstr "Titel" -#: core/models.py:890 +#: core/models.py:915 msgid "Malware detection info when the analysis status is unsafe." msgstr "Informatie over malwaredetectie wanneer de analysestatus onveilig is." -#: core/models.py:895 +#: core/models.py:920 msgid "File" msgstr "Bestand" -#: core/models.py:896 +#: core/models.py:921 msgid "Files" msgstr "Bestanden" -#: core/models.py:1000 +#: core/models.py:1041 msgid "This file is already hard deleted." msgstr "Dit bestand is al definitief verwijderd." -#: core/models.py:1010 +#: core/models.py:1051 #, fuzzy #| msgid "To hard delete a file, it must first be soft deleted." msgid "To hard delete a file, it must first be soft deleted." @@ -471,10 +513,20 @@ msgstr "" "Om een bestand definitief te verwijderen, moet het eerst zacht verwijderd " "zijn." -#: core/recording/event/notification.py:116 +#: core/recording/event/notification.py:123 msgid "Your recording is ready" msgstr "Je opname is klaar" +#: core/recording/event/notification.py:194 +msgid "Transcription" +msgstr "Transcriptie" + +#: core/recording/event/notification.py:204 +#, python-brace-format +msgid "Meeting \"{room}\" on {room_recording_date} at {room_recording_time}" +msgstr "" +"Vergadering \"{room}\" op {room_recording_date} om {room_recording_time}" + #: core/services/invitation.py:44 #, python-brace-format msgid "Video call in progress: {sender.email} is waiting for you to connect" @@ -570,7 +622,7 @@ msgid "To keep this recording permanently:" msgstr "Om deze opname permanent te bewaren:" #: core/templates/mail/html/screen_recording.html:208 -#: core/templates/mail/text/screen_recording.txt:13 +#, python-format msgid "Click the \"Open\" link below " msgstr "Klik op de \"Openen\"-link hieronder " @@ -599,18 +651,24 @@ msgstr "" " Als je vragen hebt of hulp nodig hebt, neem dan contact op met ons support " "team via %(support_email)s. " -#: meet/settings.py:223 +#: core/templates/mail/text/screen_recording.txt:13 +#, fuzzy, python-format +#| msgid "Click the \"Open\" link below " +msgid "Click the \"Open [%(link)s]\" link below " +msgstr "Klik op de \"Openen\"-link hieronder " + +#: meet/settings.py:228 msgid "English" msgstr "Engels" -#: meet/settings.py:224 +#: meet/settings.py:229 msgid "French" msgstr "Frans" -#: meet/settings.py:225 +#: meet/settings.py:230 msgid "Dutch" msgstr "Nederlands" -#: meet/settings.py:226 +#: meet/settings.py:231 msgid "German" msgstr "Duits" diff --git a/src/backend/meet/settings.py b/src/backend/meet/settings.py index 0de934da..1aacc705 100755 --- a/src/backend/meet/settings.py +++ b/src/backend/meet/settings.py @@ -748,12 +748,30 @@ class Base(Configuration): environ_prefix=None, ) + SUMMARY_SERVICE_VERSION = values.PositiveIntegerValue( + 1, environ_name="SUMMARY_SERVICE_VERSION", environ_prefix=None + ) SUMMARY_SERVICE_ENDPOINT = values.Value( None, environ_name="SUMMARY_SERVICE_ENDPOINT", environ_prefix=None ) SUMMARY_SERVICE_API_TOKEN = SecretFileValue( None, environ_name="SUMMARY_SERVICE_API_TOKEN", environ_prefix=None ) + SUMMARY_SERVICE_WEBHOOK_API_TOKEN = SecretFileValue( + None, environ_name="SUMMARY_SERVICE_WEBHOOK_API_TOKEN", environ_prefix=None + ) + SUMMARY_SERVICE_CLOUD_STORAGE_SIGNED_URL_EXPIRY_SECONDS = ( + values.PositiveIntegerValue( + 60 * 60 * 24, + environ_name="SUMMARY_SERVICE_CLOUD_STORAGE_SIGNED_URL_EXPIRY_SECONDS", + environ_prefix=None, + ) + ) + TRANSCRIPTION_SATISFACTION_FORM_BASE_URL = values.Value( + None, + environ_name="TRANSCRIPTION_SATISFACTION_FORM_BASE_URL", + environ_prefix=None, + ) SCREEN_RECORDING_BASE_URL = values.Value( None, environ_name="SCREEN_RECORDING_BASE_URL", environ_prefix=None ) diff --git a/src/frontend/src/features/recording/routes/RecordingDownload.tsx b/src/frontend/src/features/recording/routes/RecordingDownload.tsx index e50c3296..bc129dc7 100644 --- a/src/frontend/src/features/recording/routes/RecordingDownload.tsx +++ b/src/frontend/src/features/recording/routes/RecordingDownload.tsx @@ -57,7 +57,9 @@ const RecordingDownload = () => { const isSaved = data?.status === RecordingStatus.Saved || data?.status === RecordingStatus.NotificationSucceed || - data?.status === RecordingStatus.FailedToStop + data?.status === RecordingStatus.FailedToStop || + data?.status === RecordingStatus.ExternalProcessFailed || + data?.status === RecordingStatus.ExternalProcessSuccessful const pageTitle = useMemo(() => { if (isError) return `${APP_TITLE} - ${t('error.title')}` @@ -91,6 +93,8 @@ const RecordingDownload = () => { } if ( + data.status !== RecordingStatus.ExternalProcessFailed && + data.status !== RecordingStatus.ExternalProcessSuccessful && data.status !== RecordingStatus.Saved && data.status !== RecordingStatus.NotificationSucceed && data.status !== RecordingStatus.FailedToStop diff --git a/src/frontend/src/features/recording/types.ts b/src/frontend/src/features/recording/types.ts index d41b6090..fc450bfe 100644 --- a/src/frontend/src/features/recording/types.ts +++ b/src/frontend/src/features/recording/types.ts @@ -12,4 +12,6 @@ export enum RecordingStatus { FailedToStart = 'failedToStart', FailedToStop = 'failedToStop', NotificationSucceed = 'notification_succeeded', + ExternalProcessSuccessful = 'external_process_successful', + ExternalProcessFailed = 'external_process_failed', } diff --git a/src/helm/env.d/common.yaml.gotmpl b/src/helm/env.d/common.yaml.gotmpl index 26fbb043..0d74ad3e 100644 --- a/src/helm/env.d/common.yaml.gotmpl +++ b/src/helm/env.d/common.yaml.gotmpl @@ -38,7 +38,7 @@ _summaryEnvVars: &summaryEnvVars "allowed_push_to_docs": false }, { - "id": "visio", + "id": "meet", "api_key": "password", "webhook_url": "https://meet.127.0.0.1.nip.io/api/v1.0/recordings/external-process-hook/", "webhook_api_key": "webhook-password",