lazy-player/lazy_player/file_model.py

142 lines
4.2 KiB
Python

from __future__ import annotations
import os
from enum import Enum, auto
from pathlib import Path
from typing import Optional, overload
from gi.repository import Gio, GObject
from .reactive import Ref, Watcher
class FileType(Enum):
DIRECTORY = auto()
VIDEO = auto()
class FileItem(GObject.Object, Watcher):
file_type: FileType
full_path: Path
thumbnail: Ref[bytes]
attempted_thumbnail: Ref[bool]
saved_position: Ref[int]
saved_duration: Ref[int]
saved_subtitle_track: Ref[int]
saved_audio_track: Ref[int]
__gtype_name__ = "FileItem"
def __init__(self, name: str, file_type: FileType, full_path: Path):
super().__init__()
self.name = name
self.file_type = file_type
self.full_path = full_path
self.thumbnail = Ref(b"")
self.attempted_thumbnail = Ref(False)
self.saved_position = Ref(self._load_attribute("position", 0))
self.saved_duration = Ref(self._load_attribute("duration", 1))
self.saved_subtitle_track = Ref(self._load_attribute("subtitle_track", -2))
self.saved_audio_track = Ref(self._load_attribute("audio_track", 0))
self.watch_all()
def _watch_saved_position(self):
saved_position = self.saved_position.value
self._save_attribute("position", saved_position if saved_position > 0 else None)
def _watch_saved_duration(self):
saved_duration = self.saved_duration.value
self._save_attribute("duration", saved_duration if saved_duration > 0 else None)
def _watch_saved_subtitle_track(self):
saved_subtitle_track = self.saved_subtitle_track.value
self._save_attribute(
"subtitle_track",
saved_subtitle_track if saved_subtitle_track > -2 else None,
)
def _watch_saved_audio_track(self):
saved_audio_track = self.saved_audio_track.value
self._save_attribute(
"audio_track",
saved_audio_track if saved_audio_track > -1 else None,
)
@overload
def _load_attribute(self, name: str, dfl: str) -> str: ...
@overload
def _load_attribute(self, name: str, dfl: bytes) -> bytes: ...
@overload
def _load_attribute(self, name: str, dfl: int) -> int: ...
def _load_attribute(self, name: str, dfl: str | bytes | int) -> str | bytes | int:
try:
strval = os.getxattr(self.full_path, f"user.lazy_player.{name}")
return type(dfl)(strval)
except OSError:
return dfl
def _save_attribute(self, name: str, value: str | bytes | float | int | None) -> None:
try:
if value is None:
os.removexattr(self.full_path, f"user.lazy_player.{name}")
else:
if isinstance(value, bytes):
os.setxattr(self.full_path, f"user.lazy_player.{name}", value)
else:
os.setxattr(
self.full_path, f"user.lazy_player.{name}", str(value).encode("utf8")
)
except OSError:
pass
class FileListModel(GObject.Object, Gio.ListModel):
"""A ListModel implementation for FileItems"""
__gtype_name__ = "FileListModel"
items: list[FileItem]
def __init__(self):
super().__init__()
self.items = []
def do_get_item_type(self) -> GObject.GType:
return GObject.type_from_name("FileItem")
def do_get_n_items(self) -> int:
return len(self.items)
def do_get_item(self, position: int) -> Optional[FileItem]:
if 0 <= position < len(self.items):
return self.items[position]
return None
def remove_all(self) -> None:
removed = len(self.items)
self.items = []
self.items_changed(0, removed, 0)
def set_items(self, items: list[FileItem]):
removed = len(self.items)
self.items = list(items)
self.items_changed(0, removed, len(self.items))
def append(self, item: FileItem) -> None:
pos = len(self.items)
self.items.append(item)
self.items_changed(pos, 0, 1)
def remove(self, position: int) -> None:
if 0 <= position < len(self.items):
self.items.pop(position)
self.items_changed(position, 1, 0)