Add Loop/Next toggle for video playback

Default: Loop (replays video). Toggle to Next: auto-advances to
next post when video ends. Works in both preview and slideshow.
This commit is contained in:
pax 2026-04-05 00:25:08 -05:00
parent 2bca5ca188
commit ea08e0e3e4

View File

@ -71,6 +71,7 @@ class FullscreenPreview(QMainWindow):
self._stack.addWidget(self._viewer) self._stack.addWidget(self._viewer)
self._video = VideoPlayer() self._video = VideoPlayer()
self._video.play_next.connect(lambda: self.navigate.emit(1))
self._stack.addWidget(self._video) self._stack.addWidget(self._video)
self.setCentralWidget(central) self.setCentralWidget(central)
@ -326,6 +327,8 @@ class _ClickSeekSlider(QSlider):
class VideoPlayer(QWidget): class VideoPlayer(QWidget):
"""Video player with transport controls.""" """Video player with transport controls."""
play_next = Signal() # emitted when video ends in "next" mode
def __init__(self, parent: QWidget | None = None) -> None: def __init__(self, parent: QWidget | None = None) -> None:
super().__init__(parent) super().__init__(parent)
layout = QVBoxLayout(self) layout = QVBoxLayout(self)
@ -388,6 +391,15 @@ class VideoPlayer(QWidget):
self._autoplay_btn.clicked.connect(self._toggle_autoplay) self._autoplay_btn.clicked.connect(self._toggle_autoplay)
controls.addWidget(self._autoplay_btn) controls.addWidget(self._autoplay_btn)
self._loop_mode = True
self._loop_btn = QPushButton("Loop")
self._loop_btn.setFixedWidth(55)
self._loop_btn.setCheckable(True)
self._loop_btn.setChecked(True)
self._loop_btn.setToolTip("Loop: replay video / Next: play next post")
self._loop_btn.clicked.connect(self._toggle_loop)
controls.addWidget(self._loop_btn)
layout.addLayout(controls) layout.addLayout(controls)
# Signals # Signals
@ -402,7 +414,9 @@ class VideoPlayer(QWidget):
def play_file(self, path: str, info: str = "") -> None: def play_file(self, path: str, info: str = "") -> None:
self._current_file = path self._current_file = path
self._error_fired = False self._error_fired = False
self._player.setLoops(QMediaPlayer.Loops.Infinite) self._player.setLoops(
QMediaPlayer.Loops.Infinite if self._loop_mode else 1
)
self._player.setSource(QUrl.fromLocalFile(path)) self._player.setSource(QUrl.fromLocalFile(path))
if self._autoplay: if self._autoplay:
self._player.play() self._player.play()
@ -413,6 +427,13 @@ class VideoPlayer(QWidget):
self._autoplay = self._autoplay_btn.isChecked() self._autoplay = self._autoplay_btn.isChecked()
self._autoplay_btn.setText("Auto" if self._autoplay else "Man.") self._autoplay_btn.setText("Auto" if self._autoplay else "Man.")
def _toggle_loop(self) -> None:
self._loop_mode = self._loop_btn.isChecked()
self._loop_btn.setText("Loop" if self._loop_mode else "Next")
self._player.setLoops(
QMediaPlayer.Loops.Infinite if self._loop_mode else 1
)
def stop(self) -> None: def stop(self) -> None:
self._player.stop() self._player.stop()
self._player.setSource(QUrl()) self._player.setSource(QUrl())
@ -453,7 +474,8 @@ class VideoPlayer(QWidget):
self._play_btn.setText("Play") self._play_btn.setText("Play")
def _on_media_status(self, status) -> None: def _on_media_status(self, status) -> None:
pass # Looping handled by QMediaPlayer.Loops.Infinite if status == QMediaPlayer.MediaStatus.EndOfMedia and not self._loop_mode:
self.play_next.emit()
def _on_error(self, error, msg: str = "") -> None: def _on_error(self, error, msg: str = "") -> None:
if self._current_file and not self._error_fired: if self._current_file and not self._error_fired:
@ -503,6 +525,7 @@ class ImagePreview(QWidget):
# Video player (index 1) # Video player (index 1)
self._video_player = VideoPlayer() self._video_player = VideoPlayer()
self._video_player.setFocusPolicy(Qt.FocusPolicy.NoFocus) self._video_player.setFocusPolicy(Qt.FocusPolicy.NoFocus)
self._video_player.play_next.connect(lambda: self.navigate.emit(1))
self._stack.addWidget(self._video_player) self._stack.addWidget(self._video_player)
# Info label # Info label