diff --git a/server/migrations/versions/74b2b0236931_add_transcript_source_kind.py b/server/migrations/versions/74b2b0236931_add_transcript_source_kind.py new file mode 100644 index 00000000..3c4c5600 --- /dev/null +++ b/server/migrations/versions/74b2b0236931_add_transcript_source_kind.py @@ -0,0 +1,48 @@ +"""Add transcript source kind + +Revision ID: 74b2b0236931 +Revises: 0925da921477 +Create Date: 2024-10-04 14:19:23.625447 + +""" + +from typing import Sequence, Union + +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision: str = "74b2b0236931" +down_revision: Union[str, None] = "0925da921477" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column( + "transcript", + sa.Column( + "source_kind", + sa.Enum("ROOM", "LIVE", "FILE", name="sourcekind"), + nullable=True, + ), + ) + + op.execute( + "UPDATE transcript SET source_kind = 'room' WHERE meeting_id IS NOT NULL" + ) + op.execute("UPDATE transcript SET source_kind = 'live' WHERE meeting_id IS NULL") + + with op.batch_alter_table("transcript", schema=None) as batch_op: + batch_op.alter_column("source_kind", nullable=False) + + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column("transcript", "source_kind") + + +# ### end Alembic commands ### diff --git a/server/migrations/versions/a7122bc0b2ca_add_shared_rooms.py b/server/migrations/versions/a7122bc0b2ca_add_shared_rooms.py new file mode 100644 index 00000000..996f5722 --- /dev/null +++ b/server/migrations/versions/a7122bc0b2ca_add_shared_rooms.py @@ -0,0 +1,40 @@ +"""Add shared rooms + +Revision ID: a7122bc0b2ca +Revises: 74b2b0236931 +Create Date: 2024-10-04 16:41:28.841889 + +""" + +from typing import Sequence, Union + +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision: str = "a7122bc0b2ca" +down_revision: Union[str, None] = "74b2b0236931" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column( + "room", + sa.Column( + "is_shared", + sa.Boolean(), + server_default=sa.text("0"), + nullable=False, + ), + ) + + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column("room", "is_shared") + + # ### end Alembic commands ### diff --git a/server/reflector/db/rooms.py b/server/reflector/db/rooms.py index 5dc09542..09eb53b5 100644 --- a/server/reflector/db/rooms.py +++ b/server/reflector/db/rooms.py @@ -36,6 +36,9 @@ rooms = sqlalchemy.Table( nullable=False, server_default="automatic-2nd-participant", ), + sqlalchemy.Column( + "is_shared", sqlalchemy.Boolean, nullable=False, server_default=false() + ), ) @@ -53,6 +56,7 @@ class Room(BaseModel): recording_trigger: Literal[ "none", "prompt", "automatic", "automatic-2nd-participant" ] = "automatic-2nd-participant" + is_shared: bool = False class RoomController: @@ -98,6 +102,7 @@ class RoomController: room_mode: str, recording_type: str, recording_trigger: str, + is_shared: bool, ): """ Add a new room @@ -112,6 +117,7 @@ class RoomController: room_mode=room_mode, recording_type=recording_type, recording_trigger=recording_trigger, + is_shared=is_shared, ) query = rooms.insert().values(**room.model_dump()) try: diff --git a/server/reflector/db/transcripts.py b/server/reflector/db/transcripts.py index 2f8999c0..2459b239 100644 --- a/server/reflector/db/transcripts.py +++ b/server/reflector/db/transcripts.py @@ -1,3 +1,4 @@ +import enum import json import os import shutil @@ -14,8 +15,16 @@ from reflector.db import database, metadata from reflector.processors.types import Word as ProcessorWord from reflector.settings import settings from reflector.storage import Storage +from sqlalchemy import Enum from sqlalchemy.sql import false + +class SourceKind(enum.StrEnum): + ROOM = enum.auto() + LIVE = enum.auto() + FILE = enum.auto() + + transcripts = sqlalchemy.Table( "transcript", metadata, @@ -55,6 +64,11 @@ transcripts = sqlalchemy.Table( sqlalchemy.String, ), sqlalchemy.Column("zulip_message_id", sqlalchemy.Integer, nullable=True), + sqlalchemy.Column( + "source_kind", + Enum(SourceKind, values_callable=lambda obj: [e.value for e in obj]), + nullable=False, + ), ) @@ -152,6 +166,7 @@ class Transcript(BaseModel): reviewed: bool = False meeting_id: str | None = None zulip_message_id: int | None = None + source_kind: SourceKind def add_event(self, event: str, data: BaseModel) -> TranscriptEvent: ev = TranscriptEvent(event=event, data=data.model_dump()) @@ -291,6 +306,9 @@ class TranscriptController: order_by: str | None = None, filter_empty: bool | None = False, filter_recording: bool | None = False, + source_kind: SourceKind | None = None, + room_id: str | None = None, + search_term: str | None = None, return_query: bool = False, ) -> list[Transcript]: """ @@ -303,8 +321,39 @@ class TranscriptController: - `order_by`: field to order by, e.g. "-created_at" - `filter_empty`: filter out empty transcripts - `filter_recording`: filter out transcripts that are currently recording + - `room_id`: filter transcripts by room ID + - `search_term`: filter transcripts by search term """ - query = transcripts.select().where(transcripts.c.user_id == user_id) + from reflector.db.meetings import meetings + from reflector.db.rooms import rooms + + query = ( + transcripts.select() + .join(meetings, transcripts.c.meeting_id == meetings.c.id, isouter=True) + .join(rooms, meetings.c.room_id == rooms.c.id, isouter=True) + ) + + if user_id: + query = query.where(transcripts.c.user_id == user_id) + + if source_kind: + query = query.where(transcripts.c.source_kind == source_kind) + + if room_id: + query = query.where(rooms.c.id == room_id) + + if search_term: + query = query.where( + transcripts.c.title.ilike(f"%{search_term}%") + ) # Assuming there's a 'title' column + + query = query.with_only_columns( + [ + transcripts, + rooms.c.id.label("room_id"), + rooms.c.name.label("room_name"), + ] + ) if order_by is not None: field = getattr(transcripts.c, order_by[1:]) @@ -392,6 +441,7 @@ class TranscriptController: async def add( self, name: str, + source_kind: SourceKind, source_language: str = "en", target_language: str = "en", user_id: str | None = None, @@ -403,6 +453,7 @@ class TranscriptController: """ transcript = Transcript( name=name, + source_kind=source_kind, source_language=source_language, target_language=target_language, user_id=user_id, diff --git a/server/reflector/views/rooms.py b/server/reflector/views/rooms.py index 50570ff7..03a73450 100644 --- a/server/reflector/views/rooms.py +++ b/server/reflector/views/rooms.py @@ -28,6 +28,7 @@ class Room(BaseModel): room_mode: str recording_type: str recording_trigger: str + is_shared: bool class Meeting(BaseModel): @@ -49,6 +50,7 @@ class CreateRoom(BaseModel): room_mode: str recording_type: str recording_trigger: str + is_shared: bool class UpdateRoom(BaseModel): @@ -60,6 +62,7 @@ class UpdateRoom(BaseModel): room_mode: str recording_type: str recording_trigger: str + is_shared: bool class DeletionStatus(BaseModel): @@ -100,6 +103,7 @@ async def rooms_create( room_mode=room.room_mode, recording_type=room.recording_type, recording_trigger=room.recording_trigger, + is_shared=room.is_shared, ) diff --git a/server/reflector/views/transcripts.py b/server/reflector/views/transcripts.py index c9551eef..f51f5aca 100644 --- a/server/reflector/views/transcripts.py +++ b/server/reflector/views/transcripts.py @@ -9,6 +9,7 @@ from jose import jwt from pydantic import BaseModel, Field from reflector.db.migrate_user import migrate_user from reflector.db.transcripts import ( + SourceKind, TranscriptParticipant, TranscriptTopic, transcripts_controller, @@ -59,6 +60,9 @@ class GetTranscript(BaseModel): participants: list[TranscriptParticipant] | None reviewed: bool meeting_id: str | None + source_kind: SourceKind + room_id: str | None = None + room_name: str | None = None class CreateTranscript(BaseModel): @@ -85,6 +89,9 @@ class DeletionStatus(BaseModel): @router.get("/transcripts", response_model=Page[GetTranscript]) async def transcripts_list( user: Annotated[Optional[auth.UserInfo], Depends(auth.current_user_optional)], + source_kind: SourceKind | None = None, + room_id: str | None = None, + search_term: str | None = None, ): from reflector.db import database @@ -101,6 +108,9 @@ async def transcripts_list( database, await transcripts_controller.get_all( user_id=user_id, + source_kind=SourceKind(source_kind) if source_kind else None, + room_id=room_id, + search_term=search_term, order_by="-created_at", return_query=True, ), @@ -115,6 +125,7 @@ async def transcripts_create( user_id = user["sub"] if user else None return await transcripts_controller.add( info.name, + source_kind=SourceKind.LIVE, source_language=info.source_language, target_language=info.target_language, user_id=user_id, diff --git a/server/reflector/worker/process.py b/server/reflector/worker/process.py index 614f7c4c..c0281737 100644 --- a/server/reflector/worker/process.py +++ b/server/reflector/worker/process.py @@ -8,7 +8,7 @@ import structlog from celery import shared_task from celery.utils.log import get_task_logger from reflector.db.meetings import meetings_controller -from reflector.db.transcripts import transcripts_controller +from reflector.db.transcripts import SourceKind, transcripts_controller from reflector.pipelines.main_live_pipeline import asynctask, task_pipeline_process from reflector.settings import settings @@ -66,6 +66,7 @@ async def process_recording(bucket_name: str, object_key: str): meeting = await meetings_controller.get_by_room_name(room_name) transcript = await transcripts_controller.add( "", + source_kind=SourceKind.ROOM, source_language="en", target_language="en", user_id=meeting.user_id, diff --git a/www/app/(app)/browse/page.tsx b/www/app/(app)/browse/page.tsx index 7b4b10dd..5595cdcd 100644 --- a/www/app/(app)/browse/page.tsx +++ b/www/app/(app)/browse/page.tsx @@ -1,50 +1,70 @@ "use client"; -import React, { useEffect, useState } from "react"; - -import { GetTranscript } from "../../api"; -import Pagination from "./pagination"; -import NextLink from "next/link"; -import { FaArrowRotateRight, FaGear } from "react-icons/fa6"; -import { FaCheck, FaTrash, FaStar, FaMicrophone } from "react-icons/fa"; -import { MdError } from "react-icons/md"; -import useTranscriptList from "../transcripts/useTranscriptList"; -import { formatTimeMs } from "../../lib/time"; -import useApi from "../../lib/useApi"; -import { useError } from "../../(errors)/errorContext"; -import { FaEllipsisVertical } from "react-icons/fa6"; -import useSessionUser from "../../lib/useSessionUser"; +import React, { useState, useEffect } from "react"; import { Flex, Spinner, Heading, - Button, - Card, - Link, - CardBody, - Stack, + Box, Text, + Link, + Stack, + Table, + Thead, + Tbody, + Tr, + Th, + Td, + Button, + Divider, + Input, Icon, - Grid, - IconButton, - Spacer, + Tooltip, Menu, MenuButton, - MenuItem, MenuList, + MenuItem, + IconButton, AlertDialog, AlertDialogOverlay, AlertDialogContent, AlertDialogHeader, AlertDialogBody, AlertDialogFooter, - Tooltip, + Spacer, } from "@chakra-ui/react"; -import { PlusSquareIcon } from "@chakra-ui/icons"; -import { ExpandableText } from "../../lib/expandableText"; +import { + FaCheck, + FaTrash, + FaStar, + FaMicrophone, + FaGear, + FaEllipsisVertical, + FaArrowRotateRight, +} from "react-icons/fa6"; +import useTranscriptList from "../transcripts/useTranscriptList"; +import useSessionUser from "../../lib/useSessionUser"; +import NextLink from "next/link"; +import { Room, GetTranscript } from "../../api"; +import Pagination from "./pagination"; +import { formatTimeMs } from "../../lib/time"; +import useApi from "../../lib/useApi"; +import { useError } from "../../(errors)/errorContext"; +import { SourceKind } from "../../api"; export default function TranscriptBrowser() { - const [page, setPage] = useState(1); - const { loading, response, refetch } = useTranscriptList(page); + const [selectedSourceKind, setSelectedSourceKind] = + useState(null); + const [selectedRoomId, setSelectedRoomId] = useState(""); + const [rooms, setRooms] = useState([]); + const [page, setPage] = useState(1); + const [searchTerm, setSearchTerm] = useState(""); + const [searchInputValue, setSearchInputValue] = useState(""); + const { loading, response, refetch } = useTranscriptList( + page, + selectedSourceKind, + selectedRoomId, + searchTerm, + ); const userName = useSessionUser().name; const [deletionLoading, setDeletionLoading] = useState(false); const api = useApi(); @@ -54,10 +74,48 @@ export default function TranscriptBrowser() { React.useState(); const [deletedItemIds, setDeletedItemIds] = React.useState(); + const myRooms = rooms.filter((room) => !room.is_shared); + const sharedRooms = rooms.filter((room) => room.is_shared); + useEffect(() => { setDeletedItemIds([]); }, [page, response]); + useEffect(() => { + refetch(); + }, [selectedRoomId, page, searchTerm]); + + useEffect(() => { + if (!api) return; + api + .v1RoomsList({ page: 1 }) + .then((rooms) => setRooms(rooms.items)) + .catch((err) => setError(err, "There was an error fetching the rooms")); + }, [api]); + + const handleFilterTranscripts = ( + sourceKind: SourceKind | null, + roomId: string, + ) => { + setSelectedSourceKind(sourceKind); + setSelectedRoomId(roomId); + setPage(1); + }; + + const handleSearch = () => { + setPage(1); + setSearchTerm(searchInputValue); + setSelectedSourceKind(null); + setSelectedRoomId(""); + refetch(); + }; + + const handleKeyDown = (event) => { + if (event.key === "Enter") { + handleSearch(); + } + }; + if (loading && !response) return ( @@ -77,6 +135,7 @@ export default function TranscriptBrowser() { ); + const onCloseDeletion = () => setTranscriptToDeleteId(undefined); const handleDeleteTranscript = (transcriptId) => (e) => { @@ -88,7 +147,6 @@ export default function TranscriptBrowser() { .then(() => { refetch(); setDeletionLoading(false); - refetch(); onCloseDeletion(); setDeletedItemIds((deletedItemIds) => [ deletedItemIds, @@ -120,169 +178,203 @@ export default function TranscriptBrowser() { }); } }; + return ( - - {userName ? ( - {userName}'s Meetings - ) : ( - Your meetings - )} - - {loading || (deletionLoading && )} - - - + + + {userName ? `${userName}'s Transcriptions` : "Your Transcriptions"}{" "} + {loading || (deletionLoading && )} + - - {response?.items - .filter((i) => !deletedItemIds?.includes(i.id)) - .map((item: GetTranscript) => ( - - - - - - {item.title || item.name || "Unnamed Transcript"} - - - - - } - aria-label="actions" - /> - - setTranscriptToDeleteId(item.id)} - icon={} - > - Delete - - } - > - Process - - - - - - Delete{" "} - {item.title || item.name || "Unnamed Transcript"} - + + + + handleFilterTranscripts(null, "")} + color={selectedSourceKind === null ? "blue.500" : "gray.600"} + _hover={{ color: "blue.300" }} + fontWeight={selectedSourceKind === null ? "bold" : "normal"} + > + All Transcripts + - - Are you sure? You can't undo this action - afterwards. - + - - - - - - - - - - - - - {item.status == "ended" && ( - - - - - - )} - {item.status == "error" && ( - - - - - - )} - {item.status == "idle" && ( - - - - - - )} - {item.status == "processing" && ( - - - - - - )} - {item.status == "recording" && ( - - - - - - )} - + {myRooms.length > 0 && ( + <> + My Rooms + + {myRooms.map((room) => ( + handleFilterTranscripts("room", room.id)} + color={ + selectedSourceKind === "room" && + selectedRoomId === room.id + ? "blue.500" + : "gray.600" + } + _hover={{ color: "blue.300" }} + fontWeight={ + selectedSourceKind === "room" && + selectedRoomId === room.id + ? "bold" + : "normal" + } + ml={4} + > + {room.name} + + ))} + + )} + + {sharedRooms.length > 0 && ( + <> + Shared Rooms + + {sharedRooms.map((room) => ( + handleFilterTranscripts("room", room.id)} + color={ + selectedSourceKind === "room" && + selectedRoomId === room.id + ? "blue.500" + : "gray.600" + } + _hover={{ color: "blue.300" }} + fontWeight={ + selectedSourceKind === "room" && + selectedRoomId === room.id + ? "bold" + : "normal" + } + ml={4} + > + {room.name} + + ))} + + )} + + + handleFilterTranscripts("live", "")} + color={selectedSourceKind === "live" ? "blue.500" : "gray.600"} + _hover={{ color: "blue.300" }} + fontWeight={selectedSourceKind === "live" ? "bold" : "normal"} + > + Live Transcripts + + handleFilterTranscripts("file", "")} + color={selectedSourceKind === "file" ? "blue.500" : "gray.600"} + _hover={{ color: "blue.300" }} + fontWeight={selectedSourceKind === "file" ? "bold" : "normal"} + > + Uploaded Files + + + + + + + setSearchInputValue(e.target.value)} + onKeyDown={handleKeyDown} + /> + + + + + + + + + + + + + + + {response?.items?.map((item: GetTranscript) => ( + + + + + + + + ))} + +
+ Transcription Title + SourceDateDuration
+ + {item.status === "ended" && ( + + + + + + )} + {item.status === "error" && ( + + + + + + )} + {item.status === "idle" && ( + + + + + + )} + {item.status === "processing" && ( + + + + + + )} + {item.status === "recording" && ( + + + + + + )} + + {item.title || "Unnamed Transcript"} + + + + {item.source_kind === "room" + ? item.room_name + : item.source_kind} + {new Date(item.created_at).toLocaleString("en-US", { year: "numeric", month: "long", @@ -290,18 +382,146 @@ export default function TranscriptBrowser() { hour: "numeric", minute: "numeric", })} - {"\u00A0"}-{"\u00A0"} - {formatTimeMs(item.duration)} - + {formatTimeMs(item.duration)} + + } + variant="outline" + aria-label="Options" + /> + + + Delete + + + Reprocess + + + +
+
+ + + {response?.items?.map((item: GetTranscript) => ( + + + + {item.status === "ended" && ( + + + + + + )} + {item.status === "error" && ( + + + + + + )} + {item.status === "idle" && ( + + + + + + )} + {item.status === "processing" && ( + + + + + + )} + {item.status === "recording" && ( + + + + + + )} + + + + {item.title || "Unnamed Transcript"} + + + Source:{" "} + {item.source_kind === "room" + ? item.room_name + : item.source_kind} + + + Date: {new Date(item.created_at).toLocaleString()} + + Duration: {formatTimeMs(item.duration)} + + + } + variant="outline" + aria-label="Options" + /> + + + Delete + + + Reprocess + + + - - {item.short_summary} - - -
-
- ))} -
+ + ))} + + + +
+
+ + + + + + Delete Transcript + + + Are you sure? You can't undo this action afterwards. + + + + + + + + ); } diff --git a/www/app/(app)/rooms/page.tsx b/www/app/(app)/rooms/page.tsx index d8a7f960..77d3cec3 100644 --- a/www/app/(app)/rooms/page.tsx +++ b/www/app/(app)/rooms/page.tsx @@ -65,6 +65,7 @@ const roomInitialState = { roomMode: "normal", recordingType: "cloud", recordingTrigger: "automatic-2nd-participant", + isShared: false, }; export default function RoomsList() { @@ -159,6 +160,7 @@ export default function RoomsList() { room_mode: room.roomMode, recording_type: room.recordingType, recording_trigger: room.recordingTrigger, + is_shared: room.isShared, }; if (isEditing) { @@ -203,6 +205,7 @@ export default function RoomsList() { roomMode: roomData.room_mode, recordingType: roomData.recording_type, recordingTrigger: roomData.recording_trigger, + isShared: roomData.is_shared, }); setEditRoomId(roomId); setIsEditing(true); @@ -236,6 +239,11 @@ export default function RoomsList() { }); }; + const myRooms = + response?.items.filter((roomData) => !roomData.is_shared) || []; + const sharedRooms = + response?.items.filter((roomData) => roomData.is_shared) || []; + if (loading && !response) return ( @@ -375,6 +383,15 @@ export default function RoomsList() { isDisabled={!room.zulipAutoPost} /> + + + Shared room + + @@ -396,9 +413,10 @@ export default function RoomsList() { - - {response?.items && response.items.length > 0 ? ( - response.items.map((roomData) => ( + + My Rooms + {myRooms.length > 0 ? ( + myRooms.map((roomData) => ( @@ -445,9 +463,61 @@ export default function RoomsList() { )) ) : ( - - No rooms found - + No rooms found + )} + + + + Shared Rooms + {sharedRooms.length > 0 ? ( + sharedRooms.map((roomData) => ( + + + + + {roomData.name} + + + {linkCopied === roomData.name ? ( + + Link copied! + + ) : ( + } + onClick={() => handleCopyUrl(roomData.name)} + mr={2} + /> + )} + + + } + aria-label="actions" + /> + + handleEditRoom(roomData.id, roomData)} + icon={} + > + Edit + + handleDeleteRoom(roomData.id)} + icon={} + > + Delete + + + + + + + )) + ) : ( + No shared rooms found )} diff --git a/www/app/(app)/transcripts/useTranscriptList.ts b/www/app/(app)/transcripts/useTranscriptList.ts index d0f62eee..e7bf2dd0 100644 --- a/www/app/(app)/transcripts/useTranscriptList.ts +++ b/www/app/(app)/transcripts/useTranscriptList.ts @@ -1,7 +1,7 @@ import { useEffect, useState } from "react"; import { useError } from "../../(errors)/errorContext"; import useApi from "../../lib/useApi"; -import { Page_GetTranscript_ } from "../../api"; +import { Page_GetTranscript_, SourceKind } from "../../api"; type TranscriptList = { response: Page_GetTranscript_ | null; @@ -10,7 +10,12 @@ type TranscriptList = { refetch: () => void; }; -const useTranscriptList = (page: number): TranscriptList => { +const useTranscriptList = ( + page: number, + sourceKind: SourceKind | null, + roomId: string | null, + searchTerm: string | null, +): TranscriptList => { const [response, setResponse] = useState(null); const [loading, setLoading] = useState(true); const [error, setErrorState] = useState(null); @@ -27,7 +32,12 @@ const useTranscriptList = (page: number): TranscriptList => { if (!api) return; setLoading(true); api - .v1TranscriptsList({ page }) + .v1TranscriptsList({ + page, + sourceKind, + roomId, + searchTerm, + }) .then((response) => { setResponse(response); setLoading(false); @@ -38,7 +48,7 @@ const useTranscriptList = (page: number): TranscriptList => { setError(err); setErrorState(err); }); - }, [!api, page, refetchCount]); + }, [!api, page, refetchCount, roomId, searchTerm]); return { response, loading, error, refetch }; }; diff --git a/www/app/api/schemas.gen.ts b/www/app/api/schemas.gen.ts index 4327ba02..d0ada862 100644 --- a/www/app/api/schemas.gen.ts +++ b/www/app/api/schemas.gen.ts @@ -263,6 +263,31 @@ export const $GetTranscript = { ], title: "Meeting Id", }, + room_id: { + anyOf: [ + { + type: "string", + }, + { + type: "null", + }, + ], + title: "Room Id", + }, + room_name: { + anyOf: [ + { + type: "string", + }, + { + type: "null", + }, + ], + title: "Room Name", + }, + source_kind: { + $ref: "#/components/schemas/SourceKind", + }, }, type: "object", required: [ @@ -281,6 +306,9 @@ export const $GetTranscript = { "participants", "reviewed", "meeting_id", + "room_id", + "room_name", + "source_kind", ], title: "GetTranscript", } as const; @@ -708,6 +736,10 @@ export const $Room = { type: "string", title: "Recording Trigger", }, + is_shared: { + type: "boolean", + title: "Is Shared", + }, }, type: "object", required: [ @@ -722,6 +754,7 @@ export const $Room = { "room_mode", "recording_type", "recording_trigger", + "is_shared", ], title: "Room", } as const; @@ -742,6 +775,12 @@ export const $RtcOffer = { title: "RtcOffer", } as const; +export const $SourceKind = { + type: "string", + enum: ["room", "live", "file"], + title: "SourceKind", +} as const; + export const $SpeakerAssignment = { properties: { speaker: { diff --git a/www/app/api/services.gen.ts b/www/app/api/services.gen.ts index 179d8bb6..f3e07a30 100644 --- a/www/app/api/services.gen.ts +++ b/www/app/api/services.gen.ts @@ -199,18 +199,24 @@ export class DefaultService { /** * Transcripts List * @param data The data for the request. + * @param data.roomId + * @param data.searchTerm + * @param data.sourceKind * @param data.page Page number * @param data.size Page size * @returns Page_GetTranscript_ Successful Response * @throws ApiError */ public v1TranscriptsList( - data: V1TranscriptsListData = {}, + data: V1TranscriptsListData, ): CancelablePromise { return this.httpRequest.request({ method: "GET", url: "/v1/transcripts", query: { + room_id: data.roomId, + search_term: data.searchTerm, + source_kind: data.sourceKind, page: data.page, size: data.size, }, diff --git a/www/app/api/types.gen.ts b/www/app/api/types.gen.ts index 269b8a6b..ba00e0a0 100644 --- a/www/app/api/types.gen.ts +++ b/www/app/api/types.gen.ts @@ -52,6 +52,9 @@ export type GetTranscript = { participants: Array | null; reviewed: boolean; meeting_id: string | null; + room_id: string | null; + room_name: string | null; + source_kind: SourceKind; }; export type GetTranscriptSegmentTopic = { @@ -140,6 +143,7 @@ export type Room = { room_mode: string; recording_type: string; recording_trigger: string; + is_shared: boolean; }; export type RtcOffer = { @@ -147,6 +151,8 @@ export type RtcOffer = { type: string; }; +export type SourceKind = "room" | "live" | "file"; + export type SpeakerAssignment = { speaker?: number | null; participant?: string | null; @@ -274,10 +280,13 @@ export type V1TranscriptsListData = { * Page number */ page?: number; + roomId: string | null; + searchTerm: string | null; /** * Page size */ size?: number; + sourceKind?: SourceKind | null; }; export type V1TranscriptsListResponse = Page_GetTranscript_;