Cache thumbnails
This commit is contained in:
parent
a211bfd1b3
commit
86716e83b4
2 changed files with 54 additions and 7 deletions
|
|
@ -161,6 +161,8 @@ class MainWindow(Gtk.ApplicationWindow, Watcher):
|
||||||
return cast(FileItem, selected_item) if selected_item else None
|
return cast(FileItem, selected_item) if selected_item else None
|
||||||
|
|
||||||
def _setup_list_item(self, factory: Gtk.SignalListItemFactory, list_item: Gtk.ListItem):
|
def _setup_list_item(self, factory: Gtk.SignalListItemFactory, list_item: Gtk.ListItem):
|
||||||
|
factory = factory
|
||||||
|
|
||||||
# Create horizontal box to hold icon and label
|
# Create horizontal box to hold icon and label
|
||||||
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||||
box.set_spacing(8)
|
box.set_spacing(8)
|
||||||
|
|
@ -178,6 +180,8 @@ class MainWindow(Gtk.ApplicationWindow, Watcher):
|
||||||
list_item.set_child(box)
|
list_item.set_child(box)
|
||||||
|
|
||||||
def _bind_list_item(self, factory: Gtk.SignalListItemFactory, list_item: Gtk.ListItem):
|
def _bind_list_item(self, factory: Gtk.SignalListItemFactory, list_item: Gtk.ListItem):
|
||||||
|
factory = factory
|
||||||
|
|
||||||
box = cast(Gtk.Box, list_item.get_child())
|
box = cast(Gtk.Box, list_item.get_child())
|
||||||
icon = cast(Gtk.Image, box.get_first_child())
|
icon = cast(Gtk.Image, box.get_first_child())
|
||||||
label = cast(Gtk.Label, box.get_last_child())
|
label = cast(Gtk.Label, box.get_last_child())
|
||||||
|
|
@ -206,6 +210,8 @@ class MainWindow(Gtk.ApplicationWindow, Watcher):
|
||||||
update_icon()
|
update_icon()
|
||||||
|
|
||||||
def _on_activate(self, widget: Gtk.ListView, index: int):
|
def _on_activate(self, widget: Gtk.ListView, index: int):
|
||||||
|
widget = widget
|
||||||
|
|
||||||
selected_item = self.selection_model.get_item(index)
|
selected_item = self.selection_model.get_item(index)
|
||||||
if selected_item:
|
if selected_item:
|
||||||
file_item = cast(FileItem, selected_item)
|
file_item = cast(FileItem, selected_item)
|
||||||
|
|
@ -262,6 +268,8 @@ class MainWindow(Gtk.ApplicationWindow, Watcher):
|
||||||
position: int,
|
position: int,
|
||||||
n_items: int,
|
n_items: int,
|
||||||
):
|
):
|
||||||
|
n_items = n_items
|
||||||
|
|
||||||
position = selection_model.get_selected()
|
position = selection_model.get_selected()
|
||||||
|
|
||||||
if position == Gtk.INVALID_LIST_POSITION:
|
if position == Gtk.INVALID_LIST_POSITION:
|
||||||
|
|
@ -289,6 +297,9 @@ class MainWindow(Gtk.ApplicationWindow, Watcher):
|
||||||
keycode: int,
|
keycode: int,
|
||||||
state: Gdk.ModifierType,
|
state: Gdk.ModifierType,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
|
keycode = keycode
|
||||||
|
state = state
|
||||||
|
|
||||||
if keyval == Gdk.keyval_from_name("space"):
|
if keyval == Gdk.keyval_from_name("space"):
|
||||||
self._toggle_play_pause()
|
self._toggle_play_pause()
|
||||||
return True
|
return True
|
||||||
|
|
@ -387,6 +398,9 @@ class MainWindow(Gtk.ApplicationWindow, Watcher):
|
||||||
keycode: int,
|
keycode: int,
|
||||||
state: Gdk.ModifierType,
|
state: Gdk.ModifierType,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
|
keycode = keycode
|
||||||
|
state = state
|
||||||
|
|
||||||
if keyval == Gdk.keyval_from_name("q"):
|
if keyval == Gdk.keyval_from_name("q"):
|
||||||
self.close()
|
self.close()
|
||||||
return True
|
return True
|
||||||
|
|
@ -412,6 +426,8 @@ class MainWindow(Gtk.ApplicationWindow, Watcher):
|
||||||
keycode: int,
|
keycode: int,
|
||||||
state: Gdk.ModifierType,
|
state: Gdk.ModifierType,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
|
controller = controller
|
||||||
|
|
||||||
# Handle keys differently based on which view is active
|
# Handle keys differently based on which view is active
|
||||||
if self.stack.get_visible_child_name() == "player":
|
if self.stack.get_visible_child_name() == "player":
|
||||||
return self._on_player_key_pressed(keyval, keycode, state)
|
return self._on_player_key_pressed(keyval, keycode, state)
|
||||||
|
|
@ -438,6 +454,9 @@ class MainWindow(Gtk.ApplicationWindow, Watcher):
|
||||||
file_item.saved_duration.value = duration
|
file_item.saved_duration.value = duration
|
||||||
|
|
||||||
def _on_tick(self, widget: Gtk.Widget, frame_clock: Gdk.FrameClock, data: Any) -> bool:
|
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 reactive values for whole application.
|
||||||
update_all_computed()
|
update_all_computed()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,11 @@ __all__ = ["Thumbnailer", "generate_thumbnail_sync"]
|
||||||
|
|
||||||
MAX_WORKERS = max(1, multiprocessing.cpu_count() // 2)
|
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):
|
class Thumbnailer(ThreadPoolExecutor):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
@ -51,7 +56,35 @@ class Thumbnailer(ThreadPoolExecutor):
|
||||||
|
|
||||||
def generate_thumbnail_sync_nicely(file_item: FileItem):
|
def generate_thumbnail_sync_nicely(file_item: FileItem):
|
||||||
os.nice(10)
|
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):
|
def generate_thumbnail_sync(file_item: FileItem):
|
||||||
|
|
@ -118,12 +151,7 @@ def generate_thumbnail_sync(file_item: FileItem):
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
candidates.append(get_sample(duration // 3 - Gst.SECOND + i * Gst.SECOND))
|
candidates.append(get_sample(duration // 3 - Gst.SECOND + i * Gst.SECOND))
|
||||||
|
|
||||||
thumbnail = max(candidates, key=len)
|
return max(candidates, key=len)
|
||||||
|
|
||||||
def set_thumbnail():
|
|
||||||
file_item.thumbnail.value = thumbnail
|
|
||||||
|
|
||||||
GLib.idle_add(set_thumbnail)
|
|
||||||
|
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
print("[thumbnailer] Error:", file_item.full_path.name, file=sys.stderr)
|
print("[thumbnailer] Error:", file_item.full_path.name, file=sys.stderr)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue