Compare commits
3 commits
f89623a201
...
6d09b7d1e7
Author | SHA1 | Date | |
---|---|---|---|
6d09b7d1e7 | |||
7355fc6a19 | |||
a511dae284 |
3 changed files with 77 additions and 17 deletions
lazy_player
|
@ -217,8 +217,8 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||||
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||||
box.set_spacing(8)
|
box.set_spacing(8)
|
||||||
|
|
||||||
# Create icon placeholder
|
# Create icon image
|
||||||
icon = Gtk.Box()
|
icon = Gtk.Image()
|
||||||
icon.set_css_classes(["file-icon"])
|
icon.set_css_classes(["file-icon"])
|
||||||
box.append(icon)
|
box.append(icon)
|
||||||
|
|
||||||
|
@ -231,12 +231,29 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||||
|
|
||||||
def _bind_list_item(self, factory: Gtk.SignalListItemFactory, list_item: Gtk.ListItem):
|
def _bind_list_item(self, factory: Gtk.SignalListItemFactory, list_item: Gtk.ListItem):
|
||||||
box = cast(Gtk.Box, list_item.get_child())
|
box = cast(Gtk.Box, list_item.get_child())
|
||||||
icon = cast(Gtk.Box, 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())
|
||||||
item = cast(FileItem, list_item.get_item())
|
item = cast(FileItem, list_item.get_item())
|
||||||
|
|
||||||
# Make icon transparent for directories
|
# Set appropriate icon
|
||||||
icon.set_opacity(0.0 if item.file_type == FileType.DIRECTORY else 1.0)
|
icon.set_opacity(1.0)
|
||||||
|
if item.file_type == FileType.DIRECTORY:
|
||||||
|
icon.set_from_icon_name("folder-symbolic")
|
||||||
|
icon.set_css_classes(["file-icon"])
|
||||||
|
else:
|
||||||
|
position = item.load_attribute("position", 0)
|
||||||
|
duration = item.load_attribute("duration", 1) or 1
|
||||||
|
|
||||||
|
if position == 0:
|
||||||
|
icon.set_from_icon_name("checkbox-symbolic")
|
||||||
|
icon.set_css_classes(["file-icon", "unwatched"])
|
||||||
|
elif (position / duration) >= 0.9:
|
||||||
|
icon.set_from_icon_name("object-select-symbolic")
|
||||||
|
icon.set_css_classes(["file-icon", "completed"])
|
||||||
|
else:
|
||||||
|
icon.set_from_icon_name("media-playback-pause-symbolic")
|
||||||
|
icon.set_css_classes(["file-icon", "in-progress"])
|
||||||
|
|
||||||
label.set_text(item.name)
|
label.set_text(item.name)
|
||||||
|
|
||||||
def _on_selection_changed(
|
def _on_selection_changed(
|
||||||
|
@ -304,7 +321,7 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||||
success, duration = self.pipeline.query_duration(Gst.Format.TIME)
|
success, duration = self.pipeline.query_duration(Gst.Format.TIME)
|
||||||
if success:
|
if success:
|
||||||
self.pipeline.seek_simple(
|
self.pipeline.seek_simple(
|
||||||
Gst.Format.TIME, Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT, duration - 1
|
Gst.Format.TIME, Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT, duration
|
||||||
)
|
)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -314,6 +331,41 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def _toggle_watched_status(self) -> None:
|
||||||
|
"""Toggle watched status for the selected file"""
|
||||||
|
file_item = self.selection
|
||||||
|
if file_item.file_type == FileType.DIRECTORY:
|
||||||
|
return
|
||||||
|
|
||||||
|
position = file_item.load_attribute("position", 0)
|
||||||
|
duration = file_item.load_attribute("duration", 1) or 1
|
||||||
|
|
||||||
|
# If position exists and is >= 90% through, clear it
|
||||||
|
if position > 0 and (position / duration) >= 0.9:
|
||||||
|
file_item.save_attribute("position", None)
|
||||||
|
else:
|
||||||
|
# Otherwise mark as complete
|
||||||
|
file_item.save_attribute("position", duration)
|
||||||
|
|
||||||
|
# Force the list to update the changed item
|
||||||
|
self.list_store.items_changed(self.selection_model.get_selected(), 1, 1)
|
||||||
|
|
||||||
|
def _on_menu_key_pressed(
|
||||||
|
self,
|
||||||
|
keyval: int,
|
||||||
|
keycode: int,
|
||||||
|
state: Gdk.ModifierType,
|
||||||
|
) -> bool:
|
||||||
|
if keyval == Gdk.keyval_from_name("q"):
|
||||||
|
self.close()
|
||||||
|
return True
|
||||||
|
|
||||||
|
elif keyval == Gdk.keyval_from_name("w"):
|
||||||
|
self._toggle_watched_status()
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
def _on_key_pressed(
|
def _on_key_pressed(
|
||||||
self,
|
self,
|
||||||
controller: Gtk.EventControllerKey,
|
controller: Gtk.EventControllerKey,
|
||||||
|
@ -321,10 +373,11 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||||
keycode: int,
|
keycode: int,
|
||||||
state: Gdk.ModifierType,
|
state: Gdk.ModifierType,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
# If we're showing video, handle keys differently
|
# Handle keys differently based on which view is active
|
||||||
if self.stack.get_visible_child_name() == "overlay":
|
if self.stack.get_visible_child_name() == "overlay":
|
||||||
return self._on_video_key_pressed(keyval, keycode, state)
|
return self._on_video_key_pressed(keyval, keycode, state)
|
||||||
return False
|
else:
|
||||||
|
return self._on_menu_key_pressed(keyval, keycode, state)
|
||||||
|
|
||||||
def _seek_relative(self, offset: int) -> None:
|
def _seek_relative(self, offset: int) -> None:
|
||||||
"""Seek relative to current position by offset seconds"""
|
"""Seek relative to current position by offset seconds"""
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
from enum import Enum, auto
|
from enum import Enum, auto
|
||||||
from typing import overload
|
from typing import overload
|
||||||
|
|
||||||
|
@ -52,8 +51,7 @@ class FileItem(GObject.Object):
|
||||||
try:
|
try:
|
||||||
strval = os.getxattr(self.full_path, f"user.lazy_player.{name}")
|
strval = os.getxattr(self.full_path, f"user.lazy_player.{name}")
|
||||||
return type(dfl)(strval)
|
return type(dfl)(strval)
|
||||||
except OSError as err:
|
except OSError:
|
||||||
print(err, file=sys.stderr)
|
|
||||||
return dfl
|
return dfl
|
||||||
|
|
||||||
def save_attribute(self, name: str, value: str | float | int | None) -> None:
|
def save_attribute(self, name: str, value: str | float | int | None) -> None:
|
||||||
|
@ -62,5 +60,5 @@ class FileItem(GObject.Object):
|
||||||
os.removexattr(self.full_path, f"user.lazy_player.{name}")
|
os.removexattr(self.full_path, f"user.lazy_player.{name}")
|
||||||
else:
|
else:
|
||||||
os.setxattr(self.full_path, f"user.lazy_player.{name}", str(value).encode("utf8"))
|
os.setxattr(self.full_path, f"user.lazy_player.{name}", str(value).encode("utf8"))
|
||||||
except OSError as err:
|
except OSError:
|
||||||
print(err, file=sys.stderr)
|
pass
|
||||||
|
|
|
@ -5,10 +5,19 @@ listview > row {
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-icon {
|
.file-icon {
|
||||||
min-width: 32px;
|
-gtk-icon-size: 24px;
|
||||||
min-height: 32px;
|
}
|
||||||
margin-right: 8px;
|
|
||||||
border: 1px solid #666;
|
.unwatched {
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.in-progress {
|
||||||
|
color: #ff0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.completed {
|
||||||
|
color: #0f0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#black-overlay {
|
#black-overlay {
|
||||||
|
|
Loading…
Reference in a new issue