Add space in front of files
This commit is contained in:
parent
721df98762
commit
27e754b3f7
3 changed files with 103 additions and 32 deletions
lazy_player
|
@ -7,18 +7,20 @@ from typing import Any, cast
|
||||||
|
|
||||||
import gi
|
import gi
|
||||||
|
|
||||||
|
from .file_model import FileItem, FileType
|
||||||
|
|
||||||
gi.require_version("Gdk", "4.0")
|
gi.require_version("Gdk", "4.0")
|
||||||
gi.require_version("Gtk", "4.0")
|
gi.require_version("Gtk", "4.0")
|
||||||
gi.require_version("Gst", "1.0")
|
gi.require_version("Gst", "1.0")
|
||||||
gi.require_version("Pango", "1.0")
|
gi.require_version("Pango", "1.0")
|
||||||
from gi.repository import Gdk, Gst, Gtk, Pango # NOQA: E402
|
from gi.repository import Gdk, Gio, Gst, Gtk, Pango # NOQA: E402
|
||||||
|
|
||||||
|
|
||||||
class MainWindow(Gtk.ApplicationWindow):
|
class MainWindow(Gtk.ApplicationWindow):
|
||||||
file_info_label: Gtk.Label
|
file_info_label: Gtk.Label
|
||||||
stack: Gtk.Stack
|
stack: Gtk.Stack
|
||||||
list_view: Gtk.ListView
|
list_view: Gtk.ListView
|
||||||
list_store: Gtk.StringList
|
list_store: Gio.ListStore
|
||||||
video_widget: Gtk.Picture
|
video_widget: Gtk.Picture
|
||||||
pipeline: Gst.Pipeline
|
pipeline: Gst.Pipeline
|
||||||
overlay_tick_callback_id: int
|
overlay_tick_callback_id: int
|
||||||
|
@ -127,7 +129,7 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||||
main_box.append(grid)
|
main_box.append(grid)
|
||||||
|
|
||||||
# Create list store and view
|
# Create list store and view
|
||||||
self.list_store = Gtk.StringList()
|
self.list_store = Gio.ListStore(item_type=FileItem)
|
||||||
self.list_view = Gtk.ListView()
|
self.list_view = Gtk.ListView()
|
||||||
selection_model = Gtk.SingleSelection.new(self.list_store)
|
selection_model = Gtk.SingleSelection.new(self.list_store)
|
||||||
selection_model.connect("selection-changed", self._on_selection_changed)
|
selection_model.connect("selection-changed", self._on_selection_changed)
|
||||||
|
@ -137,21 +139,20 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||||
def on_activate(widget: Gtk.ListView, index: int):
|
def on_activate(widget: Gtk.ListView, index: int):
|
||||||
selected_item = selection_model.get_item(index)
|
selected_item = selection_model.get_item(index)
|
||||||
if selected_item:
|
if selected_item:
|
||||||
string_obj = cast(Gtk.StringObject, selected_item)
|
file_item = cast(FileItem, selected_item)
|
||||||
string = string_obj.get_string()
|
|
||||||
|
|
||||||
if string.endswith("/"):
|
if file_item.file_type == FileType.DIRECTORY:
|
||||||
os.chdir(string)
|
os.chdir(file_item.full_path)
|
||||||
self._populate_file_list()
|
self._populate_file_list()
|
||||||
return
|
return
|
||||||
|
|
||||||
# Start playing the video
|
# Start playing the video
|
||||||
playbin = self.pipeline.get_by_name("playbin")
|
playbin = self.pipeline.get_by_name("playbin")
|
||||||
if playbin:
|
if playbin:
|
||||||
playbin.set_property("uri", f"file://{os.path.abspath(string)}")
|
playbin.set_property("uri", f"file://{os.path.abspath(file_item.full_path)}")
|
||||||
self.pipeline.set_state(Gst.State.PLAYING)
|
self.pipeline.set_state(Gst.State.PLAYING)
|
||||||
self.stack.set_visible_child_name("overlay")
|
self.stack.set_visible_child_name("overlay")
|
||||||
self.show_overlay_text(string)
|
self.show_overlay_text(f"Playing: {file_item.name}")
|
||||||
|
|
||||||
self.list_view.connect("activate", on_activate)
|
self.list_view.connect("activate", on_activate)
|
||||||
|
|
||||||
|
@ -172,14 +173,31 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||||
self.set_child(self.stack)
|
self.set_child(self.stack)
|
||||||
|
|
||||||
def _setup_list_item(self, factory: Gtk.SignalListItemFactory, list_item: Gtk.ListItem):
|
def _setup_list_item(self, factory: Gtk.SignalListItemFactory, list_item: Gtk.ListItem):
|
||||||
|
# Create horizontal box to hold icon and label
|
||||||
|
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||||
|
box.set_spacing(8)
|
||||||
|
|
||||||
|
# Create icon placeholder
|
||||||
|
icon = Gtk.Box()
|
||||||
|
icon.set_css_classes(["file-icon"])
|
||||||
|
box.append(icon)
|
||||||
|
|
||||||
|
# Create label
|
||||||
label = Gtk.Label()
|
label = Gtk.Label()
|
||||||
label.set_halign(Gtk.Align.START)
|
label.set_halign(Gtk.Align.START)
|
||||||
list_item.set_child(label)
|
box.append(label)
|
||||||
|
|
||||||
|
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):
|
||||||
label = cast(Gtk.Label, list_item.get_child())
|
box = cast(Gtk.Box, list_item.get_child())
|
||||||
item = cast(Gtk.StringObject, list_item.get_item())
|
icon = cast(Gtk.Box, box.get_first_child())
|
||||||
label.set_text(item.get_string())
|
label = cast(Gtk.Label, box.get_last_child())
|
||||||
|
item = cast(FileItem, list_item.get_item())
|
||||||
|
|
||||||
|
# Make icon transparent for directories
|
||||||
|
icon.set_opacity(0.0 if item.file_type == FileType.DIRECTORY else 1.0)
|
||||||
|
label.set_text(item.name)
|
||||||
|
|
||||||
def _on_selection_changed(
|
def _on_selection_changed(
|
||||||
self,
|
self,
|
||||||
|
@ -192,8 +210,8 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||||
else:
|
else:
|
||||||
selected_item = selection_model.get_selected_item()
|
selected_item = selection_model.get_selected_item()
|
||||||
if selected_item:
|
if selected_item:
|
||||||
string_obj = cast(Gtk.StringObject, selected_item)
|
file_item = cast(FileItem, selected_item)
|
||||||
self.file_info_label.set_text(string_obj.get_string())
|
self.file_info_label.set_text(file_item.full_path)
|
||||||
|
|
||||||
def _toggle_play_pause(self) -> None:
|
def _toggle_play_pause(self) -> None:
|
||||||
"""Toggle between play and pause states"""
|
"""Toggle between play and pause states"""
|
||||||
|
@ -343,31 +361,35 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||||
def _populate_file_list(self) -> None:
|
def _populate_file_list(self) -> None:
|
||||||
# TODO: Implement proper version sort (strverscmp equivalent)
|
# TODO: Implement proper version sort (strverscmp equivalent)
|
||||||
|
|
||||||
directories: list[str] = ["../"]
|
items: list[FileItem] = []
|
||||||
files: list[str] = []
|
|
||||||
|
# Add parent directory
|
||||||
|
items.append(FileItem("..", FileType.DIRECTORY, "../"))
|
||||||
|
|
||||||
with os.scandir(".") as it:
|
with os.scandir(".") as it:
|
||||||
for entry in it:
|
for entry in it:
|
||||||
if entry.name == ".." or not entry.name.startswith("."):
|
if entry.name != ".." and not entry.name.startswith("."):
|
||||||
parts = entry.name.split(".")
|
try:
|
||||||
suffix = parts[-1] if len(parts) >= 2 else ""
|
if entry.is_dir():
|
||||||
|
items.append(FileItem(entry.name, FileType.DIRECTORY, entry.name + "/"))
|
||||||
|
else:
|
||||||
|
file_item = FileItem.from_path(entry.name)
|
||||||
|
items.append(file_item)
|
||||||
|
except ValueError:
|
||||||
|
# Skip unsupported file types
|
||||||
|
continue
|
||||||
|
|
||||||
if entry.is_dir():
|
# Sort directories first, then files, both alphabetically
|
||||||
directories.append(entry.name + "/")
|
items.sort(key=lambda x: (x.file_type != FileType.DIRECTORY, x.name.lower()))
|
||||||
elif suffix in ("mkv", "mp4", "avi"):
|
|
||||||
files.append(entry.name)
|
|
||||||
|
|
||||||
directories.sort(key=lambda x: x.lower())
|
|
||||||
files.sort(key=lambda x: x.lower())
|
|
||||||
|
|
||||||
while self.list_store.get_n_items():
|
while self.list_store.get_n_items():
|
||||||
self.list_store.remove(0)
|
self.list_store.remove(0)
|
||||||
|
|
||||||
for name in directories + files:
|
for item in items:
|
||||||
self.list_store.append(name)
|
self.list_store.append(item)
|
||||||
|
|
||||||
all = directories + files
|
if items:
|
||||||
self.file_info_label.set_text(all[0] if all else "")
|
self.file_info_label.set_text(items[0].full_path)
|
||||||
|
|
||||||
|
|
||||||
class App(Gtk.Application):
|
class App(Gtk.Application):
|
||||||
|
|
43
lazy_player/file_model.py
Normal file
43
lazy_player/file_model.py
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
from enum import Enum, auto
|
||||||
|
|
||||||
|
import gi
|
||||||
|
|
||||||
|
gi.require_version("GObject", "2.0")
|
||||||
|
from gi.repository import GObject # NOQA: E402
|
||||||
|
|
||||||
|
|
||||||
|
class FileType(Enum):
|
||||||
|
DIRECTORY = auto()
|
||||||
|
VIDEO = auto()
|
||||||
|
|
||||||
|
|
||||||
|
class FileItem(GObject.Object):
|
||||||
|
__gtype_name__ = "FileItem"
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
name: str,
|
||||||
|
file_type: FileType,
|
||||||
|
full_path: str,
|
||||||
|
):
|
||||||
|
super().__init__()
|
||||||
|
self.name = name
|
||||||
|
self.file_type = file_type
|
||||||
|
self.full_path = full_path
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_path(path: str) -> FileItem:
|
||||||
|
name = os.path.basename(path)
|
||||||
|
if path.endswith("/"):
|
||||||
|
return FileItem(name, FileType.DIRECTORY, path)
|
||||||
|
|
||||||
|
parts = name.split(".")
|
||||||
|
suffix = parts[-1].lower() if len(parts) >= 2 else ""
|
||||||
|
|
||||||
|
if suffix in ("mkv", "mp4", "avi"):
|
||||||
|
return FileItem(name, FileType.VIDEO, path)
|
||||||
|
|
||||||
|
raise ValueError(f"Unsupported file type: {path}")
|
|
@ -4,6 +4,13 @@ listview > row {
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.file-icon {
|
||||||
|
min-width: 32px;
|
||||||
|
min-height: 32px;
|
||||||
|
margin-right: 8px;
|
||||||
|
border: 1px solid #666;
|
||||||
|
}
|
||||||
|
|
||||||
#black-overlay {
|
#black-overlay {
|
||||||
background-color: black;
|
background-color: black;
|
||||||
}
|
}
|
||||||
|
@ -16,5 +23,4 @@ listview > row {
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
margin: 32px;
|
margin: 32px;
|
||||||
width: 80%;
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue