Compare commits

..

2 Commits

Author SHA1 Message Date
lebaudantoine 12e99f8f2a 💄(frontend) adjust font size based on Marianne recent changes
Marianne is bigger than OpenSans, adjust admin side panel style.
2025-03-24 13:38:40 +01:00
lebaudantoine 3358b23edd 🌐(frontend) add missing translation
The admin side panel was translated too quickly.
I forgot a title, fix it.
2025-03-24 13:38:38 +01:00
28 changed files with 2170 additions and 1599 deletions
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2021-2023 DINUM/Etalab
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+1 -1
View File
@@ -1,4 +1,4 @@
FROM livekit/livekit-server:v1.8.0
FROM livekit/livekit-server:latest
# We inject the nip.io certificate manually because the livekit chart doesn't support volume mounting
COPY rootCA.pem /etc/ssl/certs/
+1 -3
View File
@@ -121,9 +121,7 @@ class OIDCAuthenticationBackend(MozillaOIDCAuthenticationBackend):
contact_data = ContactData(
email=email, attributes={"VISIO_SOURCE": ["SIGNIN"]}
)
marketing_service.create_contact(
contact_data, timeout=settings.BREVO_API_TIMEOUT
)
marketing_service.create_contact(contact_data, timeout=1)
except (ContactCreationError, ImproperlyConfigured, ImportError):
pass
+7 -23
View File
@@ -12,12 +12,7 @@ from django.conf import settings
from django.core.cache import cache
from asgiref.sync import async_to_sync
from livekit.api import ( # pylint: disable=E0611
ListRoomsRequest,
LiveKitAPI,
SendDataRequest,
TwirpError,
)
from livekit.api import LiveKitAPI, SendDataRequest, TwirpError # pylint: disable=E0611
from core import models, utils
@@ -226,12 +221,6 @@ class LobbyService:
color=color,
)
try:
self.notify_participants(room_id=room_id)
except LobbyNotificationError:
# If room not created yet, there is no participants to notify
pass
cache_key = self._get_cache_key(room_id, participant_id)
cache.set(
cache_key,
@@ -239,6 +228,12 @@ class LobbyService:
timeout=settings.LOBBY_WAITING_TIMEOUT,
)
try:
self.notify_participants(room_id=room_id)
except LobbyNotificationError:
# If room not created yet, there is no participants to notify
pass
return participant
def _get_participant(
@@ -348,18 +343,7 @@ class LobbyService:
}
lkapi = LiveKitAPI(**settings.LIVEKIT_CONFIGURATION)
try:
room_response = await lkapi.room.list_rooms(
ListRoomsRequest(
names=[str(room_id)],
)
)
# Check if the room exists
if not room_response.rooms:
return
await lkapi.room.send_data(
SendDataRequest(
room=str(room_id),
@@ -776,43 +776,6 @@ def test_update_participant_status_success(mock_cache, lobby_service, participan
lobby_service._get_cache_key.assert_called_once_with(room.id, participant_id)
@mock.patch("core.services.lobby.LiveKitAPI")
def test_notify_participants_success_no_room(mock_livekit_api, lobby_service):
"""Test the notify_participants method when the LiveKit room doesn't exist yet."""
room = RoomFactory(access_level=RoomAccessLevel.RESTRICTED)
# Set up the mock LiveKitAPI and its behavior
mock_api_instance = mock.Mock()
mock_api_instance.room = mock.Mock()
mock_api_instance.room.send_data = mock.AsyncMock()
# Create a proper response object with an empty rooms list
class MockResponse:
"""LiveKit API response mock with empty rooms list."""
rooms = []
mock_api_instance.room.list_rooms = mock.AsyncMock(return_value=MockResponse())
mock_api_instance.aclose = mock.AsyncMock()
mock_livekit_api.return_value = mock_api_instance
# Act
lobby_service.notify_participants(room.id)
# Assert
# Verify the API was initialized with correct configuration
mock_livekit_api.assert_called_once_with(**settings.LIVEKIT_CONFIGURATION)
# Verify that the service checked for existing rooms
mock_api_instance.room.list_rooms.assert_called_once()
# Verify the send_data method was not called since no room exists
mock_api_instance.room.send_data.assert_not_called()
# Verify the connection was properly closed
mock_api_instance.aclose.assert_called_once()
@mock.patch("core.services.lobby.LiveKitAPI")
def test_notify_participants_success(mock_livekit_api, lobby_service):
"""Test successful participant notification."""
@@ -821,14 +784,6 @@ def test_notify_participants_success(mock_livekit_api, lobby_service):
mock_api_instance = mock.Mock()
mock_api_instance.room = mock.Mock()
mock_api_instance.room.send_data = mock.AsyncMock()
class MockResponse:
"""LiveKit API response mock with non-empty rooms list."""
rooms = ["room-1"]
mock_api_instance.room.list_rooms = mock.AsyncMock(return_value=MockResponse())
mock_api_instance.aclose = mock.AsyncMock()
mock_livekit_api.return_value = mock_api_instance
@@ -838,9 +793,6 @@ def test_notify_participants_success(mock_livekit_api, lobby_service):
# Verify the API was called correctly
mock_livekit_api.assert_called_once_with(**settings.LIVEKIT_CONFIGURATION)
# Verify that the service checked for existing rooms
mock_api_instance.room.list_rooms.assert_called_once()
# Verify the send_data method was called
mock_api_instance.room.send_data.assert_called_once()
send_data_request = mock_api_instance.room.send_data.call_args[0][0]
@@ -865,14 +817,6 @@ def test_notify_participants_error(mock_livekit_api, lobby_service):
mock_api_instance.room.send_data = mock.AsyncMock(
side_effect=TwirpError(msg="test error", code=123)
)
class MockResponse:
"""LiveKit API response mock with non-empty rooms list."""
rooms = ["room-1"]
mock_api_instance.room.list_rooms = mock.AsyncMock(return_value=MockResponse())
mock_api_instance.aclose = mock.AsyncMock()
mock_livekit_api.return_value = mock_api_instance
@@ -885,9 +829,6 @@ def test_notify_participants_error(mock_livekit_api, lobby_service):
# Verify the API was called correctly
mock_livekit_api.assert_called_once_with(**settings.LIVEKIT_CONFIGURATION)
# Verify that the service checked for existing rooms
mock_api_instance.room.list_rooms.assert_called_once()
# Verify send_data was called
mock_api_instance.room.send_data.assert_called_once()
-7
View File
@@ -19,7 +19,6 @@ from django.utils.translation import gettext_lazy as _
import sentry_sdk
from configurations import Configuration, values
from sentry_sdk.integrations.django import DjangoIntegration
from sentry_sdk.integrations.logging import ignore_logger
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@@ -498,9 +497,6 @@ class Base(Configuration):
converter=lambda x: int(x), # pylint: disable=unnecessary-lambda
)
BREVO_API_CONTACT_ATTRIBUTES = values.DictValue({"VISIO_USER": True})
BREVO_API_TIMEOUT = values.PositiveIntegerValue(
1, environ_name="BREVO_API_TIMEOUT", environ_prefix=None
)
# Lobby configurations
LOBBY_KEY_PREFIX = values.Value(
@@ -579,9 +575,6 @@ class Base(Configuration):
scope = sentry_sdk.get_global_scope()
scope.set_tag("application", "backend")
# Ignore the logs added by the DockerflowMiddleware
ignore_logger("request.summary")
class Build(Base):
"""Settings used when the application is built.
+11 -11
View File
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "meet"
version = "0.1.16"
version = "0.1.15"
authors = [{ "name" = "DINUM", "email" = "dev@mail.numerique.gouv.fr" }]
classifiers = [
"Development Status :: 5 - Production/Stable",
@@ -25,7 +25,7 @@ license = { file = "LICENSE" }
readme = "README.md"
requires-python = ">=3.10"
dependencies = [
"boto3==1.37.18",
"boto3==1.37.4",
"Brotli==1.1.0",
"brevo-python==1.1.2",
"celery[redis]==5.4.0",
@@ -48,16 +48,16 @@ dependencies = [
"june-analytics-python==2.3.0",
"markdown==3.7",
"nested-multipart-parser==1.5.0",
"psycopg[binary]==3.2.6",
"psycopg[binary]==3.2.5",
"PyJWT==2.10.1",
"python-frontmatter==1.1.0",
"requests==2.32.3",
"sentry-sdk==2.24.1",
"sentry-sdk==2.22.0",
"url-normalize==1.4.3",
"whitenoise==6.9.0",
"mozilla-django-oidc==4.0.1",
"livekit-api==0.8.2",
"aiohttp==3.11.14",
"aiohttp==3.11.13",
]
[project.urls]
@@ -72,18 +72,18 @@ dev = [
"drf-spectacular-sidecar==2025.3.1",
"freezegun==1.5.1",
"ipdb==0.13.13",
"ipython==9.0.2",
"pyfakefs==5.8.0",
"ipython==9.0.1",
"pyfakefs==5.7.4",
"pylint-django==2.6.1",
"pylint==3.3.6",
"pylint==3.3.4",
"pytest-cov==6.0.0",
"pytest-django==4.10.0",
"pytest==8.3.5",
"pytest-icdiff==0.9",
"pytest-xdist==3.6.1",
"responses==0.25.7",
"ruff==0.11.2",
"types-requests==2.32.0.20250306",
"responses==0.25.6",
"ruff==0.9.9",
"types-requests==2.32.0.20250301",
]
[tool.setuptools]
+2069 -1274
View File
File diff suppressed because it is too large Load Diff
+17 -17
View File
@@ -1,7 +1,7 @@
{
"name": "meet",
"private": true,
"version": "0.1.16",
"version": "0.1.15",
"type": "module",
"scripts": {
"dev": "panda codegen && vite",
@@ -16,45 +16,45 @@
"@livekit/components-react": "2.8.1",
"@livekit/components-styles": "1.1.4",
"@livekit/track-processors": "0.3.3",
"@pandacss/preset-panda": "0.53.3",
"@react-aria/toast": "3.0.1",
"@pandacss/preset-panda": "0.53.0",
"@react-aria/toast": "3.0.0-beta.19",
"@remixicon/react": "4.6.0",
"@tanstack/react-query": "5.69.0",
"@tanstack/react-query": "5.67.1",
"crisp-sdk-web": "1.0.25",
"hoofd": "1.7.3",
"i18next": "24.2.3",
"i18next": "24.2.2",
"i18next-browser-languagedetector": "8.0.4",
"i18next-parser": "9.3.0",
"i18next-resources-to-backend": "1.2.1",
"livekit-client": "2.9.8",
"posthog-js": "1.232.6",
"livekit-client": "2.9.5",
"posthog-js": "1.225.1",
"react": "18.3.1",
"react-aria-components": "1.7.1",
"react-aria-components": "1.6.0",
"react-dom": "18.3.1",
"react-i18next": "15.1.1",
"use-sound": "5.0.0",
"valtio": "2.1.4",
"valtio": "2.1.3",
"wouter": "3.6.0"
},
"devDependencies": {
"@pandacss/dev": "0.53.3",
"@tanstack/eslint-plugin-query": "5.68.0",
"@tanstack/react-query-devtools": "5.69.0",
"@types/node": "22.13.13",
"@pandacss/dev": "0.53.0",
"@tanstack/eslint-plugin-query": "5.66.1",
"@tanstack/react-query-devtools": "5.67.1",
"@types/node": "22.13.9",
"@types/react": "18.3.12",
"@types/react-dom": "18.3.1",
"@typescript-eslint/eslint-plugin": "8.28.0",
"@typescript-eslint/parser": "8.28.0",
"@typescript-eslint/eslint-plugin": "8.26.0",
"@typescript-eslint/parser": "8.26.0",
"@vitejs/plugin-react": "4.3.4",
"eslint": "8.57.0",
"eslint-config-prettier": "10.1.1",
"eslint-config-prettier": "10.0.2",
"eslint-plugin-jsx-a11y": "6.10.2",
"eslint-plugin-react-hooks": "5.2.0",
"eslint-plugin-react-refresh": "0.4.19",
"postcss": "8.5.3",
"prettier": "3.5.3",
"typescript": "5.8.2",
"vite": "6.2.3",
"vite": "6.2.0",
"vite-tsconfig-paths": "5.1.4"
}
}
+34 -1
View File
@@ -35,7 +35,40 @@ const config: Config = {
exclude: [],
jsxFramework: 'react',
outdir: 'src/styled-system',
globalFontface: {},
globalFontface: {
Marianne: [
{
src: 'url(/fonts/Marianne-Regular-subset.woff2) format("woff2")',
fontWeight: 400,
fontStyle: 'normal',
fontDisplay: 'swap',
},
{
path: 'url(/fonts/Marianne-Regular_Italic-subset.woff2) format("woff2")',
fontWeight: 400,
fontStyle: 'italic',
fontDisplay: 'swap',
},
{
path: 'url(/fonts/Marianne-Medium-subset.woff2) format("woff2")',
fontWeight: 500,
fontStyle: 'normal',
fontDisplay: 'swap',
},
{
path: 'url(/fonts/Marianne-Bold-subset.woff2) format("woff2")',
fontWeight: 700,
fontStyle: 'normal',
fontDisplay: 'swap',
},
{
path: 'url(/fonts/Marianne-ExtraBold-subset.woff2) format("woff2")',
fontWeight: 800,
fontStyle: 'normal',
fontDisplay: 'swap',
},
],
},
theme: {
...pandaPreset.theme,
// media queries are defined in em so that zooming with text-only mode triggers breakpoints
@@ -48,7 +48,7 @@ export const useUser = (
const isLoggedOut = isLoggedIn === false
return {
refetch: query.refetch,
...query,
user: isLoggedOut ? undefined : (query.data as ApiUser | undefined),
isLoggedIn,
logout,
@@ -18,9 +18,9 @@ const Heading = styled('h2', {
width: 'fit-content',
marginBottom: 0,
fontSize: '1.3rem',
fontWeight: '700',
fontWeight: '600',
marginTop: '0.75rem',
lineHeight: '1.7rem',
lineHeight: '2rem',
maxWidth: '23rem',
textAlign: 'center',
textWrap: 'balance',
@@ -120,7 +120,7 @@ const Separator = styled('div', {
const Heading = styled('h1', {
base: {
fontWeight: '700',
fontWeight: '600',
fontStyle: 'normal',
fontStretch: 'normal',
fontOpticalSizing: 'auto',
@@ -298,7 +298,7 @@ export const Join = ({
case ApiLobbyStatus.TIMEOUT:
return (
<VStack alignItems="center" textAlign="center">
<H lvl={1} margin={false} centered>
<H lvl={1} margin={false}>
{t('timeoutInvite.title')}
</H>
<Text as="p" variant="note">
@@ -310,7 +310,7 @@ export const Join = ({
case ApiLobbyStatus.DENIED:
return (
<VStack alignItems="center" textAlign="center">
<H lvl={1} margin={false} centered>
<H lvl={1} margin={false}>
{t('denied.title')}
</H>
<Text as="p" variant="note">
@@ -322,7 +322,7 @@ export const Join = ({
case ApiLobbyStatus.WAITING:
return (
<VStack alignItems="center" textAlign="center">
<H lvl={1} margin={false} centered>
<H lvl={1} margin={false}>
{t('waiting.title')}
</H>
<Text
@@ -346,7 +346,7 @@ export const Join = ({
}}
>
<VStack marginBottom={1}>
<H lvl={1} margin="sm" centered>
<H lvl={1} margin="sm">
{t('heading')}
</H>
<Field
@@ -75,7 +75,7 @@ export function FloatingReaction({
>
<span
className={css({
lineHeight: '57px',
lineHeight: '45px',
})}
>
{emoji}
@@ -89,10 +89,9 @@ export function FloatingReaction({
textAlign: 'center',
borderRadius: '20px',
paddingX: '0.5rem',
paddingBottom: '0.3125rem',
paddingTop: '0.15rem',
paddingY: '0.15rem',
boxShadow: '0 2px 4px rgba(0, 0, 0, 0.1)',
lineHeight: '16px',
lineHeight: '14px',
})}
>
{name}
@@ -13,7 +13,6 @@ export const Effects = () => {
<div
className={css({
padding: '0 1.5rem',
overflowY: 'scroll',
})}
>
<EffectsConfiguration
@@ -58,14 +58,14 @@ export class SdkReverseClient {
* To be used in SDK scope.
*/
export function useEnsureAuth() {
const { isLoggedIn, refetch } = useUser({
const { isLoggedIn, ...other } = useUser({
fetchUserOptions: { attemptSilent: false },
})
const startSSO = () => {
return new Promise<void>((resolve) => {
SdkReverseClient.waitForAuthenticationAck().then(async () => {
await refetch()
await other.refetch()
resolve()
})
const params = `scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no,
+1 -1
View File
@@ -106,7 +106,7 @@ const Marianne = () => {
className={css({
letterSpacing: '-.01em',
textTransform: 'uppercase',
fontWeight: '700',
fontWeight: '600',
fontFamily: 'Marianne',
fontSize: '1.25rem',
lineHeight: '1.75rem',
+1 -1
View File
@@ -44,7 +44,7 @@ const Marianne = () => {
className={css({
letterSpacing: '-.01em',
textTransform: 'uppercase',
fontWeight: '700',
fontWeight: '600',
fontFamily: 'Marianne',
fontSize: '0.7875rem !important',
})}
+1 -1
View File
@@ -44,7 +44,7 @@ const FieldWrapper = styled('div', {
const StyledLabel = styled(Label, {
base: {
display: 'block',
fontSize: '0.875rem',
fontSize: '0.75rem',
},
variants: {
center: {
-40
View File
@@ -30,46 +30,6 @@
font-display: swap;
}
@font-face {
font-family: 'Marianne';
src: url('/fonts/Marianne-Regular-subset.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Marianne';
src: url('/fonts/Marianne-Regular_Italic-subset.woff2') format('woff2');
font-weight: 400;
font-style: italic;
font-display: swap;
}
@font-face {
font-family: 'Marianne';
src: url('/fonts/Marianne-Medium-subset.woff2') format('woff2');
font-weight: 500;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Marianne';
src: url('/fonts/Marianne-Bold-subset.woff2') format('woff2');
font-weight: 700;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Marianne';
src: url('/fonts/Marianne-ExtraBold-subset.woff2') format('woff2');
font-weight: 800;
font-style: normal;
font-display: swap;
}
/*
* to reduce CLS
* values taken from https://github.com/khempenius/font-fallbacks-dataset/blob/main/font-metric-overrides.csv#L2979
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "mail_mjml",
"version": "0.1.16",
"version": "0.1.15",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "mail_mjml",
"version": "0.1.16",
"version": "0.1.15",
"license": "MIT",
"dependencies": {
"@html-to/text-cli": "0.5.4",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "mail_mjml",
"version": "0.1.16",
"version": "0.1.15",
"description": "An util to generate html and text django's templates from mjml templates",
"type": "module",
"dependencies": {
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "sdk",
"version": "0.1.16",
"version": "0.1.15",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "sdk",
"version": "0.1.16",
"version": "0.1.15",
"license": "ISC",
"workspaces": [
"./library",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "sdk",
"version": "0.1.16",
"version": "0.1.15",
"author": "",
"license": "ISC",
"description": "",
+4 -4
View File
@@ -1,7 +1,7 @@
[project]
name = "summary"
version = "0.1.16"
version = "0.1.15"
dependencies = [
"fastapi[standard]>=0.105.0",
"uvicorn>=0.24.0",
@@ -10,14 +10,14 @@ dependencies = [
"celery==5.4.0",
"redis==5.2.1",
"minio==7.2.15",
"openai==1.68.2",
"openai==1.65.2",
"requests==2.32.3",
"sentry-sdk[fastapi, celery]==2.24.1",
"sentry-sdk[fastapi, celery]==2.22.0",
]
[project.optional-dependencies]
dev = [
"ruff==0.11.2",
"ruff==0.9.9",
]
[build-system]
+4 -16
View File
@@ -1,15 +1,10 @@
"""API routes related to application tasks."""
from typing import Optional
from celery.result import AsyncResult
from fastapi import APIRouter
from pydantic import BaseModel
from summary.core.celery_worker import (
process_audio_transcribe_summarize,
process_audio_transcribe_summarize_v2,
)
from summary.core.celery_worker import process_audio_transcribe_summarize
class TaskCreation(BaseModel):
@@ -18,7 +13,6 @@ class TaskCreation(BaseModel):
filename: str
email: str
sub: str
version: Optional[int] = 2
router = APIRouter(prefix="/tasks")
@@ -27,15 +21,9 @@ router = APIRouter(prefix="/tasks")
@router.post("/")
async def create_task(request: TaskCreation):
"""Create a task."""
if request.version == 1:
task = process_audio_transcribe_summarize.delay(
request.filename, request.email, request.sub
)
else:
task = process_audio_transcribe_summarize_v2.delay(
request.filename, request.email, request.sub
)
task = process_audio_transcribe_summarize.delay(
request.filename, request.email, request.sub
)
return {"id": task.id, "message": "Task created"}
-98
View File
@@ -56,35 +56,6 @@ def create_retry_session():
return session
def format_segments(transcription_data):
"""Format transcription segments from WhisperX into a readable conversation format.
Processes transcription data with segments containing speaker information and text,
combining consecutive segments from the same speaker and formatting them as a
conversation with speaker labels.
"""
formatted_output = ""
if not transcription_data or not hasattr(transcription_data, 'segments'):
if isinstance(transcription_data, dict) and 'segments' in transcription_data:
segments = transcription_data['segments']
else:
return "Error: Invalid transcription data format"
else:
segments = transcription_data.segments
previous_speaker = None
for segment in segments:
speaker = segment.get('speaker', 'UNKNOWN_SPEAKER')
text = segment.get('text', '')
if text:
if speaker != previous_speaker:
formatted_output += f"\n\n *{speaker}*: {text}"
else:
formatted_output += f" {text}"
previous_speaker = speaker
return formatted_output
def post_with_retries(url, data):
"""Send POST request with automatic retries."""
session = create_retry_session()
@@ -169,72 +140,3 @@ def process_audio_transcribe_summarize(filename: str, email: str, sub: str):
logger.info("Webhook submitted successfully. Status: %s", response.status_code)
logger.debug("Response body: %s", response.text)
@celery.task(max_retries=settings.celery_max_retries)
def process_audio_transcribe_summarize_v2(filename: str, email: str, sub: str):
"""Process an audio file by transcribing it and generating a summary.
This Celery task performs the following operations:
1. Retrieves the audio file from MinIO storage
2. Transcribes the audio using WhisperX model
3. Sends the results via webhook
"""
logger.info("Notification received")
logger.debug("filename: %s", filename)
minio_client = Minio(
settings.aws_s3_endpoint_url,
access_key=settings.aws_s3_access_key_id,
secret_key=settings.aws_s3_secret_access_key,
secure=settings.aws_s3_secure_access,
)
logger.debug("Connection to the Minio bucket successful")
audio_file_stream = minio_client.get_object(
settings.aws_storage_bucket_name, object_name=filename
)
temp_file_path = save_audio_stream(audio_file_stream)
logger.debug("Recording successfully downloaded, filepath: %s", temp_file_path)
logger.debug("Initiating OpenAI client")
openai_client = openai.OpenAI(
api_key=settings.openai_api_key, base_url=settings.openai_base_url
)
try:
logger.debug("Querying transcription …")
with open(temp_file_path, "rb") as audio_file:
transcription = openai_client.audio.transcriptions.create(
model=settings.openai_asr_model, file=audio_file
)
logger.debug("Transcription: \n %s", transcription)
finally:
if os.path.exists(temp_file_path):
os.remove(temp_file_path)
logger.debug("Temporary file removed: %s", temp_file_path)
formatted_transcription = format_segments(transcription)
data = {
"title": "Transcription",
"content": formatted_transcription,
"email": email,
"sub": sub,
}
logger.debug("Submitting webhook to %s", settings.webhook_url)
logger.debug("Request payload: %s", json.dumps(data, indent=2))
response = post_with_retries(settings.webhook_url, data)
logger.info("Webhook submitted successfully. Status: %s", response.status_code)
logger.debug("Response body: %s", response.text)
# TODO - integrate summarize the transcript and create a new document.