mirror of
https://github.com/suitenumerique/meet.git
synced 2026-07-26 20:08:24 +00:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 85def1535e | |||
| 5ba1885411 | |||
| 273af221e6 | |||
| e8d6aba306 | |||
| 449503208a | |||
| 7eb3553bea | |||
| f0356af365 | |||
| 9921ef9d09 | |||
| 68e999a037 | |||
| 34103bf326 | |||
| e273ac9e43 | |||
| adcdbc0695 | |||
| 8f9008f1e0 | |||
| 9320a1af0c | |||
| e3827970c8 |
@@ -8,12 +8,27 @@ and this project adheres to
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## Fixed
|
||||
|
||||
- 🐛(transcription) fix silent bug in speaker assignment
|
||||
|
||||
### Changed
|
||||
|
||||
- ⬆️(frontend) upgrade @mediapipe/tasks-vision from 0.10.14 to 0.10.35
|
||||
- ⬆️(frontend) upgrade i18next from 26.3.1 to 26.3.2
|
||||
- ⬆️(frontend) upgrade posthog-js from 1.391.2 to 1.395.0
|
||||
- ⬆️(frontend) upgrade @tanstack/react-query from 5.101.0 to 5.101.1
|
||||
- ⬆️(frontend) upgrade livekit-client from 2.19.2 to 2.20.0
|
||||
|
||||
## [1.24.0] - 2026-07-21
|
||||
|
||||
### Added
|
||||
|
||||
- ✨(backend) allow searching the recording admin table by owner email
|
||||
- ✨(frontend) add participant color gradient when camera is off #1490
|
||||
- ✨(all) allow forcing SSO display name for authenticated users
|
||||
- ➕(frontend) install vite-plugin-static-copy for MediaPipe WASM assets
|
||||
- ✨(addon) show add-in tools when creating meetings in shared calendars
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -33,6 +48,7 @@ and this project adheres to
|
||||
- 🩹(backend) identify externally provisioned users to PostHog
|
||||
- 🐛(backend) fix info panel crash for unregistered rooms
|
||||
- ♿️(frontend) focus side panel container on open #1452
|
||||
- 🐛(summary) whisper call error handling
|
||||
|
||||
## [1.23.0] - 2026-07-08
|
||||
|
||||
|
||||
@@ -164,6 +164,7 @@ echo " - src/backend/pyproject.toml"
|
||||
echo " - src/backend/uv.lock"
|
||||
echo " - src/summary/pyproject.toml"
|
||||
echo " - src/agents/pyproject.toml"
|
||||
echo " - src/agents/uv.lock"
|
||||
echo " - CHANGELOG.md"
|
||||
echo ""
|
||||
print_warning "Next steps:"
|
||||
|
||||
+4
-4
@@ -11,14 +11,14 @@ There are two ways to customize LaSuite Meet:
|
||||
|
||||
### How to Use
|
||||
|
||||
To use this feature, simply set the `FRONTEND_CSS_URL` environment variable to the URL of your custom CSS file. For example:
|
||||
To use this feature, simply set the `FRONTEND_CUSTOM_CSS_URL` environment variable (of the **backend** service) to the URL of your custom CSS file. For example:
|
||||
|
||||
```javascript
|
||||
FRONTEND_CSS_URL=https://example.com/custom-style.css
|
||||
FRONTEND_CUSTOM_CSS_URL=https://example.com/custom-style.css
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> If you serve your CSS file on the same domain as LaSuite Meet, paths are supported, i.e. `FRONTEND_CSS_URL=/custom/style.css` will load `https://your-domain.com/custom/style.css`.
|
||||
> If you serve your CSS file on the same domain as LaSuite Meet, paths are supported, i.e. `FRONTEND_CUSTOM_CSS_URL=/custom/style.css` will load `https://your-domain.com/custom/style.css`.
|
||||
|
||||
Setting this variable makes the app load your CSS at runtime, adding a `<link>` to `<head>` so you can override CSS variables and customize the frontend without rebuilding.
|
||||
|
||||
@@ -37,7 +37,7 @@ Let's say you want to change the font of our application to a custom font. You c
|
||||
}
|
||||
```
|
||||
|
||||
Then, set the `FRONTEND_CSS_URL` environment variable to the URL of your custom CSS file. Once you've done this, our application will load your custom CSS file and apply the styles, changing the default font to the one you specified.
|
||||
Then, set the `FRONTEND_CUSTOM_CSS_URL` environment variable to the URL of your custom CSS file. Once you've done this, our application will load your custom CSS file and apply the styles, changing the default font to the one you specified.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> You can override any CSS token—semantic or palette. See [panda.config.ts](../src/frontend/panda.config.ts) for all defined semantic tokens.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<OfficeApp xmlns="http://schemas.microsoft.com/office/appforoffice/1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bt="http://schemas.microsoft.com/office/officeappbasictypes/1.0" xmlns:mailappor="http://schemas.microsoft.com/office/mailappversionoverrides/1.0" xsi:type="MailApp">
|
||||
<Id>a025f0f6-757a-4790-97f3-99c66c4a5795</Id>
|
||||
<Version>0.0.2.0</Version>
|
||||
<Version>1.0.0.0</Version>
|
||||
<ProviderName>__APP_NAME__</ProviderName>
|
||||
<DefaultLocale>fr-FR</DefaultLocale>
|
||||
<DisplayName DefaultValue="__APP_NAME__"/>
|
||||
@@ -205,5 +205,174 @@
|
||||
</bt:String>
|
||||
</bt:LongStrings>
|
||||
</Resources>
|
||||
|
||||
<!-- ─── V1.1 override: required for shared folder / delegate support ─── -->
|
||||
<VersionOverrides xmlns="http://schemas.microsoft.com/office/mailappversionoverrides/1.1" xsi:type="VersionOverridesV1_1">
|
||||
<Requirements>
|
||||
<bt:Sets DefaultMinVersion="1.8">
|
||||
<bt:Set Name="Mailbox"/>
|
||||
</bt:Sets>
|
||||
</Requirements>
|
||||
<Hosts>
|
||||
<Host xsi:type="MailHost">
|
||||
<DesktopFormFactor>
|
||||
<FunctionFile resid="Commands.Url"/>
|
||||
<SupportsSharedFolders>true</SupportsSharedFolders>
|
||||
|
||||
<!-- ─── Mail: Read ─────────────────────────────────────────── -->
|
||||
<ExtensionPoint xsi:type="MessageReadCommandSurface">
|
||||
<OfficeTab id="TabDefault">
|
||||
<Group id="msgReadGroup">
|
||||
<Label resid="GroupLabel"/>
|
||||
<Control xsi:type="Button" id="msgReadOpenPaneButton">
|
||||
<Label resid="TaskpaneButton.Label"/>
|
||||
<Supertip>
|
||||
<Title resid="TaskpaneButton.Label"/>
|
||||
<Description resid="TaskpaneButton.Tooltip"/>
|
||||
</Supertip>
|
||||
<Icon>
|
||||
<bt:Image size="16" resid="Icon.16x16"/>
|
||||
<bt:Image size="32" resid="Icon.32x32"/>
|
||||
<bt:Image size="80" resid="Icon.80x80"/>
|
||||
</Icon>
|
||||
<Action xsi:type="ShowTaskpane">
|
||||
<SourceLocation resid="Taskpane.Url"/>
|
||||
</Action>
|
||||
</Control>
|
||||
</Group>
|
||||
</OfficeTab>
|
||||
</ExtensionPoint>
|
||||
|
||||
<!-- ─── Mail: Compose ─────────────────────────────────────── -->
|
||||
<ExtensionPoint xsi:type="MessageComposeCommandSurface">
|
||||
<OfficeTab id="TabDefault">
|
||||
<Group id="msgComposeGroup">
|
||||
<Label resid="GroupLabel"/>
|
||||
<Control xsi:type="Button" id="msgComposeGenerateLinkButton">
|
||||
<Label resid="GenerateLink.Label"/>
|
||||
<Supertip>
|
||||
<Title resid="GenerateLink.Label"/>
|
||||
<Description resid="GenerateLink.Tooltip"/>
|
||||
</Supertip>
|
||||
<Icon>
|
||||
<bt:Image size="16" resid="Icon.16x16"/>
|
||||
<bt:Image size="32" resid="Icon.32x32"/>
|
||||
<bt:Image size="80" resid="Icon.80x80"/>
|
||||
</Icon>
|
||||
<Action xsi:type="ExecuteFunction">
|
||||
<FunctionName>generateMeetingLinkFromMail</FunctionName>
|
||||
</Action>
|
||||
</Control>
|
||||
<Control xsi:type="Button" id="msgComposeOpenPaneButton">
|
||||
<Label resid="TaskpaneButton.Label"/>
|
||||
<Supertip>
|
||||
<Title resid="TaskpaneButton.Label"/>
|
||||
<Description resid="TaskpaneButton.Tooltip"/>
|
||||
</Supertip>
|
||||
<Icon>
|
||||
<bt:Image size="16" resid="Settings.16x16"/>
|
||||
<bt:Image size="32" resid="Settings.32x32"/>
|
||||
<bt:Image size="80" resid="Settings.80x80"/>
|
||||
</Icon>
|
||||
<Action xsi:type="ShowTaskpane">
|
||||
<SourceLocation resid="Taskpane.Url"/>
|
||||
</Action>
|
||||
</Control>
|
||||
</Group>
|
||||
</OfficeTab>
|
||||
</ExtensionPoint>
|
||||
|
||||
<!-- ─── Calendar: Compose (New/Edit appointment) ──────────── -->
|
||||
<ExtensionPoint xsi:type="AppointmentOrganizerCommandSurface">
|
||||
<OfficeTab id="TabDefault">
|
||||
<Group id="apptComposeGroup">
|
||||
<Label resid="GroupLabel"/>
|
||||
<Control xsi:type="Button" id="apptGenerateLinkButton">
|
||||
<Label resid="GenerateLink.Label"/>
|
||||
<Supertip>
|
||||
<Title resid="GenerateLink.Label"/>
|
||||
<Description resid="GenerateLink.Tooltip"/>
|
||||
</Supertip>
|
||||
<Icon>
|
||||
<bt:Image size="16" resid="Icon.16x16"/>
|
||||
<bt:Image size="32" resid="Icon.32x32"/>
|
||||
<bt:Image size="80" resid="Icon.80x80"/>
|
||||
</Icon>
|
||||
<Action xsi:type="ExecuteFunction">
|
||||
<FunctionName>generateMeetingLinkFromCalendar</FunctionName>
|
||||
</Action>
|
||||
</Control>
|
||||
|
||||
<Control xsi:type="Button" id="apptOpenSettingsButton">
|
||||
<Label resid="OpenSettings.Label"/>
|
||||
<Supertip>
|
||||
<Title resid="OpenSettings.Label"/>
|
||||
<Description resid="OpenSettings.Tooltip"/>
|
||||
</Supertip>
|
||||
<Icon>
|
||||
<bt:Image size="16" resid="Settings.16x16"/>
|
||||
<bt:Image size="32" resid="Settings.32x32"/>
|
||||
<bt:Image size="80" resid="Settings.80x80"/>
|
||||
</Icon>
|
||||
<Action xsi:type="ShowTaskpane">
|
||||
<SourceLocation resid="Taskpane.Url"/>
|
||||
</Action>
|
||||
</Control>
|
||||
|
||||
</Group>
|
||||
</OfficeTab>
|
||||
</ExtensionPoint>
|
||||
|
||||
</DesktopFormFactor>
|
||||
</Host>
|
||||
</Hosts>
|
||||
<Resources>
|
||||
<bt:Images>
|
||||
<bt:Image id="Settings.16x16" DefaultValue="https://localhost:3000/addons/outlook/assets/settings-16.png"/>
|
||||
<bt:Image id="Settings.32x32" DefaultValue="https://localhost:3000/addons/outlook/assets/settings-32.png"/>
|
||||
<bt:Image id="Settings.80x80" DefaultValue="https://localhost:3000/addons/outlook/assets/settings-80.png"/>
|
||||
<bt:Image id="Add.16x16" DefaultValue="https://localhost:3000/addons/outlook/assets/add-16.png"/>
|
||||
<bt:Image id="Add.32x32" DefaultValue="https://localhost:3000/addons/outlook/assets/add-32.png"/>
|
||||
<bt:Image id="Add.80x80" DefaultValue="https://localhost:3000/addons/outlook/assets/add-80.png"/>
|
||||
<bt:Image id="Icon.16x16" DefaultValue="https://localhost:3000/addons/outlook/assets/icon-16.png"/>
|
||||
<bt:Image id="Icon.32x32" DefaultValue="https://localhost:3000/addons/outlook/assets/icon-32.png"/>
|
||||
<bt:Image id="Icon.80x80" DefaultValue="https://localhost:3000/addons/outlook/assets/icon-80.png"/>
|
||||
</bt:Images>
|
||||
<bt:Urls>
|
||||
<bt:Url id="Commands.Url" DefaultValue="https://localhost:3000/addons/outlook/commands.html"/>
|
||||
<bt:Url id="Taskpane.Url" DefaultValue="https://localhost:3000/addons/outlook/taskpane.html"/>
|
||||
</bt:Urls>
|
||||
<bt:ShortStrings>
|
||||
<!-- Default (French) -->
|
||||
<bt:String id="GroupLabel" DefaultValue="__APP_NAME__"/>
|
||||
<bt:String id="GenerateLink.Label" DefaultValue="Ajouter un lien __APP_NAME__">
|
||||
<bt:Override Locale="en-US" Value="Add a __APP_NAME__ link"/>
|
||||
<bt:Override Locale="de-DE" Value="__APP_NAME__-Link hinzufügen"/>
|
||||
</bt:String>
|
||||
<bt:String id="TaskpaneButton.Label" DefaultValue="Ouvrir les paramètres">
|
||||
<bt:Override Locale="en-US" Value="Open settings"/>
|
||||
<bt:Override Locale="de-DE" Value="Einstellungen öffnen"/>
|
||||
</bt:String>
|
||||
<bt:String id="OpenSettings.Label" DefaultValue="Paramètres">
|
||||
<bt:Override Locale="en-US" Value="Settings"/>
|
||||
<bt:Override Locale="de-DE" Value="Einstellungen"/>
|
||||
</bt:String>
|
||||
</bt:ShortStrings>
|
||||
<bt:LongStrings>
|
||||
<bt:String id="GenerateLink.Tooltip" DefaultValue="Génère un lien de réunion __APP_NAME__ et l'insère dans l'événement.">
|
||||
<bt:Override Locale="de-DE" Value="Generiert einen __APP_NAME__-Besprechungslink und fügt ihn in den Termin ein."/>
|
||||
<bt:Override Locale="en-US" Value="Generates a __APP_NAME__ meeting link and inserts it into the item."/>
|
||||
</bt:String>
|
||||
<bt:String id="TaskpaneButton.Tooltip" DefaultValue="Ouvre les paramètres de connexion __APP_NAME__.">
|
||||
<bt:Override Locale="de-DE" Value="Öffnet die __APP_NAME__-Verbindungseinstellungen."/>
|
||||
<bt:Override Locale="en-US" Value="Opens the __APP_NAME__ connection settings."/>
|
||||
</bt:String>
|
||||
<bt:String id="OpenSettings.Tooltip" DefaultValue="Ouvre les paramètres de connexion __APP_NAME__.">
|
||||
<bt:Override Locale="de-DE" Value="Öffnet die __APP_NAME__-Verbindungseinstellungen."/>
|
||||
<bt:Override Locale="en-US" Value="Opens the __APP_NAME__ connection settings."/>
|
||||
</bt:String>
|
||||
</bt:LongStrings>
|
||||
</Resources>
|
||||
</VersionOverrides>
|
||||
</VersionOverrides>
|
||||
</OfficeApp>
|
||||
|
||||
Generated
+4
-4
@@ -10,7 +10,7 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"core-js": "3.49.0",
|
||||
"i18next": "26.3.1",
|
||||
"i18next": "^26.3.2",
|
||||
"i18next-browser-languagedetector": "8.2.1",
|
||||
"regenerator-runtime": "0.14.1"
|
||||
},
|
||||
@@ -9364,9 +9364,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/i18next": {
|
||||
"version": "26.3.1",
|
||||
"resolved": "https://registry.npmjs.org/i18next/-/i18next-26.3.1.tgz",
|
||||
"integrity": "sha512-txQqd5EULsqEh9OJqRH15aCaOuy/nLJyhw5EHCSKLKJE1aBbb3Zve2+uQIxgWhPm1QqUQoWyQBm2kfmmIrzkcQ==",
|
||||
"version": "26.3.2",
|
||||
"resolved": "https://registry.npmjs.org/i18next/-/i18next-26.3.2.tgz",
|
||||
"integrity": "sha512-QQkXAM1sPDHqhxMQuBeHVMUn6mJchF+wdpOoQerciLAFqO3ZYdxO0EUbeEhruyutnNwpUQIITDVzLjwnNL0T1w==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"core-js": "3.49.0",
|
||||
"i18next": "26.3.1",
|
||||
"i18next": "26.3.2",
|
||||
"i18next-browser-languagedetector": "8.2.1",
|
||||
"regenerator-runtime": "0.14.1"
|
||||
},
|
||||
|
||||
@@ -57,8 +57,7 @@
|
||||
data-i18n="footer.feedback"
|
||||
></a>
|
||||
<div id="footer-right">
|
||||
<span class="version-badge">beta</span>
|
||||
<span class="version-number">0.0.2</span>
|
||||
<span class="version-number">1.0.0</span>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
[project]
|
||||
name = "agents"
|
||||
version = "1.23.0"
|
||||
version = "1.24.0"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = [
|
||||
"livekit-agents==1.6.4",
|
||||
|
||||
Generated
+1
-1
@@ -9,7 +9,7 @@ resolution-markers = [
|
||||
|
||||
[[package]]
|
||||
name = "agents"
|
||||
version = "1.23.0"
|
||||
version = "1.24.0"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "livekit-agents" },
|
||||
|
||||
+27
-40
@@ -423,6 +423,31 @@ def detect_mimetype(file_buffer: bytes, filename: str | None = None) -> str:
|
||||
return mimetype_from_content or "application/octet-stream"
|
||||
|
||||
|
||||
def _get_s3_client(*, override_domain: bool = True):
|
||||
"""Return an S3 client, honoring the AWS_S3_DOMAIN_REPLACE endpoint override.
|
||||
|
||||
AWS_S3_DOMAIN_REPLACE is used when the backend and frontend reach object
|
||||
storage under different domains (this is the case in the docker compose stack
|
||||
used in development: the frontend connects to the object storage on localhost
|
||||
while the backend uses the object storage service name declared in the stack).
|
||||
The domain name is used to compute the signature, so it can't be changed
|
||||
dynamically by the frontend; we build a dedicated boto3 client pointed at that
|
||||
endpoint. Otherwise we reuse the default storage client.
|
||||
"""
|
||||
if settings.AWS_S3_DOMAIN_REPLACE and override_domain:
|
||||
return boto3.client(
|
||||
"s3",
|
||||
aws_access_key_id=settings.AWS_S3_ACCESS_KEY_ID,
|
||||
aws_secret_access_key=settings.AWS_S3_SECRET_ACCESS_KEY,
|
||||
endpoint_url=settings.AWS_S3_DOMAIN_REPLACE,
|
||||
config=botocore.client.Config(
|
||||
region_name=settings.AWS_S3_REGION_NAME,
|
||||
signature_version=settings.AWS_S3_SIGNATURE_VERSION,
|
||||
),
|
||||
)
|
||||
return default_storage.connection.meta.client
|
||||
|
||||
|
||||
def generate_upload_policy(file):
|
||||
"""
|
||||
Generate a S3 upload policy for a given file.
|
||||
@@ -433,26 +458,7 @@ def generate_upload_policy(file):
|
||||
|
||||
key = file.temporary_file_key
|
||||
|
||||
# This settings 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
|
||||
# docker compose stack used in development. The frontend application will use localhost
|
||||
# to connect to the object storage while the backend application will use the object storage
|
||||
# service name declared in the docker compose stack.
|
||||
# This is needed because the domain name is used to compute the signature. So it can't be
|
||||
# changed dynamically by the frontend application.
|
||||
if settings.AWS_S3_DOMAIN_REPLACE:
|
||||
s3_client = boto3.client(
|
||||
"s3",
|
||||
aws_access_key_id=settings.AWS_S3_ACCESS_KEY_ID,
|
||||
aws_secret_access_key=settings.AWS_S3_SECRET_ACCESS_KEY,
|
||||
endpoint_url=settings.AWS_S3_DOMAIN_REPLACE,
|
||||
config=botocore.client.Config(
|
||||
region_name=settings.AWS_S3_REGION_NAME,
|
||||
signature_version=settings.AWS_S3_SIGNATURE_VERSION,
|
||||
),
|
||||
)
|
||||
else:
|
||||
s3_client = default_storage.connection.meta.client
|
||||
s3_client = _get_s3_client()
|
||||
|
||||
# Generate the policy
|
||||
policy = s3_client.generate_presigned_url(
|
||||
@@ -473,26 +479,7 @@ def generate_download_s3_url(
|
||||
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
|
||||
# docker compose stack used in development. The frontend application will use localhost
|
||||
# to connect to the object storage while the backend application will use the object storage
|
||||
# service name declared in the docker compose stack.
|
||||
# This is needed because the domain name is used to compute the signature. So it can't be
|
||||
# changed dynamically by the frontend application.
|
||||
if settings.AWS_S3_DOMAIN_REPLACE and override_domain:
|
||||
s3_client = boto3.client(
|
||||
"s3",
|
||||
aws_access_key_id=settings.AWS_S3_ACCESS_KEY_ID,
|
||||
aws_secret_access_key=settings.AWS_S3_SECRET_ACCESS_KEY,
|
||||
endpoint_url=settings.AWS_S3_DOMAIN_REPLACE,
|
||||
config=botocore.client.Config(
|
||||
region_name=settings.AWS_S3_REGION_NAME,
|
||||
signature_version=settings.AWS_S3_SIGNATURE_VERSION,
|
||||
),
|
||||
)
|
||||
else:
|
||||
s3_client = default_storage.connection.meta.client
|
||||
s3_client = _get_s3_client(override_domain=override_domain)
|
||||
|
||||
return s3_client.generate_presigned_url(
|
||||
ClientMethod="get_object",
|
||||
|
||||
@@ -7,7 +7,7 @@ build-backend = "uv_build"
|
||||
|
||||
[project]
|
||||
name = "meet"
|
||||
version = "1.23.0"
|
||||
version = "1.24.0"
|
||||
authors = [{ "name" = "DINUM", "email" = "dev@mail.numerique.gouv.fr" }]
|
||||
classifiers = [
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
|
||||
Generated
+1
-1
@@ -1187,7 +1187,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "meet"
|
||||
version = "1.23.0"
|
||||
version = "1.24.0"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "aiohttp" },
|
||||
|
||||
Generated
+32
-26
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "meet",
|
||||
"version": "1.23.0",
|
||||
"version": "1.24.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "meet",
|
||||
"version": "1.23.0",
|
||||
"version": "1.24.0",
|
||||
"dependencies": {
|
||||
"@fontsource-variable/atkinson-hyperlegible-next": "5.2.6",
|
||||
"@fontsource-variable/lexend": "5.2.11",
|
||||
@@ -15,11 +15,11 @@
|
||||
"@livekit/components-react": "2.9.21",
|
||||
"@livekit/components-styles": "1.2.0",
|
||||
"@livekit/track-processors": "0.7.2",
|
||||
"@mediapipe/tasks-vision": "0.10.14",
|
||||
"@mediapipe/tasks-vision": "0.10.35",
|
||||
"@pandacss/preset-panda": "1.11.3",
|
||||
"@react-types/overlays": "3.10.0",
|
||||
"@remixicon/react": "4.9.0",
|
||||
"@tanstack/react-query": "5.101.0",
|
||||
"@tanstack/react-query": "5.101.1",
|
||||
"@timephy/rnnoise-wasm": "1.0.0",
|
||||
"crisp-sdk-web": "1.1.2",
|
||||
"derive-valtio": "0.2.0",
|
||||
@@ -29,8 +29,8 @@
|
||||
"i18next-browser-languagedetector": "8.2.1",
|
||||
"i18next-parser": "9.4.0",
|
||||
"i18next-resources-to-backend": "1.2.1",
|
||||
"livekit-client": "2.19.2",
|
||||
"posthog-js": "1.391.2",
|
||||
"livekit-client": "2.20.0",
|
||||
"posthog-js": "1.395.0",
|
||||
"react": "18.3.1",
|
||||
"react-aria": "3.50.0",
|
||||
"react-aria-components": "1.19.0",
|
||||
@@ -1028,9 +1028,9 @@
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@livekit/protocol": {
|
||||
"version": "1.45.8",
|
||||
"resolved": "https://registry.npmjs.org/@livekit/protocol/-/protocol-1.45.8.tgz",
|
||||
"integrity": "sha512-Q+l57E7w/xxOBFVWzdX5rkAZO7ffyF+rlDzNUYq2SU114+5aTyCq+PK4unaEVDNd4952Af7wteKr3sOgasGuaA==",
|
||||
"version": "1.46.6",
|
||||
"resolved": "https://registry.npmjs.org/@livekit/protocol/-/protocol-1.46.6.tgz",
|
||||
"integrity": "sha512-upzlHP1vi/kZ/QqALZTFskQ0ifqc2f15RKucHYOsIHJsaXvEYanG75mAb7o+Yomfs4XhQ4BaRsdY+TFHXpaqrg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@bufbuild/protobuf": "^1.10.0"
|
||||
@@ -1049,12 +1049,18 @@
|
||||
"livekit-client": "^1.12.0 || ^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@mediapipe/tasks-vision": {
|
||||
"node_modules/@livekit/track-processors/node_modules/@mediapipe/tasks-vision": {
|
||||
"version": "0.10.14",
|
||||
"resolved": "https://registry.npmjs.org/@mediapipe/tasks-vision/-/tasks-vision-0.10.14.tgz",
|
||||
"integrity": "sha512-vOifgZhkndgybdvoRITzRkIueWWSiCKuEUXXK6Q4FaJsFvRJuwgg++vqFUMlL0Uox62U5aEXFhHxlhV7Ja5e3Q==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@mediapipe/tasks-vision": {
|
||||
"version": "0.10.35",
|
||||
"resolved": "https://registry.npmjs.org/@mediapipe/tasks-vision/-/tasks-vision-0.10.35.tgz",
|
||||
"integrity": "sha512-HOvadwVRE6JC+45nyYhmnywnr5h/J8KZvOeUNVOG9q/0875pZgItznFB9bRTvLc264YSJqiZ1NsIpCStJw/egg==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@modelcontextprotocol/sdk": {
|
||||
"version": "1.29.0",
|
||||
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz",
|
||||
@@ -2415,9 +2421,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/query-core": {
|
||||
"version": "5.101.0",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.101.0.tgz",
|
||||
"integrity": "sha512-cQetA74EB+seWySv1TTKr828TnP0u39m6LykwDXIo84SNortpDkp30TMEjkqtYCNP9c40uT/iwl6MLiufEt0Ow==",
|
||||
"version": "5.101.1",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.101.1.tgz",
|
||||
"integrity": "sha512-Y6Y92dkXtNqx67m2pMSxUsA3zOCwv862JexZRP8/EPwvKXMPu9m8rv43spiXWzOUIggQ3SQApttALStzhA8B4g==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2436,12 +2442,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/react-query": {
|
||||
"version": "5.101.0",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.101.0.tgz",
|
||||
"integrity": "sha512-rLlJXSpkqfizLWgkR5+eLeIk0MvTx/meEIR7LRjxic+qxiQP8zVjq7BqQkiCMNLQBlLfuOLqqr6KO5GtrDlmSg==",
|
||||
"version": "5.101.1",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.101.1.tgz",
|
||||
"integrity": "sha512-ZnONUuQKJe1bJMStXUL1s5uKN9FcfC28j5cK+iDZcdSHtUv1wtin1cGc/Oewhf2Oc4eKY7lggtpvT/AbMmhHew==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tanstack/query-core": "5.101.0"
|
||||
"@tanstack/query-core": "5.101.1"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -8119,13 +8125,13 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/livekit-client": {
|
||||
"version": "2.19.2",
|
||||
"resolved": "https://registry.npmjs.org/livekit-client/-/livekit-client-2.19.2.tgz",
|
||||
"integrity": "sha512-Kvk07QYDWRAbmYNLRll04ZIuxMQobW/oLPYnmR1kCy8GGHpU0gqyHf704Rz+29zfy8IJZRjKqeVbzGSKn9sumw==",
|
||||
"version": "2.20.0",
|
||||
"resolved": "https://registry.npmjs.org/livekit-client/-/livekit-client-2.20.0.tgz",
|
||||
"integrity": "sha512-RIJcpvBmOmwz3jTj3rmdY6Dzr55HrhcaJjMgY+HSmoEM+yIRyA40m7r8UKv0hnZWM3z/AYhP1q8C8ciz5UWFKQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@livekit/mutex": "1.1.1",
|
||||
"@livekit/protocol": "1.45.8",
|
||||
"@livekit/protocol": "1.46.6",
|
||||
"events": "^3.3.0",
|
||||
"jose": "^6.1.0",
|
||||
"loglevel": "^1.9.2",
|
||||
@@ -9130,13 +9136,13 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/posthog-js": {
|
||||
"version": "1.391.2",
|
||||
"resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.391.2.tgz",
|
||||
"integrity": "sha512-q0DZN6ljchSnAFJIXf+sQFTPlsLjTlRa+TvrL+QRb6413BGtib/MNiQy1bnwLKt8KR+f6xJYvkqdLyty9s4Aww==",
|
||||
"version": "1.395.0",
|
||||
"resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.395.0.tgz",
|
||||
"integrity": "sha512-5iTb00CGt2eQUUiBQysQiX89RAbCN6wK2sDNzvs9zv0alaY8mJ0ZySrUD3LQ+XyLhgM5pCpacBuUwChqiYDLDw==",
|
||||
"license": "SEE LICENSE IN LICENSE",
|
||||
"dependencies": {
|
||||
"@posthog/core": "^1.35.3",
|
||||
"@posthog/types": "^1.390.2",
|
||||
"@posthog/core": "^1.38.0",
|
||||
"@posthog/types": "^1.391.1",
|
||||
"core-js": "^3.38.1",
|
||||
"dompurify": "^3.3.2",
|
||||
"fflate": "^0.4.8",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "meet",
|
||||
"private": true,
|
||||
"version": "1.23.0",
|
||||
"version": "1.24.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "panda codegen && vite",
|
||||
@@ -22,11 +22,11 @@
|
||||
"@livekit/components-react": "2.9.21",
|
||||
"@livekit/components-styles": "1.2.0",
|
||||
"@livekit/track-processors": "0.7.2",
|
||||
"@mediapipe/tasks-vision": "0.10.14",
|
||||
"@mediapipe/tasks-vision": "0.10.35",
|
||||
"@pandacss/preset-panda": "1.11.3",
|
||||
"@react-types/overlays": "3.10.0",
|
||||
"@remixicon/react": "4.9.0",
|
||||
"@tanstack/react-query": "5.101.0",
|
||||
"@tanstack/react-query": "5.101.1",
|
||||
"@timephy/rnnoise-wasm": "1.0.0",
|
||||
"crisp-sdk-web": "1.1.2",
|
||||
"derive-valtio": "0.2.0",
|
||||
@@ -36,8 +36,8 @@
|
||||
"i18next-browser-languagedetector": "8.2.1",
|
||||
"i18next-parser": "9.4.0",
|
||||
"i18next-resources-to-backend": "1.2.1",
|
||||
"livekit-client": "2.19.2",
|
||||
"posthog-js": "1.391.2",
|
||||
"livekit-client": "2.20.0",
|
||||
"posthog-js": "1.395.0",
|
||||
"react": "18.3.1",
|
||||
"react-aria": "3.50.0",
|
||||
"react-aria-components": "1.19.0",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
apiVersion: v2
|
||||
type: application
|
||||
name: meet
|
||||
version: 0.0.26
|
||||
version: 0.0.27
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<OfficeApp xmlns="http://schemas.microsoft.com/office/appforoffice/1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bt="http://schemas.microsoft.com/office/officeappbasictypes/1.0" xmlns:mailappor="http://schemas.microsoft.com/office/mailappversionoverrides/1.0" xsi:type="MailApp">
|
||||
<Id>{{ .id }}</Id>
|
||||
<Version>0.0.2.0</Version>
|
||||
<Version>1.0.0.0</Version>
|
||||
<ProviderName>{{ .appName }}</ProviderName>
|
||||
<DefaultLocale>fr-FR</DefaultLocale>
|
||||
<DisplayName DefaultValue="{{ .appName }}"/>
|
||||
@@ -205,5 +205,174 @@
|
||||
</bt:String>
|
||||
</bt:LongStrings>
|
||||
</Resources>
|
||||
|
||||
<!-- ─── V1.1 override: MUST be nested inside the V1.0 block, after Resources ─── -->
|
||||
<VersionOverrides xmlns="http://schemas.microsoft.com/office/mailappversionoverrides/1.1" xsi:type="VersionOverridesV1_1">
|
||||
<Requirements>
|
||||
<bt:Sets DefaultMinVersion="1.8">
|
||||
<bt:Set Name="Mailbox"/>
|
||||
</bt:Sets>
|
||||
</Requirements>
|
||||
<Hosts>
|
||||
<Host xsi:type="MailHost">
|
||||
<DesktopFormFactor>
|
||||
<SupportsSharedFolders>true</SupportsSharedFolders>
|
||||
<FunctionFile resid="Commands.Url"/>
|
||||
|
||||
<!-- ─── Mail: Read ─────────────────────────────────────────── -->
|
||||
<ExtensionPoint xsi:type="MessageReadCommandSurface">
|
||||
<OfficeTab id="TabDefault">
|
||||
<Group id="msgReadGroup">
|
||||
<Label resid="GroupLabel"/>
|
||||
<Control xsi:type="Button" id="msgReadOpenPaneButton">
|
||||
<Label resid="TaskpaneButton.Label"/>
|
||||
<Supertip>
|
||||
<Title resid="TaskpaneButton.Label"/>
|
||||
<Description resid="TaskpaneButton.Tooltip"/>
|
||||
</Supertip>
|
||||
<Icon>
|
||||
<bt:Image size="16" resid="Icon.16x16"/>
|
||||
<bt:Image size="32" resid="Icon.32x32"/>
|
||||
<bt:Image size="80" resid="Icon.80x80"/>
|
||||
</Icon>
|
||||
<Action xsi:type="ShowTaskpane">
|
||||
<SourceLocation resid="Taskpane.Url"/>
|
||||
</Action>
|
||||
</Control>
|
||||
</Group>
|
||||
</OfficeTab>
|
||||
</ExtensionPoint>
|
||||
|
||||
<!-- ─── Mail: Compose ─────────────────────────────────────── -->
|
||||
<ExtensionPoint xsi:type="MessageComposeCommandSurface">
|
||||
<OfficeTab id="TabDefault">
|
||||
<Group id="msgComposeGroup">
|
||||
<Label resid="GroupLabel"/>
|
||||
<Control xsi:type="Button" id="msgComposeGenerateLinkButton">
|
||||
<Label resid="GenerateLink.Label"/>
|
||||
<Supertip>
|
||||
<Title resid="GenerateLink.Label"/>
|
||||
<Description resid="GenerateLink.Tooltip"/>
|
||||
</Supertip>
|
||||
<Icon>
|
||||
<bt:Image size="16" resid="Icon.16x16"/>
|
||||
<bt:Image size="32" resid="Icon.32x32"/>
|
||||
<bt:Image size="80" resid="Icon.80x80"/>
|
||||
</Icon>
|
||||
<Action xsi:type="ExecuteFunction">
|
||||
<FunctionName>generateMeetingLinkFromMail</FunctionName>
|
||||
</Action>
|
||||
</Control>
|
||||
<Control xsi:type="Button" id="msgComposeOpenPaneButton">
|
||||
<Label resid="TaskpaneButton.Label"/>
|
||||
<Supertip>
|
||||
<Title resid="TaskpaneButton.Label"/>
|
||||
<Description resid="TaskpaneButton.Tooltip"/>
|
||||
</Supertip>
|
||||
<Icon>
|
||||
<bt:Image size="16" resid="Settings.16x16"/>
|
||||
<bt:Image size="32" resid="Settings.32x32"/>
|
||||
<bt:Image size="80" resid="Settings.80x80"/>
|
||||
</Icon>
|
||||
<Action xsi:type="ShowTaskpane">
|
||||
<SourceLocation resid="Taskpane.Url"/>
|
||||
</Action>
|
||||
</Control>
|
||||
</Group>
|
||||
</OfficeTab>
|
||||
</ExtensionPoint>
|
||||
|
||||
<!-- ─── Calendar: Compose (New/Edit appointment) ──────────── -->
|
||||
<ExtensionPoint xsi:type="AppointmentOrganizerCommandSurface">
|
||||
<OfficeTab id="TabDefault">
|
||||
<Group id="apptComposeGroup">
|
||||
<Label resid="GroupLabel"/>
|
||||
<Control xsi:type="Button" id="apptGenerateLinkButton">
|
||||
<Label resid="GenerateLink.Label"/>
|
||||
<Supertip>
|
||||
<Title resid="GenerateLink.Label"/>
|
||||
<Description resid="GenerateLink.Tooltip"/>
|
||||
</Supertip>
|
||||
<Icon>
|
||||
<bt:Image size="16" resid="Icon.16x16"/>
|
||||
<bt:Image size="32" resid="Icon.32x32"/>
|
||||
<bt:Image size="80" resid="Icon.80x80"/>
|
||||
</Icon>
|
||||
<Action xsi:type="ExecuteFunction">
|
||||
<FunctionName>generateMeetingLinkFromCalendar</FunctionName>
|
||||
</Action>
|
||||
</Control>
|
||||
|
||||
<Control xsi:type="Button" id="apptOpenSettingsButton">
|
||||
<Label resid="OpenSettings.Label"/>
|
||||
<Supertip>
|
||||
<Title resid="OpenSettings.Label"/>
|
||||
<Description resid="OpenSettings.Tooltip"/>
|
||||
</Supertip>
|
||||
<Icon>
|
||||
<bt:Image size="16" resid="Settings.16x16"/>
|
||||
<bt:Image size="32" resid="Settings.32x32"/>
|
||||
<bt:Image size="80" resid="Settings.80x80"/>
|
||||
</Icon>
|
||||
<Action xsi:type="ShowTaskpane">
|
||||
<SourceLocation resid="Taskpane.Url"/>
|
||||
</Action>
|
||||
</Control>
|
||||
|
||||
</Group>
|
||||
</OfficeTab>
|
||||
</ExtensionPoint>
|
||||
|
||||
</DesktopFormFactor>
|
||||
</Host>
|
||||
</Hosts>
|
||||
<Resources>
|
||||
<bt:Images>
|
||||
<bt:Image id="Settings.16x16" DefaultValue="{{ .baseUrl }}/assets/settings-16.png"/>
|
||||
<bt:Image id="Settings.32x32" DefaultValue="{{ .baseUrl }}/assets/settings-32.png"/>
|
||||
<bt:Image id="Settings.80x80" DefaultValue="{{ .baseUrl }}/assets/settings-80.png"/>
|
||||
<bt:Image id="Add.16x16" DefaultValue="{{ .baseUrl }}/assets/add-16.png"/>
|
||||
<bt:Image id="Add.32x32" DefaultValue="{{ .baseUrl }}/assets/add-32.png"/>
|
||||
<bt:Image id="Add.80x80" DefaultValue="{{ .baseUrl }}/assets/add-80.png"/>
|
||||
<bt:Image id="Icon.16x16" DefaultValue="{{ .baseUrl }}/assets/icon-16.png"/>
|
||||
<bt:Image id="Icon.32x32" DefaultValue="{{ .baseUrl }}/assets/icon-32.png"/>
|
||||
<bt:Image id="Icon.80x80" DefaultValue="{{ .baseUrl }}/assets/icon-80.png"/>
|
||||
</bt:Images>
|
||||
<bt:Urls>
|
||||
<bt:Url id="Commands.Url" DefaultValue="{{ .baseUrl }}/commands.html"/>
|
||||
<bt:Url id="Taskpane.Url" DefaultValue="{{ .baseUrl }}/taskpane.html"/>
|
||||
</bt:Urls>
|
||||
<bt:ShortStrings>
|
||||
<!-- Default (French) -->
|
||||
<bt:String id="GroupLabel" DefaultValue="{{ .appName }}"/>
|
||||
<bt:String id="GenerateLink.Label" DefaultValue="Ajouter un lien {{ .appName }}">
|
||||
<bt:Override Locale="en-US" Value="Add a {{ .appName }} link"/>
|
||||
<bt:Override Locale="de-DE" Value="{{ .appName }}-Link hinzufügen"/>
|
||||
</bt:String>
|
||||
<bt:String id="TaskpaneButton.Label" DefaultValue="Ouvrir les paramètres">
|
||||
<bt:Override Locale="en-US" Value="Open settings"/>
|
||||
<bt:Override Locale="de-DE" Value="Einstellungen öffnen"/>
|
||||
</bt:String>
|
||||
<bt:String id="OpenSettings.Label" DefaultValue="Paramètres">
|
||||
<bt:Override Locale="en-US" Value="Settings"/>
|
||||
<bt:Override Locale="de-DE" Value="Einstellungen"/>
|
||||
</bt:String>
|
||||
</bt:ShortStrings>
|
||||
<bt:LongStrings>
|
||||
<bt:String id="GenerateLink.Tooltip" DefaultValue="Génère un lien de réunion {{ .appName }} et l'insère dans l'événement.">
|
||||
<bt:Override Locale="de-DE" Value="Generiert einen {{ .appName }}-Besprechungslink und fügt ihn in den Termin ein."/>
|
||||
<bt:Override Locale="en-US" Value="Generates a {{ .appName }} meeting link and inserts it into the item."/>
|
||||
</bt:String>
|
||||
<bt:String id="TaskpaneButton.Tooltip" DefaultValue="Ouvre les paramètres de connexion {{ .appName }}.">
|
||||
<bt:Override Locale="de-DE" Value="Öffnet die {{ .appName }}-Verbindungseinstellungen."/>
|
||||
<bt:Override Locale="en-US" Value="Opens the {{ .appName }} connection settings."/>
|
||||
</bt:String>
|
||||
<bt:String id="OpenSettings.Tooltip" DefaultValue="Ouvre les paramètres de connexion {{ .appName }}.">
|
||||
<bt:Override Locale="de-DE" Value="Öffnet die {{ .appName }}-Verbindungseinstellungen."/>
|
||||
<bt:Override Locale="en-US" Value="Opens the {{ .appName }} connection settings."/>
|
||||
</bt:String>
|
||||
</bt:LongStrings>
|
||||
</Resources>
|
||||
</VersionOverrides>
|
||||
</VersionOverrides>
|
||||
</OfficeApp>
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "mail_mjml",
|
||||
"version": "1.23.0",
|
||||
"version": "1.24.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "mail_mjml",
|
||||
"version": "1.23.0",
|
||||
"version": "1.24.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@html-to/text-cli": "0.6.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mail_mjml",
|
||||
"version": "1.23.0",
|
||||
"version": "1.24.0",
|
||||
"description": "An util to generate html and text django's templates from mjml templates",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "sdk",
|
||||
"version": "1.23.0",
|
||||
"version": "1.24.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "sdk",
|
||||
"version": "1.23.0",
|
||||
"version": "1.24.0",
|
||||
"license": "ISC",
|
||||
"workspaces": [
|
||||
"./library",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sdk",
|
||||
"version": "1.23.0",
|
||||
"version": "1.24.0",
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
[project]
|
||||
name = "summary"
|
||||
version = "1.23.0"
|
||||
version = "1.24.0"
|
||||
dependencies = [
|
||||
"fastapi[standard]>=0.105.0",
|
||||
"uvicorn>=0.24.0",
|
||||
|
||||
@@ -18,7 +18,12 @@ from requests import exceptions
|
||||
from summary.core.analytics import MetadataManager, get_analytics
|
||||
from summary.core.config import get_settings
|
||||
from summary.core.docs_service import create_document_in_lasuite_docs
|
||||
from summary.core.file_service import FileService, FileServiceException, TranscribeError
|
||||
from summary.core.file_service import (
|
||||
CorruptedAudioFile,
|
||||
FileService,
|
||||
FileServiceException,
|
||||
TranscribeError,
|
||||
)
|
||||
from summary.core.llm_service import LLMException, LLMObservability, LLMService
|
||||
from summary.core.locales import get_locale
|
||||
from summary.core.models import (
|
||||
@@ -147,10 +152,20 @@ def transcribe_audio(
|
||||
# Mimic OpenAI's timeout settings
|
||||
timeout=(60, 10 * 60),
|
||||
)
|
||||
if res.status_code == 400:
|
||||
logger.info(
|
||||
"WhisperX transcription failed, "
|
||||
"likely due to a corrupted audio file: %s",
|
||||
res.text,
|
||||
)
|
||||
raise CorruptedAudioFile("WhisperX coudln't decode the audio file.")
|
||||
|
||||
try:
|
||||
res.raise_for_status()
|
||||
except requests.exceptions.HTTPError as e:
|
||||
raise RuntimeError("WhisperX transcription failed, %s", res.text) from e
|
||||
except requests.exceptions.HTTPError:
|
||||
logger.exception("WhisperX transcription failed")
|
||||
# We reraise the error so that it can be retried by celery
|
||||
raise
|
||||
|
||||
transcription_json: dict[str, Any] = res.json()
|
||||
# We remove the "usage" key from the transcription_json dictionary
|
||||
|
||||
@@ -41,6 +41,12 @@ class NoAudioInFileError(TranscribeError):
|
||||
error_code = "no_audio_in_file"
|
||||
|
||||
|
||||
class CorruptedAudioFile(TranscribeError):
|
||||
"""Raised when a media file does not contain any audio."""
|
||||
|
||||
error_code = "corrupted_audio_file"
|
||||
|
||||
|
||||
def _get_duration_from_packets(local_path: Path) -> float:
|
||||
"""Estimate duration from audio packet timestamps."""
|
||||
# Run ffprobe to inspect the first audio stream in the file.
|
||||
|
||||
@@ -96,7 +96,13 @@ class TranscribeWebhookFailurePayload(BaseWebhook):
|
||||
# we authorized any other string than the one in the literal
|
||||
# to avoid causing a breaking change on the client side
|
||||
error_code: (
|
||||
Literal["unknown_error", "no_audio_in_file", "media_duration_too_long"] | str
|
||||
Literal[
|
||||
"unknown_error",
|
||||
"no_audio_in_file",
|
||||
"media_duration_too_long",
|
||||
"corrupted_audio_file",
|
||||
]
|
||||
| str
|
||||
) = Field(title="Error code", description="The error code.")
|
||||
|
||||
|
||||
|
||||
@@ -275,7 +275,7 @@ def _build_participant_timelines(
|
||||
def _build_speaker_timelines(transcription: Any) -> dict[str, list[Interval]]:
|
||||
"""Build interval timelines from WhisperX transcription segments."""
|
||||
intervals: dict[str, list[Interval]] = {}
|
||||
segments = transcription.segments if hasattr(transcription, "segments") else []
|
||||
segments = transcription.get("segments") or []
|
||||
max_word_duration = settings.resolve_speaker_identities_max_word_duration
|
||||
|
||||
for segment in segments:
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"""Tests for the speaker-to-user assignment service."""
|
||||
|
||||
import math
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime
|
||||
|
||||
from summary.core import user_assign
|
||||
@@ -17,11 +16,9 @@ from summary.core.user_assign import (
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class FakeTranscription:
|
||||
"""Mimics the OpenAI Transcription pydantic model for testing."""
|
||||
|
||||
segments: list = field(default_factory=list)
|
||||
def make_transcription(segments: list | None = None) -> dict:
|
||||
"""Build a WhisperX transcription dict, matching WhisperXResponse.model_dump()."""
|
||||
return {"segments": segments or []}
|
||||
|
||||
|
||||
RECORDING_START = datetime.fromisoformat("2026-03-17T15:30:33.000001")
|
||||
@@ -68,7 +65,7 @@ METADATA_SINGLE_USER = {
|
||||
],
|
||||
}
|
||||
|
||||
DIARIZATION_SINGLE_SPEAKER = FakeTranscription(
|
||||
DIARIZATION_SINGLE_SPEAKER = make_transcription(
|
||||
segments=[
|
||||
{
|
||||
"start": 1.363,
|
||||
@@ -182,7 +179,7 @@ class TestBuildSpeakerTimelines:
|
||||
|
||||
def test_segment_without_words_falls_back_to_segment_bounds(self):
|
||||
"""Segments missing a `words` key use the segment start/end as one interval."""
|
||||
transcription = FakeTranscription(
|
||||
transcription = make_transcription(
|
||||
segments=[{"start": 1.5, "end": 3.5, "speaker": "SPEAKER_00"}],
|
||||
)
|
||||
result = _build_speaker_timelines(transcription)
|
||||
@@ -190,7 +187,7 @@ class TestBuildSpeakerTimelines:
|
||||
|
||||
def test_segment_with_only_none_word_timestamps_falls_back(self):
|
||||
"""If every word has None start/end, fall back to segment bounds."""
|
||||
transcription = FakeTranscription(
|
||||
transcription = make_transcription(
|
||||
segments=[
|
||||
{
|
||||
"start": 1.0,
|
||||
@@ -208,7 +205,7 @@ class TestBuildSpeakerTimelines:
|
||||
|
||||
def test_short_words_only_uses_segment_start_and_last_word_end(self):
|
||||
"""With no overly long words, the interval runs segment start to end."""
|
||||
transcription = FakeTranscription(
|
||||
transcription = make_transcription(
|
||||
segments=[
|
||||
{
|
||||
"start": 1.0,
|
||||
@@ -231,7 +228,7 @@ class TestBuildSpeakerTimelines:
|
||||
max_word_duration = (
|
||||
user_assign.settings.resolve_speaker_identities_max_word_duration
|
||||
)
|
||||
transcription = FakeTranscription(
|
||||
transcription = make_transcription(
|
||||
segments=[
|
||||
{
|
||||
"start": 0.0,
|
||||
@@ -252,7 +249,7 @@ class TestBuildSpeakerTimelines:
|
||||
|
||||
def test_long_word_in_middle_splits_segment(self):
|
||||
"""Short words around a long word produce two intervals (before-cap + after)."""
|
||||
transcription = FakeTranscription(
|
||||
transcription = make_transcription(
|
||||
segments=[
|
||||
{
|
||||
"start": 0.0,
|
||||
@@ -275,7 +272,7 @@ class TestBuildSpeakerTimelines:
|
||||
|
||||
def test_tail_word_is_capped_at_max_duration(self):
|
||||
"""The trailing word's end is capped at word.start + max_word_duration."""
|
||||
transcription = FakeTranscription(
|
||||
transcription = make_transcription(
|
||||
segments=[
|
||||
{
|
||||
"start": 0.0,
|
||||
@@ -302,7 +299,7 @@ class TestBuildSpeakerTimelines:
|
||||
update={"resolve_speaker_identities_enable_split_on_words": False},
|
||||
),
|
||||
)
|
||||
transcription = FakeTranscription(
|
||||
transcription = make_transcription(
|
||||
segments=[
|
||||
{
|
||||
"start": 0.0,
|
||||
@@ -322,7 +319,7 @@ class TestBuildSpeakerTimelines:
|
||||
|
||||
def test_multiple_speakers_keep_separate_timelines(self):
|
||||
"""Segments from different speakers populate independent timeline entries."""
|
||||
transcription = FakeTranscription(
|
||||
transcription = make_transcription(
|
||||
segments=[
|
||||
{
|
||||
"start": 0.0,
|
||||
@@ -402,7 +399,7 @@ class TestResolveSpeakerIdentities:
|
||||
],
|
||||
"participants": [{"participantId": "user-a", "name": "Shared Mic"}],
|
||||
}
|
||||
transcription = FakeTranscription(
|
||||
transcription = make_transcription(
|
||||
segments=[
|
||||
{"start": 1.0, "end": 3.0, "speaker": "SPEAKER_00"},
|
||||
{"start": 5.0, "end": 7.0, "speaker": "SPEAKER_01"},
|
||||
@@ -445,7 +442,7 @@ class TestResolveSpeakerIdentities:
|
||||
{"participantId": "user-b", "name": "Bob"},
|
||||
],
|
||||
}
|
||||
transcription = FakeTranscription(
|
||||
transcription = make_transcription(
|
||||
segments=[
|
||||
{"start": 1.5, "end": 3.5, "speaker": "SPEAKER_00"},
|
||||
{"start": 5.5, "end": 7.5, "speaker": "SPEAKER_01"},
|
||||
@@ -493,7 +490,7 @@ class TestResolveSpeakerIdentities:
|
||||
{"participantId": "user-b", "name": "Bob"},
|
||||
],
|
||||
}
|
||||
transcription = FakeTranscription(
|
||||
transcription = make_transcription(
|
||||
segments=[
|
||||
{"start": 1.5, "end": 5.5, "speaker": "SPEAKER_00"},
|
||||
{"start": 4.0, "end": 7.5, "speaker": "SPEAKER_01"},
|
||||
@@ -529,7 +526,7 @@ class TestResolveSpeakerIdentities:
|
||||
],
|
||||
"participants": [{"participantId": "user-a", "name": "Brief User"}],
|
||||
}
|
||||
transcription = FakeTranscription(
|
||||
transcription = make_transcription(
|
||||
segments=[
|
||||
{"start": 1.0, "end": 10.0, "speaker": "SPEAKER_00"},
|
||||
],
|
||||
@@ -561,7 +558,7 @@ class TestResolveSpeakerIdentities:
|
||||
],
|
||||
"participants": [{"participantId": "user-a", "name": "Early User"}],
|
||||
}
|
||||
transcription = FakeTranscription(
|
||||
transcription = make_transcription(
|
||||
segments=[
|
||||
{"start": 0.0, "end": 3.0, "speaker": "SPEAKER_00"},
|
||||
],
|
||||
@@ -576,7 +573,7 @@ class TestResolveSpeakerIdentities:
|
||||
"""No segments produces empty result."""
|
||||
result = resolve_speaker_identities(
|
||||
METADATA_SINGLE_USER,
|
||||
FakeTranscription(segments=[]),
|
||||
make_transcription(segments=[]),
|
||||
RECORDING_START,
|
||||
RECORDING_END,
|
||||
)
|
||||
@@ -584,7 +581,7 @@ class TestResolveSpeakerIdentities:
|
||||
|
||||
def test_segment_without_speaker_ignored(self):
|
||||
"""Segments missing speaker key are skipped."""
|
||||
transcription = FakeTranscription(
|
||||
transcription = make_transcription(
|
||||
segments=[
|
||||
{"start": 1.0, "end": 3.0, "text": "no speaker"},
|
||||
],
|
||||
@@ -608,7 +605,7 @@ class TestResolveSpeakerIdentities:
|
||||
],
|
||||
"participants": [{"participantId": "user-a", "name": "Still Talking"}],
|
||||
}
|
||||
transcription = FakeTranscription(
|
||||
transcription = make_transcription(
|
||||
segments=[
|
||||
{"start": 2.0, "end": 9.0, "speaker": "SPEAKER_00"},
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user