Skip to content

Commit 015ab0f

Browse files
committed
feat: add RTStream export and improve capture API
- Add export() method to RTStream for exporting recordings as video/audio assets - Add RTStreamExportResult class to hold export metadata - Add store parameter to create_rtstream() for enabling recording storage - Rename capture methods for consistency: - start_capture_session -> start_session - stop_capture -> stop_session
1 parent 4d93eff commit 015ab0f

4 files changed

Lines changed: 67 additions & 2 deletions

File tree

videodb/_constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ class ApiPath:
109109
session = "session"
110110
token = "token"
111111
websocket = "websocket"
112+
export = "export"
112113

113114

114115
class Status:

videodb/capture.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,7 @@ async def list_channels(self) -> Channels:
356356

357357
return Channels(mics=mics, displays=displays, system_audio=system_audio)
358358

359-
async def start_capture_session(
359+
async def start_session(
360360
self,
361361
capture_session_id: str,
362362
channels: List[Channel],
@@ -385,7 +385,7 @@ async def start_capture_session(
385385

386386
await self._send_command("startRecording", payload)
387387

388-
async def stop_capture(self) -> None:
388+
async def stop_session(self) -> None:
389389
"""Stop the current recording session."""
390390
if not self._session_id:
391391
raise RuntimeError("No active capture session to stop.")

videodb/collection.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ def connect_rtstream(
174174
name: str,
175175
media_types: List[str] = None,
176176
sample_rate: int = None,
177+
store: bool = None,
177178
enable_transcript: bool = None,
178179
ws_connection_id: str = None,
179180
) -> RTStream:
@@ -184,6 +185,8 @@ def connect_rtstream(
184185
:param list media_types: List of media types to capture (default: [MediaType.video]).
185186
Valid values: :attr:`MediaType.audio`, :attr:`MediaType.video`
186187
:param int sample_rate: Sample rate of the rtstream (optional, server default: 30)
188+
:param bool store: Enable recording storage (optional, default: False).
189+
When True, the stream recording is stored and can be exported via :meth:`RTStream.export`.
187190
:param bool enable_transcript: Enable real-time transcription (optional)
188191
:param str ws_connection_id: WebSocket connection ID for receiving events (optional)
189192
:return: :class:`RTStream <RTStream>` object
@@ -206,6 +209,8 @@ def connect_rtstream(
206209
}
207210
if sample_rate is not None:
208211
data["sample_rate"] = sample_rate
212+
if store is not None:
213+
data["store"] = store
209214
if enable_transcript is not None:
210215
data["enable_transcript"] = enable_transcript
211216
if ws_connection_id is not None:

videodb/rtstream.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,39 @@ def get_shots(self) -> List["RTStreamShot"]:
3939
return self.shots
4040

4141

42+
class RTStreamExportResult:
43+
"""Result of exporting an RTStream recording.
44+
45+
:ivar str video_id: ID of the exported video or audio asset
46+
:ivar str stream_url: URL to stream the exported asset (may be None for audio)
47+
:ivar str player_url: URL to play the exported asset in a player (may be None for audio)
48+
:ivar str name: Name of the exported recording
49+
:ivar float duration: Duration of the exported recording in seconds (may be None on idempotent calls)
50+
"""
51+
52+
def __init__(
53+
self,
54+
video_id: str,
55+
stream_url: Optional[str] = None,
56+
player_url: Optional[str] = None,
57+
name: Optional[str] = None,
58+
duration: Optional[float] = None,
59+
) -> None:
60+
self.video_id = video_id
61+
self.stream_url = stream_url
62+
self.player_url = player_url
63+
self.name = name
64+
self.duration = duration
65+
66+
def __repr__(self) -> str:
67+
return (
68+
f"RTStreamExportResult("
69+
f"video_id={self.video_id}, "
70+
f"name={self.name}, "
71+
f"duration={self.duration})"
72+
)
73+
74+
4275
class RTStreamShot:
4376
"""RTStreamShot class for rtstream search results
4477
@@ -326,6 +359,32 @@ def stop(self):
326359
)
327360
self.status = "stopped"
328361

362+
def export(self, name: Optional[str] = None) -> "RTStreamExportResult":
363+
"""Export the latest completed recording as a video or audio asset.
364+
365+
The stream must be stopped before exporting. The call is idempotent:
366+
calling it again returns the same asset without re-ingesting.
367+
368+
:param str name: Name for the exported asset (optional, defaults to "{stream_name} - Recording")
369+
:return: Export result with the asset ID and metadata
370+
:rtype: :class:`RTStreamExportResult`
371+
"""
372+
data = {}
373+
if name is not None:
374+
data["name"] = name
375+
376+
export_data = self._connection.post(
377+
path=f"{ApiPath.rtstream}/{self.id}/{ApiPath.export}",
378+
data=data,
379+
)
380+
return RTStreamExportResult(
381+
video_id=export_data.get("video_id"),
382+
stream_url=export_data.get("stream_url"),
383+
player_url=export_data.get("player_url"),
384+
name=export_data.get("name"),
385+
duration=export_data.get("duration"),
386+
)
387+
329388
def start_transcript(
330389
self, ws_connection_id: Optional[str] = None, engine: str = "assemblyai"
331390
) -> dict:

0 commit comments

Comments
 (0)