Improve thumbnailing process

This commit is contained in:
Jan Hamal Dvořák 2025-03-11 10:38:42 +01:00
parent 4985de9ca8
commit 0972dbd238
3 changed files with 35 additions and 19 deletions

View file

@ -8,6 +8,8 @@ from typing import Optional, overload
from gi.repository import Gio, GObject from gi.repository import Gio, GObject
from .thumbnailer import Thumbnailer
class FileType(Enum): class FileType(Enum):
DIRECTORY = auto() DIRECTORY = auto()
@ -18,6 +20,8 @@ class FileItem(GObject.Object):
file_type: FileType file_type: FileType
full_path: Path full_path: Path
thumbnail: bytes thumbnail: bytes
attempted_thumbnail: bool
_has_thumbnail: bool _has_thumbnail: bool
__gtype_name__ = "FileItem" __gtype_name__ = "FileItem"
@ -28,6 +32,7 @@ class FileItem(GObject.Object):
self.file_type = file_type self.file_type = file_type
self.full_path = full_path self.full_path = full_path
self.thumbnail = b"" self.thumbnail = b""
self.attempted_thumbnail = False
self._has_thumbnail = False self._has_thumbnail = False
@GObject.Property(type=GObject.TYPE_UINT64) @GObject.Property(type=GObject.TYPE_UINT64)
@ -75,6 +80,13 @@ class FileItem(GObject.Object):
self._has_thumbnail = value self._has_thumbnail = value
self.notify("has-thumbnail") 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 @overload
def _load_attribute(self, name: str, dfl: str) -> str: ... def _load_attribute(self, name: str, dfl: str) -> str: ...

View file

@ -246,21 +246,20 @@ class MainWindow(Gtk.ApplicationWindow):
item.connect("notify::saved-duration", update_icon) item.connect("notify::saved-duration", update_icon)
update_icon() update_icon()
def _refresh(self): def _restore_selection(self):
self._populate_file_list() pos = self.selection_history.get(os.getcwd(), 0)
pos = self.selection_history.get(str(os.getcwd()), 0)
self.selection_model.set_selected(pos) self.selection_model.set_selected(pos)
self.list_view.scroll_to(pos, Gtk.ListScrollFlags.SELECT | Gtk.ListScrollFlags.FOCUS, None) 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): def _navigate_to(self, path: Path):
self.directory_history.append(Path(os.getcwd())) self.directory_history.append(Path(os.getcwd()))
os.chdir(path) os.chdir(path)
self._populate_file_list() self._populate_file_list()
self._restore_selection()
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)
def _navigate_back(self): def _navigate_back(self):
if not self.directory_history: if not self.directory_history:
@ -269,10 +268,7 @@ class MainWindow(Gtk.ApplicationWindow):
prev_dir = self.directory_history.pop() prev_dir = self.directory_history.pop()
os.chdir(prev_dir) os.chdir(prev_dir)
self._populate_file_list() self._populate_file_list()
self._restore_selection()
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)
def _on_selection_changed( def _on_selection_changed(
self, self,
@ -287,8 +283,7 @@ class MainWindow(Gtk.ApplicationWindow):
self.selection_history[os.getcwd()] = position self.selection_history[os.getcwd()] = position
file_item = self.selection if file_item := self.selection:
if file_item is not None:
# Update thumbnail if available # Update thumbnail if available
if file_item.thumbnail: if file_item.thumbnail:
gbytes = GLib.Bytes.new(cast(Any, 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 self.last_position_save = frame_time
# Update thumbnail if available # 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(): if file_item.thumbnail and not self.thumbnail_image.get_paintable():
gbytes = GLib.Bytes.new(cast(Any, file_item.thumbnail)) gbytes = GLib.Bytes.new(cast(Any, file_item.thumbnail))
texture = Gdk.Texture.new_from_bytes(gbytes) texture = Gdk.Texture.new_from_bytes(gbytes)

View file

@ -3,7 +3,7 @@ from __future__ import annotations
import threading import threading
from queue import Empty, Queue from queue import Empty, Queue
from gi.repository import Gst from gi.repository import GLib, Gst
from .file_model import FileItem from .file_model import FileItem
@ -28,6 +28,7 @@ class Thumbnailer(threading.Thread):
self.queue.get_nowait() self.queue.get_nowait()
except Empty: except Empty:
pass pass
self.queue.put_nowait(file_item) self.queue.put_nowait(file_item)
def stop(self): def stop(self):
@ -51,6 +52,7 @@ class Thumbnailer(threading.Thread):
if file_item is None: if file_item is None:
break break
file_item.attempted_thumbnail = True
self._generate_thumbnail(file_item) self._generate_thumbnail(file_item)
def _generate_thumbnail(self, file_item: FileItem): def _generate_thumbnail(self, file_item: FileItem):
@ -105,11 +107,18 @@ class Thumbnailer(threading.Thread):
return return
try: try:
file_item.thumbnail = bytes(map_info.data) thumbnail = bytes(map_info.data)
file_item.has_thumbnail = True
finally: finally:
buffer.unmap(map_info) 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: except Exception as err:
print("Failed:", file_item.full_path, err) print("Failed:", file_item.full_path, err)
finally: finally:
pipeline.set_state(Gst.State.NULL) pipeline.set_state(Gst.State.NULL)