@@ -507,7 +507,12 @@ async def stop_recording(self, session: SessionState) -> bool:
507507 return False
508508 session .ffmpeg_process = None
509509
510- session .status = SessionStatus .STOPPING
510+ # Only move to STOPPING if we are still in the RECORDING state.
511+ # handle_session_closed() sets status to CLOSED before calling us;
512+ # overwriting that with STOPPING would prevent _cleanup_session_delayed
513+ # from ever cleaning up the session (it checks status == CLOSED).
514+ if session .status == SessionStatus .RECORDING :
515+ session .status = SessionStatus .STOPPING
511516 session .end_time = datetime .now ()
512517
513518 try :
@@ -528,9 +533,11 @@ async def stop_recording(self, session: SessionState) -> bool:
528533 # 255 is ffmpeg's own graceful-stop exit code (exit_program(255) in its SIGTERM handler).
529534 if rc not in (0 , 255 , - signal .SIGTERM , - signal .SIGKILL ):
530535 logger .error (f"ffmpeg exited with unexpected code { rc } for { session .session_id } " )
536+ session .status = SessionStatus .CLOSED
531537 return False
532538
533539 self .recorded_count += 1
540+ session .status = SessionStatus .CLOSED
534541 duration = session .duration_seconds
535542 logger .info (
536543 f"Stopped recording: session={ session .session_id } , " f"duration={ duration :.1f} s"
@@ -540,6 +547,7 @@ async def stop_recording(self, session: SessionState) -> bool:
540547 return True
541548 except Exception as e :
542549 logger .error (f"Failed to stop recording for { session .session_id } : { e } " )
550+ session .status = SessionStatus .CLOSED
543551 return False
544552
545553 # ==================== Upload Functions ====================
@@ -611,7 +619,12 @@ async def process_upload(self, task: UploadTask) -> None:
611619 except asyncio .TimeoutError :
612620 logger .warning (f"Upload timed out after { self .upload_timeout } s: { task .video_file } , killing process" )
613621 proc .kill ()
614- await proc .communicate ()
622+ _ , stderr_bytes = await proc .communicate ()
623+ if stderr_bytes :
624+ logger .debug (
625+ f"Upload stderr at timeout for { task .video_file } : "
626+ f"{ stderr_bytes .decode (errors = 'replace' ).strip ()} "
627+ )
615628 return
616629 finally :
617630 try :
@@ -641,6 +654,10 @@ async def upload_worker(self) -> None:
641654 logger .warning ("Upload worker cancelled, pending uploads may be lost" )
642655 for t in active_tasks :
643656 t .cancel ()
657+ # Await cancelled tasks so they are not left as orphaned
658+ # asyncio tasks (which causes "Task destroyed but pending" warnings
659+ # and makes the active_uploads kill loop in run() the sole cleanup).
660+ await asyncio .gather (* active_tasks , return_exceptions = True )
644661 raise
645662
646663 # None is the sentinel pushed by cleanup() to signal no more uploads
0 commit comments