having a problem, maybe it is basic misunderstandi...
# egress
a
having a problem, maybe it is basic misunderstanding on my part. My egress handler keeps shutting down before individual voice audio tracks have finished transferring to s3. Can someone tell me maybe what hook I should use so it completes upload before my app finishes? I tried to write my own completion handler but fails. Thanks!
Copy code
async def stop_recording(self) -> AudioFileInfo | None:
        """Stop the active egress recording and wait for upload to complete.

        Returns:
            AudioFileInfo with file details if successful, None on error
        """
        if not self._egress_id:
            logger.debug("No active egress to stop")
            return None

        egress_id = self._egress_id
        try:
            logger.info(f"Stopping egress recording, egress_id={egress_id}...")
            await self.livekit_api.egress.stop_egress(
                egress_proto.StopEgressRequest(egress_id=egress_id)
            )
            logger.info(
                f"Stop request sent for egress_id={egress_id}, waiting for upload to complete..."
            )

            # Wait for the egress to complete and get file info
            file_info = await self._wait_for_completion(egress_id)
            self._egress_id = None
            self._expected_filepath = None
            return file_info

        except Exception as e:
            logger.error(f"Failed to stop egress recording: {e}")
            return None
Copy code
async def _wait_for_completion(
        self,
        egress_id: str,
        timeout: float = 60.0,
        poll_interval: float = 1.0,
        file_results_timeout: float = 10.0,
    ) -> AudioFileInfo | None:
        """Wait for egress to complete and return file info.

        Args:
            egress_id: The egress ID to wait for
            timeout: Maximum time to wait in seconds
            poll_interval: Time between status checks in seconds
            file_results_timeout: Additional time to wait for file_results after
                EGRESS_COMPLETE status (handles S3 upload delay on LiveKit Cloud)

        Returns:
            AudioFileInfo with file details if successful, None on error/timeout
        """
        start_time = asyncio.get_event_loop().time()
        last_status = None
        complete_time: float | None = None  # Track when EGRESS_COMPLETE was first seen

        while True:
            elapsed = asyncio.get_event_loop().time() - start_time
            if elapsed > timeout:
                logger.error(
                    f"Timeout waiting for egress completion after {timeout}s, "
                    f"egress_id={egress_id}, last_status={last_status}"
                )
                return None

            try:
                # List egress by ID to get current status
                response = await self.livekit_api.egress.list_egress(
                    egress_proto.ListEgressRequest(egress_id=egress_id)
                )

                if not response.items:
                    logger.warning(f"Egress {egress_id} not found in list response")
                    await asyncio.sleep(poll_interval)
                    continue

                egress_info = response.items[0]
                status = egress_info.status
                last_status = egress_proto.EgressStatus.Name(status)

                logger.debug(
                    f"Egress status: {last_status}, egress_id={egress_id}, "
                    f"elapsed={elapsed:.1f}s"
                )

                # Check terminal states
                if status == egress_proto.EgressStatus.EGRESS_COMPLETE:
                    # Extract file info from results
                    if egress_info.file_results:
                        file_result = egress_info.file_results[0]
                        file_info = AudioFileInfo(
                            filename=file_result.filename,
                            location=file_result.location,
                            duration=file_result.duration,
                            size=file_result.size,
                        )
                        logger.info(
                            f"Egress completed successfully! "
                            f"File uploaded to: {file_info.location}, "
                            f"filename={file_info.filename}, "
                            f"duration={file_info.duration}ns, "
                            f"size={file_info.size} bytes"
                        )
                        return file_info
                    else:
                        # file_results may not be populated immediately on LiveKit Cloud
                        # Continue polling for a short time to allow S3 upload to complete
                        current_time = asyncio.get_event_loop().time()
                        if complete_time is None:
                            complete_time = current_time
                            # Log detailed egress info for debugging S3 upload issues
                            logger.warning(
                                f"Egress completed but file_results empty - "
                                f"egress_id={egress_info.egress_id}, "
                                f"room_name={egress_info.room_name}, "
                                f"error={egress_info.error or 'None'}, "
                                f"stream_results={egress_info.stream_results}, "
                                f"segment_results={egress_info.segment_results}, "
                                f"waiting up to {file_results_timeout}s for upload"
                            )

                        time_since_complete = current_time - complete_time
                        if time_since_complete >= file_results_timeout:
                            logger.warning(
                                f"Egress completed but no file_results found after "
                                f"{file_results_timeout}s wait, egress_id={egress_id}"
                            )
                            return None

                        # Continue polling for file_results
                        await asyncio.sleep(poll_interval)
                        continue

                elif status == egress_proto.EgressStatus.EGRESS_FAILED:
                    error_msg = egress_info.error or "Unknown error"
                    logger.error(f"Egress failed: {error_msg}, egress_id={egress_id}")
                    return None

                elif status == egress_proto.EgressStatus.EGRESS_ABORTED:
                    logger.warning(f"Egress was aborted, egress_id={egress_id}")
                    return None

                elif status == egress_proto.EgressStatus.EGRESS_LIMIT_REACHED:
                    logger.warning(f"Egress limit reached, egress_id={egress_id}")
                    # Still try to get file info as partial recording may exist
                    if egress_info.file_results:
                        file_result = egress_info.file_results[0]
                        return AudioFileInfo(
                            filename=file_result.filename,
                            location=file_result.location,
                            duration=file_result.duration,
                            size=file_result.size,
                        )
                    return None

                # Still in progress, wait and poll again
                await asyncio.sleep(poll_interval)

            except Exception as e:
                logger.error(f"Error polling egress status: {e}")
                await asyncio.sleep(poll_interval)