(summary) link transcript to their downloadable recording

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.
This commit is contained in:
lebaudantoine
2026-01-03 01:12:09 +01:00
committed by aleb_the_flash
parent f7b45622bc
commit 39271544d7
10 changed files with 45 additions and 10 deletions

View File

@@ -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

View File

@@ -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 = {

View File

@@ -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(

View File

@@ -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

View File

@@ -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'

View File

@@ -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:

View File

@@ -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,
)

View File

@@ -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 = {

View File

@@ -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 = {

View File

@@ -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,