22import uuid
33import asyncio
44import logging
5- from typing import Optional , Dict , Set , List
5+ from typing import Optional , List
66
77import aioice
88import aiortc
2525from twirp import context
2626
2727from getstream .video .rtc .coordinator import join_call_coordinator_request
28- from getstream .video .rtc .recording import AudioRecorder , RecordingType
28+ from getstream .video .rtc .recording import RecordingManager , RecordingType
29+
2930
3031from getstream .video .rtc .track_util import (
3132 BufferedMediaTrack ,
@@ -126,8 +127,7 @@ def __init__(self, call: Call, user_id: str = None, create: bool = True, **kwarg
126127
127128 self .participants_state = ParticipantsState ()
128129
129- # Audio recording functionality
130- self .audio_recorder = AudioRecorder ()
130+ self .recording_manager = RecordingManager ()
131131
132132 async def _full_connect (self ):
133133 """Perform location discovery and join call via coordinator.
@@ -257,6 +257,8 @@ async def on_audio(pcm_data, user):
257257 )
258258 self .ws_client .on_event ("ice_trickle" , self ._on_ice_trickle )
259259 self .ws_client .on_event ("subscriber_offer" , self ._on_subscriber_offer )
260+ self .ws_client .on_event ("participant_left" , self ._on_participant_left )
261+
260262 # Mark as running and clear stop event
261263 self .running = True
262264 self ._stop_event .clear ()
@@ -279,8 +281,7 @@ async def leave(self):
279281 self .running = False
280282 self ._stop_event .set () # Signal the iterator to stop
281283
282- # Stop recording if active
283- self .audio_recorder .cleanup ()
284+ await self .recording_manager .cleanup ()
284285
285286 if self .ws_client :
286287 self .ws_client .close ()
@@ -727,9 +728,8 @@ async def add_video_track(self, track: aiortc.mediastreams.MediaStreamTrack):
727728
728729 await self .add_tracks (video = track )
729730
730- # Recording functionality methods
731-
732- def start_recording (
731+
732+ async def start_recording (
733733 self ,
734734 recording_types : List [RecordingType ],
735735 user_ids : Optional [List [str ]] = None ,
@@ -739,13 +739,14 @@ def start_recording(
739739 Start recording audio tracks.
740740
741741 Args:
742- recording_types: List of recording types to start (INDIVIDUAL , COMPOSITE)
742+ recording_types: List of recording types to start (TRACK , COMPOSITE)
743743 user_ids: Optional list of specific user IDs to record (None = all users)
744744 output_dir: Directory to save recording files
745745 """
746- self .audio_recorder .start_recording (recording_types , user_ids , output_dir )
746+ logger .info ("Starting recording" )
747+ await self .recording_manager .start_recording (recording_types , user_ids , output_dir )
747748
748- def stop_recording (
749+ async def stop_recording (
749750 self ,
750751 recording_types : Optional [List [RecordingType ]] = None ,
751752 user_ids : Optional [List [str ]] = None
@@ -757,23 +758,36 @@ def stop_recording(
757758 recording_types: Optional list of recording types to stop (None = stop all)
758759 user_ids: Optional specific user IDs to stop recording (None = stop all users)
759760 """
760- self .audio_recorder .stop_recording (recording_types , user_ids )
761+ await self .recording_manager .stop_recording (recording_types , user_ids )
761762
762763 def _record_audio_data (self , pcm_data , user_id : str ):
763764 """
764- Record audio data for a specific user and/or composite.
765+ Record audio data for a specific user
765766
766767 Args:
767768 pcm_data: PCM audio data (can be bytes, PcmData object, or numpy array)
768769 user_id: ID of the user whose audio this is
769770 """
770- self .audio_recorder .record_audio_data (pcm_data , user_id )
771+ self .recording_manager .record_audio_data (pcm_data , user_id )
771772
772773 @property
773774 def is_recording (self ) -> bool :
774775 """Check if recording is currently active."""
775- return self .audio_recorder .is_recording
776+ return self .recording_manager .is_recording
776777
777778 def get_recording_status (self ) -> dict :
778779 """Get current recording status and information."""
779- return self .audio_recorder .get_recording_status ()
780+ return self .recording_manager .get_recording_status ()
781+
782+ async def _on_participant_left (self , event ):
783+ """Handle participant leaving - cleanup their recording if active."""
784+ try :
785+ await self .participants_state ._on_participant_left (event )
786+
787+ # Stop recording for the user who left
788+ if hasattr (event , 'participant' ) and hasattr (event .participant , 'user_id' ):
789+ user_id = event .participant .user_id
790+ await self .recording_manager .on_user_left (user_id )
791+
792+ except Exception as e :
793+ logger .error (f"Error handling participant left event: { e } " )
0 commit comments