diff --git a/application/logic/app_batch_processor.py b/application/logic/app_batch_processor.py index afd72ec5..c927f3f3 100644 --- a/application/logic/app_batch_processor.py +++ b/application/logic/app_batch_processor.py @@ -147,6 +147,30 @@ def resume_batch_processing(self): def is_batch_paused(self) -> bool: return self.app.is_batch_processing_active and self.app.pause_batch_event.is_set() + def _set_mpv_audio_enabled(self, enabled: bool) -> None: + """Best-effort toggle for embedded mpv audio output.""" + gui = getattr(self.app, "gui_instance", None) + disp = getattr(gui, "mpv_display", None) if gui else None + if not disp: + return + try: + disp.set_audio_enabled(bool(enabled)) + self.app.logger.info(f"Batch: mpv audio {'enabled' if enabled else 'disabled'}") + except Exception as e: + self.app.logger.debug(f"Batch: set mpv audio failed: {e}") + + @staticmethod + def _should_disable_mpv_audio(runtime_category, selected_mode: str) -> bool: + """Disable mpv audio only for the problematic offline VR hybrid batch mode.""" + try: + from config.tracker_discovery import TrackerCategory + return ( + runtime_category == TrackerCategory.OFFLINE + and selected_mode == "OFFLINE_VR_HYBRID_CHAPTER" + ) + except Exception: + return False + # --- Adaptive Batch Tuning Methods --- def _init_adaptive_tuning(self) -> AdaptiveTuningState: @@ -244,6 +268,7 @@ def _run_batch_processing_thread(self): batch_failures = [] # (video_path, reason, timestamp) + restore_mpv_audio_enabled = bool(self.app.app_settings.config.audio.enabled) try: for i, video_data in enumerate(self.app.batch_video_paths): if self.app.stop_batch_event.is_set(): @@ -315,15 +340,6 @@ def _run_batch_processing_thread(self): f"Processing '{video_basename}': Mode 2 selected, will process regardless of funscript existence or version.") # --- End of pre-flight checks --- - open_success = self.app.file_manager.open_video_from_path(video_path) - if not open_success: - self.app.logger.error(f"Failed to open video, skipping: {video_path}") - batch_failures.append((video_path, "Failed to open video", datetime.now().isoformat())) - continue - - time.sleep(1.0) - if self.app.stop_batch_event.is_set(): break - # Use the dynamically selected tracker name discovery = get_tracker_discovery() @@ -351,6 +367,17 @@ def _run_batch_processing_thread(self): # would skip them here otherwise. from config.tracker_discovery import TrackerCategory runtime_category = discovery.get_runtime_category(self.app.batch_tracker_name) + disable_mpv_audio = self._should_disable_mpv_audio(runtime_category, selected_mode) + self._set_mpv_audio_enabled(not disable_mpv_audio) + + open_success = self.app.file_manager.open_video_from_path(video_path) + if not open_success: + self.app.logger.error(f"Failed to open video, skipping: {video_path}") + batch_failures.append((video_path, "Failed to open video", datetime.now().isoformat())) + continue + + time.sleep(1.0) + if self.app.stop_batch_event.is_set(): break # --- OFFLINE MODES (Stage-based processing) --- if runtime_category == TrackerCategory.OFFLINE: @@ -498,6 +525,7 @@ def _run_batch_processing_thread(self): self.app.logger.error(f"An error occurred during the batch process: {e}", exc_info=True) batch_failures.append(("(batch process)", str(e), datetime.now().isoformat())) finally: + self._set_mpv_audio_enabled(restore_mpv_audio_enabled) # Write batch error report if any failures occurred if batch_failures: self._write_batch_error_report(batch_failures) diff --git a/video/mpv_display.py b/video/mpv_display.py index 82ebae80..60a87419 100644 --- a/video/mpv_display.py +++ b/video/mpv_display.py @@ -251,6 +251,16 @@ def load(self, video_path: str, vf: Optional[str] = None) -> bool: self._loaded = False return False + def set_audio_enabled(self, enabled: bool) -> None: + """Toggle libmpv audio output at runtime.""" + self.with_audio = bool(enabled) + if self._player is None: + return + try: + self._player["audio"] = "auto" if self.with_audio else "no" + except Exception as e: + self.logger.debug(f"set_audio_enabled failed: {e}") + def set_fps_fallback(self, fps: float) -> None: try: f = float(fps) diff --git a/video/mpv_display_gl.py b/video/mpv_display_gl.py index efd2efde..0fbe2576 100644 --- a/video/mpv_display_gl.py +++ b/video/mpv_display_gl.py @@ -312,6 +312,20 @@ def load(self, video_path: str, vf: Optional[str] = None, self._loaded = False return False + def set_audio_enabled(self, enabled: bool) -> None: + """Toggle libmpv audio output at runtime. + + Batch/offline processing can disable mpv audio to avoid WASAPI device + invalidation from interrupting long unattended runs. + """ + self.with_audio = bool(enabled) + if self._player is None: + return + try: + self._player["audio"] = "auto" if self.with_audio else "no" + except Exception as e: + self.logger.debug(f"set_audio_enabled failed: {e}") + def set_fps_fallback(self, fps: float) -> None: try: f = float(fps)