diff --git a/main.py b/main.py index df88370..0da589b 100644 --- a/main.py +++ b/main.py @@ -303,10 +303,13 @@ class MainWindow(QMainWindow): self.player.mpv.mute = checked def _seek_relative(self, delta_seconds: float): + # Unlike frame-stepping (which always pauses -- you're precisely + # inspecting a frame), a 5s skip is a coarse nudge while scrubbing + # around; seeking doesn't change mpv's pause state on its own, so + # just not touching play/pause here is enough to preserve whichever + # state the user was already in. if self.input_path is None: return - self.player.pause() - self.play_button.setText("Play") duration = self.player.mpv.duration or 0.0 target = min(max(self.player.get_position() + delta_seconds, 0.0), duration) self.player.seek(target) diff --git a/scripts/smoke_test_preserve_play_state.py b/scripts/smoke_test_preserve_play_state.py new file mode 100644 index 0000000..6e8d82c --- /dev/null +++ b/scripts/smoke_test_preserve_play_state.py @@ -0,0 +1,75 @@ +"""Verifies arrow-key skips preserve whichever play/pause state the user +was already in, instead of always forcing a pause. + +Calls the real MainWindow._seek_relative() method directly rather than +simulating the QShortcut keypress: QTest.keyClick against this shortcut was +observed to be unreliable in this headless setup even with Qt's +active-window state forced (see smoke_test_arrow_seek.py's note) -- +sometimes the simulated key silently doesn't reach the shortcut at all, and +a debug script isolated that PlayerWidget.seek() itself works correctly +while playing, so the flakiness is in the synthetic-keypress dispatch, not +the app. The QShortcut -> _seek_relative wiring itself (a single line) was +already independently confirmed with real xdotool keypresses against a +live window in an earlier session. +""" +import sys +import time +from pathlib import Path + +from PyQt6.QtWidgets import QApplication + +sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) +import main as app_main + +ROOT = Path(__file__).resolve().parent.parent +CLIP = str(ROOT / "test_media" / "sync_test_INSYNC.mp4") + + +def main(): + qapp = QApplication(sys.argv) + win = app_main.MainWindow() + win.show() + qapp.processEvents() + + win._load(CLIP) + qapp.processEvents() + time.sleep(0.2) + qapp.processEvents() + + # -- Case 1: paused before the skip -> stays paused after + assert win.player.mpv.pause is True + before_pos = win.player.get_position() + win._seek_relative(5.0) + qapp.processEvents() + time.sleep(0.2) + qapp.processEvents() + after_pos = win.player.get_position() + print(f"paused case: before={before_pos:.3f}s after={after_pos:.3f}s") + assert win.player.mpv.pause is True, "should still be paused after skip" + assert win.play_button.text() == "Play" + assert after_pos > before_pos + 4.0, "should have actually skipped forward" + print("PASS: stays paused across arrow-key skip when it started paused") + + # -- Case 2: playing before the skip -> stays playing after + win.player.seek(0.0) + win.player.play() + win.play_button.setText("Pause") + qapp.processEvents() + time.sleep(0.2) + before_pos = win.player.get_position() + win._seek_relative(5.0) + qapp.processEvents() + time.sleep(0.2) + qapp.processEvents() + assert win.player.mpv.pause is False, "should still be playing after skip" + assert win.play_button.text() == "Pause" + after_pos = win.player.get_position() + print(f"playing case: before={before_pos:.3f}s after={after_pos:.3f}s (should have jumped ~5s and kept playing)") + assert after_pos > before_pos + 4.0, "should have actually skipped forward" + + win.player.shutdown() + print("\nPRESERVE PLAY STATE SMOKE TEST PASSED") + + +if __name__ == "__main__": + main()