From 39271544d78d8874a20ff01eb6e0a4d5924dd1fb Mon Sep 17 00:00:00 2001 From: lebaudantoine Date: Sat, 3 Jan 2026 01:12:09 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8(summary)=20link=20transcript=20to=20t?= =?UTF-8?q?heir=20downloadable=20recording?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Link the transcription document to its related recording by adding a short header explaining that users can download the audio file via a dedicated link. This was a highly requested feature, as many users need to keep their audio files. As part of a small refactor, remove the argument length check in the metadata analytics class. The hardcoded argument count made code evolution harder and was easy to forget updating. Argument unwrapping remains fragile and should be redesigned later to be more robust. The backend is responsible for generating the download link to ensure consistency and reliability. I tried adding a divider, but the Markdown-to-Yjs conversion is very lossy and almost never handles it correctly. Only about one out of ten conversions works as expected. --- env.d/development/common.dist | 2 +- .../core/recording/event/notification.py | 20 ++++++++++++++++++- src/backend/meet/settings.py | 3 +++ .../env.d/dev-dinum/values.meet.yaml.gotmpl | 2 +- .../dev-keycloak/values.meet.yaml.gotmpl | 2 +- src/helm/env.d/dev/values.meet.yaml.gotmpl | 2 +- src/summary/summary/api/route/tasks.py | 2 ++ src/summary/summary/core/analytics.py | 5 ----- src/summary/summary/core/celery_worker.py | 2 ++ .../summary/core/transcript_formatter.py | 15 ++++++++++++++ 10 files changed, 45 insertions(+), 10 deletions(-) diff --git a/env.d/development/common.dist b/env.d/development/common.dist index 91760b49..d42b1052 100644 --- a/env.d/development/common.dist +++ b/env.d/development/common.dist @@ -63,7 +63,7 @@ RECORDING_STORAGE_EVENT_ENABLE=True RECORDING_STORAGE_EVENT_TOKEN=password SUMMARY_SERVICE_ENDPOINT=http://app-summary-dev:8000/api/v1/tasks/ SUMMARY_SERVICE_API_TOKEN=password -SCREEN_RECORDING_BASE_URL=http://localhost:3000/recordings +RECORDING_DOWNLOAD_BASE_URL=http://localhost:3000/recording # Telephony ROOM_TELEPHONY_ENABLED=True diff --git a/src/backend/core/recording/event/notification.py b/src/backend/core/recording/event/notification.py index f7fe1d57..761a7d79 100644 --- a/src/backend/core/recording/event/notification.py +++ b/src/backend/core/recording/event/notification.py @@ -16,6 +16,23 @@ from core import models logger = logging.getLogger(__name__) +def get_recording_download_base_url() -> str: + """Get the recording download base URL with backward compatibility.""" + new_setting = settings.RECORDING_DOWNLOAD_BASE_URL + old_setting = settings.SCREEN_RECORDING_BASE_URL + + if old_setting: + logger.warning( + "SCREEN_RECORDING_BASE_URL is deprecated and will be removed in a future version. " + "Please use RECORDING_DOWNLOAD_BASE_URL instead." + ) + + if new_setting: + return new_setting + + return old_setting + + class NotificationService: """Service for processing recordings and notifying external services.""" @@ -69,7 +86,7 @@ class NotificationService: "domain": settings.EMAIL_DOMAIN, "room_name": recording.room.name, "recording_expiration_days": settings.RECORDING_EXPIRATION_DAYS, - "link": f"{settings.SCREEN_RECORDING_BASE_URL}/{recording.id}", + "link": f"{get_recording_download_base_url()}/{recording.id}", } has_failures = False @@ -149,6 +166,7 @@ class NotificationService: "recording_time": recording.created_at.astimezone( owner_access.user.timezone ).strftime("%H:%M"), + "download_link": f"{get_recording_download_base_url()}/{recording.id}", } headers = { diff --git a/src/backend/meet/settings.py b/src/backend/meet/settings.py index ca9c74d2..dd6cca0f 100755 --- a/src/backend/meet/settings.py +++ b/src/backend/meet/settings.py @@ -634,6 +634,9 @@ class Base(Configuration): SCREEN_RECORDING_BASE_URL = values.Value( None, environ_name="SCREEN_RECORDING_BASE_URL", environ_prefix=None ) + RECORDING_DOWNLOAD_BASE_URL = values.Value( + None, environ_name="RECORDING_DOWNLOAD_BASE_URL", environ_prefix=None + ) # Marketing and communication settings SIGNUP_NEW_USER_TO_MARKETING_EMAIL = values.BooleanValue( diff --git a/src/helm/env.d/dev-dinum/values.meet.yaml.gotmpl b/src/helm/env.d/dev-dinum/values.meet.yaml.gotmpl index 7f7b6181..abfa5080 100644 --- a/src/helm/env.d/dev-dinum/values.meet.yaml.gotmpl +++ b/src/helm/env.d/dev-dinum/values.meet.yaml.gotmpl @@ -67,7 +67,7 @@ backend: RECORDING_STORAGE_EVENT_TOKEN: password SUMMARY_SERVICE_ENDPOINT: http://meet-summary:80/api/v1/tasks/ SUMMARY_SERVICE_API_TOKEN: password - SCREEN_RECORDING_BASE_URL: https://meet.127.0.0.1.nip.io/recordings + RECORDING_DOWNLOAD_BASE_URL: https://meet.127.0.0.1.nip.io/recording ROOM_TELEPHONY_ENABLED: True SSL_CERT_FILE: /usr/local/lib/python3.12/site-packages/certifi/cacert.pem diff --git a/src/helm/env.d/dev-keycloak/values.meet.yaml.gotmpl b/src/helm/env.d/dev-keycloak/values.meet.yaml.gotmpl index 71b5eba0..ef62a542 100644 --- a/src/helm/env.d/dev-keycloak/values.meet.yaml.gotmpl +++ b/src/helm/env.d/dev-keycloak/values.meet.yaml.gotmpl @@ -68,7 +68,7 @@ backend: RECORDING_STORAGE_EVENT_TOKEN: password SUMMARY_SERVICE_ENDPOINT: http://meet-summary:80/api/v1/tasks/ SUMMARY_SERVICE_API_TOKEN: password - SCREEN_RECORDING_BASE_URL: https://meet.127.0.0.1.nip.io/recordings + RECORDING_DOWNLOAD_BASE_URL: https://meet.127.0.0.1.nip.io/recording ROOM_TELEPHONY_ENABLED: True ROOM_TELEPHONY_DEFAULT_COUNTRY: 'FR' ROOM_TELEPHONY_PHONE_NUMBER: '+33901020304' diff --git a/src/helm/env.d/dev/values.meet.yaml.gotmpl b/src/helm/env.d/dev/values.meet.yaml.gotmpl index 720a2f51..0188e861 100644 --- a/src/helm/env.d/dev/values.meet.yaml.gotmpl +++ b/src/helm/env.d/dev/values.meet.yaml.gotmpl @@ -84,7 +84,7 @@ backend: RECORDING_STORAGE_EVENT_TOKEN: password SUMMARY_SERVICE_ENDPOINT: http://meet-summary:80/api/v1/tasks/ SUMMARY_SERVICE_API_TOKEN: password - SCREEN_RECORDING_BASE_URL: https://meet.127.0.0.1.nip.io/recordings + RECORDING_DOWNLOAD_BASE_URL: https://meet.127.0.0.1.nip.io/recording SIGNUP_NEW_USER_TO_MARKETING_EMAIL: True BREVO_API_KEY: secretKeyRef: diff --git a/src/summary/summary/api/route/tasks.py b/src/summary/summary/api/route/tasks.py index 02e9f541..9d6f73d5 100644 --- a/src/summary/summary/api/route/tasks.py +++ b/src/summary/summary/api/route/tasks.py @@ -27,6 +27,7 @@ class TaskCreation(BaseModel): recording_date: Optional[str] recording_time: Optional[str] language: Optional[str] + download_link: Optional[str] @field_validator("language") @classmethod @@ -57,6 +58,7 @@ async def create_task(request: TaskCreation): request.recording_date, request.recording_time, request.language, + request.download_link, ], queue=settings.transcribe_queue, ) diff --git a/src/summary/summary/core/analytics.py b/src/summary/summary/core/analytics.py index e8b8dd55..407f2d39 100644 --- a/src/summary/summary/core/analytics.py +++ b/src/summary/summary/core/analytics.py @@ -118,11 +118,6 @@ class MetadataManager: "retries": 0, } - _required_args_count = 9 - if len(task_args) != _required_args_count: - logger.error("Invalid number of arguments to enable metadata manager.") - return - _, filename, email, _, received_at, *_ = task_args initial_metadata = { diff --git a/src/summary/summary/core/celery_worker.py b/src/summary/summary/core/celery_worker.py index 4888c2ab..013f1b0e 100644 --- a/src/summary/summary/core/celery_worker.py +++ b/src/summary/summary/core/celery_worker.py @@ -120,6 +120,7 @@ def process_audio_transcribe_summarize_v2( recording_date: Optional[str], recording_time: Optional[str], language: Optional[str], + download_link: Optional[str], ): """Process an audio file by transcribing it and generating a summary. @@ -184,6 +185,7 @@ def process_audio_transcribe_summarize_v2( room=room, recording_date=recording_date, recording_time=recording_time, + download_link=download_link, ) data = { diff --git a/src/summary/summary/core/transcript_formatter.py b/src/summary/summary/core/transcript_formatter.py index db123533..6c62acf0 100644 --- a/src/summary/summary/core/transcript_formatter.py +++ b/src/summary/summary/core/transcript_formatter.py @@ -65,6 +65,7 @@ class TranscriptFormatter: room: Optional[str] = None, recording_date: Optional[str] = None, recording_time: Optional[str] = None, + download_link: Optional[str] = None, ) -> Tuple[str, str]: """Format transcription into the final document and its title.""" segments = self._get_segments(transcription) @@ -74,6 +75,7 @@ class TranscriptFormatter: else: content = self._format_speaker(segments) content = self._remove_hallucinations(content) + content = self._add_header(content, download_link) title = self._generate_title(room, recording_date, recording_time) @@ -104,6 +106,19 @@ class TranscriptFormatter: return formatted_output + def _add_header(self, content, download_link: Optional[str]) -> str: + """Add download link header to the document content.""" + if not download_link: + return content + + header = ( + f"\n*Télécharger votre enregistrement " + f"en [suivant ce lien]({download_link})*\n" + ) + content = header + content + + return content + def _generate_title( self, room: Optional[str] = None,