From 33293dfbae3c852e9f276c8c79954f91e30f8d7b Mon Sep 17 00:00:00 2001 From: pax Date: Tue, 7 Apr 2026 11:41:26 -0500 Subject: [PATCH] Wrap video Next loop to start of bookmarks/library list at end of media --- booru_viewer/gui/app.py | 35 +++++++++++++++++++++++++++++++++-- booru_viewer/gui/preview.py | 6 ++++-- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/booru_viewer/gui/app.py b/booru_viewer/gui/app.py index 3cbfdf6..7ae697b 100644 --- a/booru_viewer/gui/app.py +++ b/booru_viewer/gui/app.py @@ -460,6 +460,7 @@ class BooruApp(QMainWindow): self._preview.blacklist_tag_requested.connect(self._blacklist_tag_from_popout) self._preview.blacklist_post_requested.connect(self._blacklist_post_from_popout) self._preview.navigate.connect(self._navigate_preview) + self._preview.play_next_requested.connect(self._on_video_end_next) self._preview.fullscreen_requested.connect(self._open_fullscreen_preview) self._preview.set_folders_callback(self._db.get_folders) self._fullscreen_window = None @@ -1667,8 +1668,13 @@ class BooruApp(QMainWindow): if 0 <= idx < len(self._posts): self._open_in_browser(self._posts[idx]) - def _navigate_preview(self, direction: int) -> None: - """Navigate to prev/next post in the preview. direction: -1 or +1.""" + def _navigate_preview(self, direction: int, wrap: bool = False) -> None: + """Navigate to prev/next post in the preview. direction: -1 or +1. + + wrap=True wraps to the start (or end) of the bookmarks/library lists + when running off the edge — used for the video-end "Next" auto-advance + on tabs that don't have pagination. + """ if self._stack.currentIndex() == 1: # Bookmarks view grid = self._bookmarks_view._grid @@ -1677,6 +1683,10 @@ class BooruApp(QMainWindow): if 0 <= idx < len(favs): grid._select(idx) self._on_bookmark_activated(favs[idx]) + elif wrap and favs: + idx = 0 if direction > 0 else len(favs) - 1 + grid._select(idx) + self._on_bookmark_activated(favs[idx]) elif self._stack.currentIndex() == 2: # Library view grid = self._library_view._grid @@ -1685,6 +1695,10 @@ class BooruApp(QMainWindow): if 0 <= idx < len(files): grid._select(idx) self._library_view.file_activated.emit(str(files[idx])) + elif wrap and files: + idx = 0 if direction > 0 else len(files) - 1 + grid._select(idx) + self._library_view.file_activated.emit(str(files[idx])) else: idx = self._grid.selected_index + direction log.info(f"Navigate: direction={direction} current={self._grid.selected_index} next={idx} total={len(self._posts)}") @@ -1698,6 +1712,22 @@ class BooruApp(QMainWindow): self._search.nav_page_turn = "last" self._prev_page() + def _on_video_end_next(self) -> None: + """Auto-advance from end of video in 'Next' mode. + + Wraps to start on bookmarks/library tabs (where there is no + pagination), so a single video looping with Next mode keeps moving + through the list indefinitely instead of stopping at the end. Browse + tab keeps its existing page-turn behaviour. + """ + self._navigate_preview(1, wrap=True) + # Sync popout if it's open + if self._fullscreen_window and self._preview._current_path: + self._update_fullscreen( + self._preview._current_path, + self._preview._info_label.text(), + ) + def _is_post_saved(self, post_id: int) -> bool: """Check if a post is saved in the library (any folder).""" from ..core.config import saved_dir, saved_folder_dir, MEDIA_EXTENSIONS @@ -1880,6 +1910,7 @@ class BooruApp(QMainWindow): monitor = self._db.get_setting("slideshow_monitor") self._fullscreen_window = FullscreenPreview(grid_cols=cols, show_actions=show_actions, monitor=monitor, parent=self) self._fullscreen_window.navigate.connect(self._navigate_fullscreen) + self._fullscreen_window.play_next_requested.connect(self._on_video_end_next) if show_actions: self._fullscreen_window.bookmark_requested.connect(self._bookmark_from_preview) self._fullscreen_window.save_toggle_requested.connect(self._save_toggle_from_popout) diff --git a/booru_viewer/gui/preview.py b/booru_viewer/gui/preview.py index f9aef3c..096811f 100644 --- a/booru_viewer/gui/preview.py +++ b/booru_viewer/gui/preview.py @@ -63,6 +63,7 @@ class FullscreenPreview(QMainWindow): """Fullscreen media viewer with navigation — images, GIFs, and video.""" navigate = Signal(int) # direction: -1/+1 for left/right, -cols/+cols for up/down + play_next_requested = Signal() # video ended in "Next" mode (wrap-aware) bookmark_requested = Signal() save_toggle_requested = Signal() # save or unsave depending on state blacklist_tag_requested = Signal(str) # tag name @@ -90,7 +91,7 @@ class FullscreenPreview(QMainWindow): self._stack.addWidget(self._viewer) self._video = VideoPlayer() - self._video.play_next.connect(lambda: self.navigate.emit(1)) + self._video.play_next.connect(self.play_next_requested) self._video.video_size.connect(self._on_video_size) self._stack.addWidget(self._video) @@ -1190,6 +1191,7 @@ class ImagePreview(QWidget): blacklist_tag_requested = Signal(str) blacklist_post_requested = Signal() navigate = Signal(int) # -1 = prev, +1 = next + play_next_requested = Signal() # video ended in "Next" mode (wrap-aware) fullscreen_requested = Signal() def __init__(self, parent: QWidget | None = None) -> None: @@ -1257,7 +1259,7 @@ class ImagePreview(QWidget): # Video player (index 1) self._video_player = VideoPlayer() self._video_player.setFocusPolicy(Qt.FocusPolicy.NoFocus) - self._video_player.play_next.connect(lambda: self.navigate.emit(1)) + self._video_player.play_next.connect(self.play_next_requested) self._stack.addWidget(self._video_player) # Info label