lazy-player/lazy_player/file_model.py

161 lines
4.7 KiB
Python

from __future__ import annotations
import os
import sys
from enum import Enum, auto
from pathlib import Path
from typing import Optional, overload
from gi.repository import Gio, GObject
from .thumbnailer import Thumbnailer
class FileType(Enum):
DIRECTORY = auto()
VIDEO = auto()
class FileItem(GObject.Object):
file_type: FileType
full_path: Path
thumbnail: bytes
attempted_thumbnail: bool
_has_thumbnail: bool
__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 = b""
self.attempted_thumbnail = False
self._has_thumbnail = False
@GObject.Property(type=GObject.TYPE_UINT64)
def saved_position(self) -> int:
return self._load_attribute("position", 0)
@saved_position.setter
def set_saved_position(self, value: int):
self._save_attribute("position", value if value else None)
self.notify("saved-position")
@GObject.Property(type=GObject.TYPE_UINT64)
def saved_duration(self):
return self._load_attribute("duration", 1) or 1
@saved_duration.setter
def set_saved_duration(self, value: int):
self._save_attribute("duration", value if value > 0 else None)
self.notify("saved-duration")
@GObject.Property(type=int)
def saved_subtitle_track(self):
return self._load_attribute("subtitle_track", -2)
@saved_subtitle_track.setter
def set_saved_subtitle_track(self, value: int):
self._save_attribute("subtitle_track", value if value >= -1 else None)
self.notify("saved-subtitle-track")
@GObject.Property(type=int)
def saved_audio_track(self):
return self._load_attribute("audio_track", 0)
@saved_audio_track.setter
def set_saved_audio_track(self, value: int):
self._save_attribute("audio_track", value if value > 0 else None)
self.notify("saved-audio-track")
@GObject.Property(type=bool, default=False)
def has_thumbnail(self):
return self._has_thumbnail
@has_thumbnail.setter
def set_has_thumbnail(self, value: bool):
self._has_thumbnail = value
self.notify("has-thumbnail")
def ensure_thumbnail(self, thumbnailer: Thumbnailer):
if self.thumbnail or self.attempted_thumbnail:
return
if not self.attempted_thumbnail:
thumbnailer.generate_thumbnail(self)
@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 as err:
print(err, file=sys.stderr)
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)