mirror of
https://github.com/suitenumerique/meet.git
synced 2026-07-27 04:09:26 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f06cbd787a |
@@ -8,20 +8,6 @@ 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
|
||||
|
||||
@@ -164,7 +164,6 @@ 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:"
|
||||
|
||||
Generated
+4
-4
@@ -10,7 +10,7 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"core-js": "3.49.0",
|
||||
"i18next": "^26.3.2",
|
||||
"i18next": "26.3.1",
|
||||
"i18next-browser-languagedetector": "8.2.1",
|
||||
"regenerator-runtime": "0.14.1"
|
||||
},
|
||||
@@ -9364,9 +9364,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/i18next": {
|
||||
"version": "26.3.2",
|
||||
"resolved": "https://registry.npmjs.org/i18next/-/i18next-26.3.2.tgz",
|
||||
"integrity": "sha512-QQkXAM1sPDHqhxMQuBeHVMUn6mJchF+wdpOoQerciLAFqO3ZYdxO0EUbeEhruyutnNwpUQIITDVzLjwnNL0T1w==",
|
||||
"version": "26.3.1",
|
||||
"resolved": "https://registry.npmjs.org/i18next/-/i18next-26.3.1.tgz",
|
||||
"integrity": "sha512-txQqd5EULsqEh9OJqRH15aCaOuy/nLJyhw5EHCSKLKJE1aBbb3Zve2+uQIxgWhPm1QqUQoWyQBm2kfmmIrzkcQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"core-js": "3.49.0",
|
||||
"i18next": "26.3.2",
|
||||
"i18next": "26.3.1",
|
||||
"i18next-browser-languagedetector": "8.2.1",
|
||||
"regenerator-runtime": "0.14.1"
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
[project]
|
||||
name = "agents"
|
||||
version = "1.24.0"
|
||||
version = "1.23.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.24.0"
|
||||
version = "1.23.0"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "livekit-agents" },
|
||||
|
||||
+40
-27
@@ -423,31 +423,6 @@ 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.
|
||||
@@ -458,7 +433,26 @@ def generate_upload_policy(file):
|
||||
|
||||
key = file.temporary_file_key
|
||||
|
||||
s3_client = _get_s3_client()
|
||||
# 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
|
||||
|
||||
# Generate the policy
|
||||
policy = s3_client.generate_presigned_url(
|
||||
@@ -479,7 +473,26 @@ def generate_download_s3_url(
|
||||
if not key:
|
||||
raise ValueError("key cannot be empty")
|
||||
|
||||
s3_client = _get_s3_client(override_domain=override_domain)
|
||||
# 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
|
||||
|
||||
return s3_client.generate_presigned_url(
|
||||
ClientMethod="get_object",
|
||||
|
||||
@@ -7,7 +7,7 @@ build-backend = "uv_build"
|
||||
|
||||
[project]
|
||||
name = "meet"
|
||||
version = "1.24.0"
|
||||
version = "1.23.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.24.0"
|
||||
version = "1.23.0"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "aiohttp" },
|
||||
|
||||
Generated
+26
-32
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "meet",
|
||||
"version": "1.24.0",
|
||||
"version": "1.23.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "meet",
|
||||
"version": "1.24.0",
|
||||
"version": "1.23.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.35",
|
||||
"@mediapipe/tasks-vision": "0.10.14",
|
||||
"@pandacss/preset-panda": "1.11.3",
|
||||
"@react-types/overlays": "3.10.0",
|
||||
"@remixicon/react": "4.9.0",
|
||||
"@tanstack/react-query": "5.101.1",
|
||||
"@tanstack/react-query": "5.101.0",
|
||||
"@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.20.0",
|
||||
"posthog-js": "1.395.0",
|
||||
"livekit-client": "2.19.2",
|
||||
"posthog-js": "1.391.2",
|
||||
"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.46.6",
|
||||
"resolved": "https://registry.npmjs.org/@livekit/protocol/-/protocol-1.46.6.tgz",
|
||||
"integrity": "sha512-upzlHP1vi/kZ/QqALZTFskQ0ifqc2f15RKucHYOsIHJsaXvEYanG75mAb7o+Yomfs4XhQ4BaRsdY+TFHXpaqrg==",
|
||||
"version": "1.45.8",
|
||||
"resolved": "https://registry.npmjs.org/@livekit/protocol/-/protocol-1.45.8.tgz",
|
||||
"integrity": "sha512-Q+l57E7w/xxOBFVWzdX5rkAZO7ffyF+rlDzNUYq2SU114+5aTyCq+PK4unaEVDNd4952Af7wteKr3sOgasGuaA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@bufbuild/protobuf": "^1.10.0"
|
||||
@@ -1049,18 +1049,12 @@
|
||||
"livekit-client": "^1.12.0 || ^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@livekit/track-processors/node_modules/@mediapipe/tasks-vision": {
|
||||
"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",
|
||||
@@ -2421,9 +2415,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/query-core": {
|
||||
"version": "5.101.1",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.101.1.tgz",
|
||||
"integrity": "sha512-Y6Y92dkXtNqx67m2pMSxUsA3zOCwv862JexZRP8/EPwvKXMPu9m8rv43spiXWzOUIggQ3SQApttALStzhA8B4g==",
|
||||
"version": "5.101.0",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.101.0.tgz",
|
||||
"integrity": "sha512-cQetA74EB+seWySv1TTKr828TnP0u39m6LykwDXIo84SNortpDkp30TMEjkqtYCNP9c40uT/iwl6MLiufEt0Ow==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2442,12 +2436,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/react-query": {
|
||||
"version": "5.101.1",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.101.1.tgz",
|
||||
"integrity": "sha512-ZnONUuQKJe1bJMStXUL1s5uKN9FcfC28j5cK+iDZcdSHtUv1wtin1cGc/Oewhf2Oc4eKY7lggtpvT/AbMmhHew==",
|
||||
"version": "5.101.0",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.101.0.tgz",
|
||||
"integrity": "sha512-rLlJXSpkqfizLWgkR5+eLeIk0MvTx/meEIR7LRjxic+qxiQP8zVjq7BqQkiCMNLQBlLfuOLqqr6KO5GtrDlmSg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tanstack/query-core": "5.101.1"
|
||||
"@tanstack/query-core": "5.101.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -8125,13 +8119,13 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/livekit-client": {
|
||||
"version": "2.20.0",
|
||||
"resolved": "https://registry.npmjs.org/livekit-client/-/livekit-client-2.20.0.tgz",
|
||||
"integrity": "sha512-RIJcpvBmOmwz3jTj3rmdY6Dzr55HrhcaJjMgY+HSmoEM+yIRyA40m7r8UKv0hnZWM3z/AYhP1q8C8ciz5UWFKQ==",
|
||||
"version": "2.19.2",
|
||||
"resolved": "https://registry.npmjs.org/livekit-client/-/livekit-client-2.19.2.tgz",
|
||||
"integrity": "sha512-Kvk07QYDWRAbmYNLRll04ZIuxMQobW/oLPYnmR1kCy8GGHpU0gqyHf704Rz+29zfy8IJZRjKqeVbzGSKn9sumw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@livekit/mutex": "1.1.1",
|
||||
"@livekit/protocol": "1.46.6",
|
||||
"@livekit/protocol": "1.45.8",
|
||||
"events": "^3.3.0",
|
||||
"jose": "^6.1.0",
|
||||
"loglevel": "^1.9.2",
|
||||
@@ -9136,13 +9130,13 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/posthog-js": {
|
||||
"version": "1.395.0",
|
||||
"resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.395.0.tgz",
|
||||
"integrity": "sha512-5iTb00CGt2eQUUiBQysQiX89RAbCN6wK2sDNzvs9zv0alaY8mJ0ZySrUD3LQ+XyLhgM5pCpacBuUwChqiYDLDw==",
|
||||
"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==",
|
||||
"license": "SEE LICENSE IN LICENSE",
|
||||
"dependencies": {
|
||||
"@posthog/core": "^1.38.0",
|
||||
"@posthog/types": "^1.391.1",
|
||||
"@posthog/core": "^1.35.3",
|
||||
"@posthog/types": "^1.390.2",
|
||||
"core-js": "^3.38.1",
|
||||
"dompurify": "^3.3.2",
|
||||
"fflate": "^0.4.8",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "meet",
|
||||
"private": true,
|
||||
"version": "1.24.0",
|
||||
"version": "1.23.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.35",
|
||||
"@mediapipe/tasks-vision": "0.10.14",
|
||||
"@pandacss/preset-panda": "1.11.3",
|
||||
"@react-types/overlays": "3.10.0",
|
||||
"@remixicon/react": "4.9.0",
|
||||
"@tanstack/react-query": "5.101.1",
|
||||
"@tanstack/react-query": "5.101.0",
|
||||
"@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.20.0",
|
||||
"posthog-js": "1.395.0",
|
||||
"livekit-client": "2.19.2",
|
||||
"posthog-js": "1.391.2",
|
||||
"react": "18.3.1",
|
||||
"react-aria": "3.50.0",
|
||||
"react-aria-components": "1.19.0",
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "mail_mjml",
|
||||
"version": "1.24.0",
|
||||
"version": "1.23.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "mail_mjml",
|
||||
"version": "1.24.0",
|
||||
"version": "1.23.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@html-to/text-cli": "0.6.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mail_mjml",
|
||||
"version": "1.24.0",
|
||||
"version": "1.23.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.24.0",
|
||||
"version": "1.23.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "sdk",
|
||||
"version": "1.24.0",
|
||||
"version": "1.23.0",
|
||||
"license": "ISC",
|
||||
"workspaces": [
|
||||
"./library",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sdk",
|
||||
"version": "1.24.0",
|
||||
"version": "1.23.0",
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
[project]
|
||||
name = "summary"
|
||||
version = "1.24.0"
|
||||
version = "1.23.0"
|
||||
dependencies = [
|
||||
"fastapi[standard]>=0.105.0",
|
||||
"uvicorn>=0.24.0",
|
||||
|
||||
@@ -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.get("segments") or []
|
||||
segments = transcription.segments if hasattr(transcription, "segments") else []
|
||||
max_word_duration = settings.resolve_speaker_identities_max_word_duration
|
||||
|
||||
for segment in segments:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""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
|
||||
@@ -16,9 +17,11 @@ from summary.core.user_assign import (
|
||||
)
|
||||
|
||||
|
||||
def make_transcription(segments: list | None = None) -> dict:
|
||||
"""Build a WhisperX transcription dict, matching WhisperXResponse.model_dump()."""
|
||||
return {"segments": segments or []}
|
||||
@dataclass
|
||||
class FakeTranscription:
|
||||
"""Mimics the OpenAI Transcription pydantic model for testing."""
|
||||
|
||||
segments: list = field(default_factory=list)
|
||||
|
||||
|
||||
RECORDING_START = datetime.fromisoformat("2026-03-17T15:30:33.000001")
|
||||
@@ -65,7 +68,7 @@ METADATA_SINGLE_USER = {
|
||||
],
|
||||
}
|
||||
|
||||
DIARIZATION_SINGLE_SPEAKER = make_transcription(
|
||||
DIARIZATION_SINGLE_SPEAKER = FakeTranscription(
|
||||
segments=[
|
||||
{
|
||||
"start": 1.363,
|
||||
@@ -179,7 +182,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 = make_transcription(
|
||||
transcription = FakeTranscription(
|
||||
segments=[{"start": 1.5, "end": 3.5, "speaker": "SPEAKER_00"}],
|
||||
)
|
||||
result = _build_speaker_timelines(transcription)
|
||||
@@ -187,7 +190,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 = make_transcription(
|
||||
transcription = FakeTranscription(
|
||||
segments=[
|
||||
{
|
||||
"start": 1.0,
|
||||
@@ -205,7 +208,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 = make_transcription(
|
||||
transcription = FakeTranscription(
|
||||
segments=[
|
||||
{
|
||||
"start": 1.0,
|
||||
@@ -228,7 +231,7 @@ class TestBuildSpeakerTimelines:
|
||||
max_word_duration = (
|
||||
user_assign.settings.resolve_speaker_identities_max_word_duration
|
||||
)
|
||||
transcription = make_transcription(
|
||||
transcription = FakeTranscription(
|
||||
segments=[
|
||||
{
|
||||
"start": 0.0,
|
||||
@@ -249,7 +252,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 = make_transcription(
|
||||
transcription = FakeTranscription(
|
||||
segments=[
|
||||
{
|
||||
"start": 0.0,
|
||||
@@ -272,7 +275,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 = make_transcription(
|
||||
transcription = FakeTranscription(
|
||||
segments=[
|
||||
{
|
||||
"start": 0.0,
|
||||
@@ -299,7 +302,7 @@ class TestBuildSpeakerTimelines:
|
||||
update={"resolve_speaker_identities_enable_split_on_words": False},
|
||||
),
|
||||
)
|
||||
transcription = make_transcription(
|
||||
transcription = FakeTranscription(
|
||||
segments=[
|
||||
{
|
||||
"start": 0.0,
|
||||
@@ -319,7 +322,7 @@ class TestBuildSpeakerTimelines:
|
||||
|
||||
def test_multiple_speakers_keep_separate_timelines(self):
|
||||
"""Segments from different speakers populate independent timeline entries."""
|
||||
transcription = make_transcription(
|
||||
transcription = FakeTranscription(
|
||||
segments=[
|
||||
{
|
||||
"start": 0.0,
|
||||
@@ -399,7 +402,7 @@ class TestResolveSpeakerIdentities:
|
||||
],
|
||||
"participants": [{"participantId": "user-a", "name": "Shared Mic"}],
|
||||
}
|
||||
transcription = make_transcription(
|
||||
transcription = FakeTranscription(
|
||||
segments=[
|
||||
{"start": 1.0, "end": 3.0, "speaker": "SPEAKER_00"},
|
||||
{"start": 5.0, "end": 7.0, "speaker": "SPEAKER_01"},
|
||||
@@ -442,7 +445,7 @@ class TestResolveSpeakerIdentities:
|
||||
{"participantId": "user-b", "name": "Bob"},
|
||||
],
|
||||
}
|
||||
transcription = make_transcription(
|
||||
transcription = FakeTranscription(
|
||||
segments=[
|
||||
{"start": 1.5, "end": 3.5, "speaker": "SPEAKER_00"},
|
||||
{"start": 5.5, "end": 7.5, "speaker": "SPEAKER_01"},
|
||||
@@ -490,7 +493,7 @@ class TestResolveSpeakerIdentities:
|
||||
{"participantId": "user-b", "name": "Bob"},
|
||||
],
|
||||
}
|
||||
transcription = make_transcription(
|
||||
transcription = FakeTranscription(
|
||||
segments=[
|
||||
{"start": 1.5, "end": 5.5, "speaker": "SPEAKER_00"},
|
||||
{"start": 4.0, "end": 7.5, "speaker": "SPEAKER_01"},
|
||||
@@ -526,7 +529,7 @@ class TestResolveSpeakerIdentities:
|
||||
],
|
||||
"participants": [{"participantId": "user-a", "name": "Brief User"}],
|
||||
}
|
||||
transcription = make_transcription(
|
||||
transcription = FakeTranscription(
|
||||
segments=[
|
||||
{"start": 1.0, "end": 10.0, "speaker": "SPEAKER_00"},
|
||||
],
|
||||
@@ -558,7 +561,7 @@ class TestResolveSpeakerIdentities:
|
||||
],
|
||||
"participants": [{"participantId": "user-a", "name": "Early User"}],
|
||||
}
|
||||
transcription = make_transcription(
|
||||
transcription = FakeTranscription(
|
||||
segments=[
|
||||
{"start": 0.0, "end": 3.0, "speaker": "SPEAKER_00"},
|
||||
],
|
||||
@@ -573,7 +576,7 @@ class TestResolveSpeakerIdentities:
|
||||
"""No segments produces empty result."""
|
||||
result = resolve_speaker_identities(
|
||||
METADATA_SINGLE_USER,
|
||||
make_transcription(segments=[]),
|
||||
FakeTranscription(segments=[]),
|
||||
RECORDING_START,
|
||||
RECORDING_END,
|
||||
)
|
||||
@@ -581,7 +584,7 @@ class TestResolveSpeakerIdentities:
|
||||
|
||||
def test_segment_without_speaker_ignored(self):
|
||||
"""Segments missing speaker key are skipped."""
|
||||
transcription = make_transcription(
|
||||
transcription = FakeTranscription(
|
||||
segments=[
|
||||
{"start": 1.0, "end": 3.0, "text": "no speaker"},
|
||||
],
|
||||
@@ -605,7 +608,7 @@ class TestResolveSpeakerIdentities:
|
||||
],
|
||||
"participants": [{"participantId": "user-a", "name": "Still Talking"}],
|
||||
}
|
||||
transcription = make_transcription(
|
||||
transcription = FakeTranscription(
|
||||
segments=[
|
||||
{"start": 2.0, "end": 9.0, "speaker": "SPEAKER_00"},
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user