From 0972dbd238d21feabdac42965126eae974c68601 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Hamal=20Dvo=C5=99=C3=A1k?= <mordae@anilinux.org> Date: Tue, 11 Mar 2025 10:38:42 +0100 Subject: [PATCH] Improve thumbnailing process --- lazy_player/file_model.py | 12 ++++++++++++ lazy_player/main_window.py | 27 +++++++++++---------------- lazy_player/thumbnailer.py | 15 ++++++++++++--- 3 files changed, 35 insertions(+), 19 deletions(-) diff --git a/lazy_player/file_model.py b/lazy_player/file_model.py index 698ed9e..7daeb16 100644 --- a/lazy_player/file_model.py +++ b/lazy_player/file_model.py @@ -8,6 +8,8 @@ from typing import Optional, overload from gi.repository import Gio, GObject +from .thumbnailer import Thumbnailer + class FileType(Enum): DIRECTORY = auto() @@ -18,6 +20,8 @@ class FileItem(GObject.Object): file_type: FileType full_path: Path thumbnail: bytes + attempted_thumbnail: bool + _has_thumbnail: bool __gtype_name__ = "FileItem" @@ -28,6 +32,7 @@ class FileItem(GObject.Object): self.file_type = file_type self.full_path = full_path self.thumbnail = b"" + self.attempted_thumbnail = False self._has_thumbnail = False @GObject.Property(type=GObject.TYPE_UINT64) @@ -75,6 +80,13 @@ class FileItem(GObject.Object): self._has_thumbnail = value self.notify("has-thumbnail") + def ensure_thumbnail(self, thumbnailer: Thumbnailer): + if self.thumbnail or self.attempted_thumbnail: + return + + if not self.attempted_thumbnail: + thumbnailer.generate_thumbnail(self) + @overload def _load_attribute(self, name: str, dfl: str) -> str: ... diff --git a/lazy_player/main_window.py b/lazy_player/main_window.py index 8ffbd57..c00161f 100644 --- a/lazy_player/main_window.py +++ b/lazy_player/main_window.py @@ -246,21 +246,20 @@ class MainWindow(Gtk.ApplicationWindow): item.connect("notify::saved-duration", update_icon) update_icon() - def _refresh(self): - self._populate_file_list() - - pos = self.selection_history.get(str(os.getcwd()), 0) + def _restore_selection(self): + pos = self.selection_history.get(os.getcwd(), 0) self.selection_model.set_selected(pos) self.list_view.scroll_to(pos, Gtk.ListScrollFlags.SELECT | Gtk.ListScrollFlags.FOCUS, None) + def _refresh(self): + self._populate_file_list() + self._restore_selection() + def _navigate_to(self, path: Path): self.directory_history.append(Path(os.getcwd())) os.chdir(path) self._populate_file_list() - - pos = self.selection_history.get(str(path), 0) - self.selection_model.set_selected(pos) - self.list_view.scroll_to(pos, Gtk.ListScrollFlags.SELECT | Gtk.ListScrollFlags.FOCUS, None) + self._restore_selection() def _navigate_back(self): if not self.directory_history: @@ -269,10 +268,7 @@ class MainWindow(Gtk.ApplicationWindow): prev_dir = self.directory_history.pop() os.chdir(prev_dir) self._populate_file_list() - - pos = self.selection_history.get(str(prev_dir), 0) - self.selection_model.set_selected(pos) - self.list_view.scroll_to(pos, Gtk.ListScrollFlags.SELECT | Gtk.ListScrollFlags.FOCUS, None) + self._restore_selection() def _on_selection_changed( self, @@ -287,8 +283,7 @@ class MainWindow(Gtk.ApplicationWindow): self.selection_history[os.getcwd()] = position - file_item = self.selection - if file_item is not None: + if file_item := self.selection: # Update thumbnail if available if file_item.thumbnail: gbytes = GLib.Bytes.new(cast(Any, file_item.thumbnail)) @@ -475,9 +470,9 @@ class MainWindow(Gtk.ApplicationWindow): self.last_position_save = frame_time # Update thumbnail if available - file_item = self.selection + if file_item := self.selection: + file_item.ensure_thumbnail(self.thumbnailer) - if file_item is not None: if file_item.thumbnail and not self.thumbnail_image.get_paintable(): gbytes = GLib.Bytes.new(cast(Any, file_item.thumbnail)) texture = Gdk.Texture.new_from_bytes(gbytes) diff --git a/lazy_player/thumbnailer.py b/lazy_player/thumbnailer.py index ed27715..dcf69ef 100644 --- a/lazy_player/thumbnailer.py +++ b/lazy_player/thumbnailer.py @@ -3,7 +3,7 @@ from __future__ import annotations import threading from queue import Empty, Queue -from gi.repository import Gst +from gi.repository import GLib, Gst from .file_model import FileItem @@ -28,6 +28,7 @@ class Thumbnailer(threading.Thread): self.queue.get_nowait() except Empty: pass + self.queue.put_nowait(file_item) def stop(self): @@ -51,6 +52,7 @@ class Thumbnailer(threading.Thread): if file_item is None: break + file_item.attempted_thumbnail = True self._generate_thumbnail(file_item) def _generate_thumbnail(self, file_item: FileItem): @@ -105,11 +107,18 @@ class Thumbnailer(threading.Thread): return try: - file_item.thumbnail = bytes(map_info.data) - file_item.has_thumbnail = True + thumbnail = bytes(map_info.data) finally: buffer.unmap(map_info) + + def set_thumbnail(): + file_item.thumbnail = thumbnail + file_item.has_thumbnail = True + + GLib.idle_add(set_thumbnail) + except Exception as err: print("Failed:", file_item.full_path, err) + finally: pipeline.set_state(Gst.State.NULL)