Cache thumbnails

This commit is contained in:
Jan Hamal Dvořák 2025-06-26 21:49:16 +02:00
parent a211bfd1b3
commit 86716e83b4
2 changed files with 54 additions and 7 deletions

View file

@ -161,6 +161,8 @@ class MainWindow(Gtk.ApplicationWindow, Watcher):
return cast(FileItem, selected_item) if selected_item else None
def _setup_list_item(self, factory: Gtk.SignalListItemFactory, list_item: Gtk.ListItem):
factory = factory
# Create horizontal box to hold icon and label
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
box.set_spacing(8)
@ -178,6 +180,8 @@ class MainWindow(Gtk.ApplicationWindow, Watcher):
list_item.set_child(box)
def _bind_list_item(self, factory: Gtk.SignalListItemFactory, list_item: Gtk.ListItem):
factory = factory
box = cast(Gtk.Box, list_item.get_child())
icon = cast(Gtk.Image, box.get_first_child())
label = cast(Gtk.Label, box.get_last_child())
@ -206,6 +210,8 @@ class MainWindow(Gtk.ApplicationWindow, Watcher):
update_icon()
def _on_activate(self, widget: Gtk.ListView, index: int):
widget = widget
selected_item = self.selection_model.get_item(index)
if selected_item:
file_item = cast(FileItem, selected_item)
@ -262,6 +268,8 @@ class MainWindow(Gtk.ApplicationWindow, Watcher):
position: int,
n_items: int,
):
n_items = n_items
position = selection_model.get_selected()
if position == Gtk.INVALID_LIST_POSITION:
@ -289,6 +297,9 @@ class MainWindow(Gtk.ApplicationWindow, Watcher):
keycode: int,
state: Gdk.ModifierType,
) -> bool:
keycode = keycode
state = state
if keyval == Gdk.keyval_from_name("space"):
self._toggle_play_pause()
return True
@ -387,6 +398,9 @@ class MainWindow(Gtk.ApplicationWindow, Watcher):
keycode: int,
state: Gdk.ModifierType,
) -> bool:
keycode = keycode
state = state
if keyval == Gdk.keyval_from_name("q"):
self.close()
return True
@ -412,6 +426,8 @@ class MainWindow(Gtk.ApplicationWindow, Watcher):
keycode: int,
state: Gdk.ModifierType,
) -> bool:
controller = controller
# Handle keys differently based on which view is active
if self.stack.get_visible_child_name() == "player":
return self._on_player_key_pressed(keyval, keycode, state)
@ -438,6 +454,9 @@ class MainWindow(Gtk.ApplicationWindow, Watcher):
file_item.saved_duration.value = duration
def _on_tick(self, widget: Gtk.Widget, frame_clock: Gdk.FrameClock, data: Any) -> bool:
widget = widget
data = data
# Update all reactive values for whole application.
update_all_computed()

View file

@ -26,6 +26,11 @@ __all__ = ["Thumbnailer", "generate_thumbnail_sync"]
MAX_WORKERS = max(1, multiprocessing.cpu_count() // 2)
CACHE_HOME = os.environ.get(
"XDG_CACHE_HOME", os.path.join(os.environ.get("HOME", "/tmp"), ".cache")
)
CACHE_PATH = os.path.join(CACHE_HOME, "lazy-player", "thumbnails")
class Thumbnailer(ThreadPoolExecutor):
def __init__(self):
@ -51,7 +56,35 @@ class Thumbnailer(ThreadPoolExecutor):
def generate_thumbnail_sync_nicely(file_item: FileItem):
os.nice(10)
return generate_thumbnail_sync(file_item)
stat = os.stat(file_item.full_path)
name = f"{stat.st_ino}-{stat.st_size}.jpeg"
path = os.path.join(CACHE_PATH, name)
if os.path.exists(path):
with open(path, "rb") as fp:
thumbnail = fp.read()
def set_thumbnail():
file_item.thumbnail.value = thumbnail
GLib.idle_add(set_thumbnail)
return
else:
os.makedirs(CACHE_PATH, exist_ok=True)
thumbnail = generate_thumbnail_sync(file_item)
if thumbnail is None:
return
with open(path, "wb") as fp:
fp.write(thumbnail)
def set_thumbnail():
file_item.thumbnail.value = thumbnail
GLib.idle_add(set_thumbnail)
def generate_thumbnail_sync(file_item: FileItem):
@ -118,12 +151,7 @@ def generate_thumbnail_sync(file_item: FileItem):
for i in range(3):
candidates.append(get_sample(duration // 3 - Gst.SECOND + i * Gst.SECOND))
thumbnail = max(candidates, key=len)
def set_thumbnail():
file_item.thumbnail.value = thumbnail
GLib.idle_add(set_thumbnail)
return max(candidates, key=len)
except Exception as err:
print("[thumbnailer] Error:", file_item.full_path.name, file=sys.stderr)