from typing import Type import os import sys from urllib import parse from collections import defaultdict from PyQt5.QtWidgets import QApplication from PyQt5.QtQml import qmlRegisterType, QQmlApplicationEngine from PyQt5.QtQuick import QQuickImageProvider # from PyQt5.QtQuick import QQuickView from PyQt5.QtCore import QObject, pyqtProperty, pyqtSlot from PyQt5.QtGui import QImage # from PyQt5.QtCore import QObject, QUrl, pyqtProperty from .qtmpv import MpvObject import requests import toml class Episode(QObject): def __init__(self, source, parent=None): super().__init__(parent) self._source = source @pyqtProperty("QString", constant=True) def title(self): return self._source["Title"] @pyqtProperty(int, constant=True) def season(self): return self._source["Season"] @pyqtProperty(int, constant=True) def episode(self): return self._source["Episode"] @pyqtProperty("QString", constant=True) def filename(self): return self._source["OriginalFilename"] class Show(QObject): def __init__(self, source, episodes, parent=None): super().__init__(parent) self._source = source self._episodes = episodes @pyqtProperty("QString", constant=True) def title(self) -> str: return self._source["title"] @pyqtProperty(int, constant=True) def year(self) -> int: return self._source["year"] @pyqtProperty("QString", constant=True) def description(self) -> str: return self._source["description"] @pyqtProperty(int, constant=True) def watched(self) -> int: return self._source["watched"] @pyqtProperty("QString", constant=True) def poster(self) -> str: return self._source["poster"] @pyqtProperty(list, constant=True) def episodes(self) -> list[Episode]: return self._episodes class ProviderImageProvider(QQuickImageProvider): def __init__(self, icon_data): self._icon_data = icon_data super(ProviderImageProvider, self).__init__(QQuickImageProvider.Image) def requestImage(self, p_str, size): print(p_str) print(size) # img = QImage.fromData(self._icon_data.encode("utf-8")) import base64 data: bytes = base64.b64decode(self._icon_data) img = QImage.fromData(data) # img = QImage.fromData(self._icon_data.encode("utf-8")) # img = QImage(300, 300, QImage.Format_RGBA8888) # img.fill(Qt.red) return img, img.size() def getUrl(base: str, path: str) -> dict: url: str = parse.urljoin(base, path) r: requests.Response = requests.get(url) return r.json() class Provider(QObject): def __init__(self, url: str, parent=None): super().__init__(parent) self.url: str = url describe: dict = getUrl(self.url, "describe") self._name: str = describe["name"] self.logo_provider: ProviderImageProvider = ProviderImageProvider( describe["icon"] ) def default_val(): return [] episodes: dict = getUrl(self.url, "episodes") _episodes: defaultdict[str, list[Episode]] = defaultdict(default_val) for e in episodes["episodes"]: _episodes[e["ShowTitle"]].append(Episode(e)) shows: dict = getUrl(self.url, "shows") self._shows: dict[int, Show] = { e["id"]: Show(e, _episodes[e["title"]]) for e in shows["data"] } recently_added: dict = getUrl(self.url, "recently_added") self._recently_added: list[int] = recently_added["data"] in_progress: dict = getUrl(self.url, "in_progress") self._in_progress: list[int] = in_progress["data"] @pyqtProperty("QString", constant=True) def name(self) -> str: return self._name @pyqtProperty("QString", constant=True) def logo(self) -> str: return f"image://{self._name}/logo" # @pyqtProperty("QObject") @pyqtSlot(int, result=QObject) def getShow(self, id) -> Show: return self._shows[id] @pyqtProperty(list, constant=True) def showsAlphabetic(self) -> list[int]: return [ elem[0] for elem in sorted( [(id, show) for id, show in self._shows.items()], key=lambda elem: elem[1].title, ) ] @pyqtProperty(list, constant=True) def recentlyAdded(self) -> list[int]: return self._recently_added @pyqtProperty(list, constant=True) def inProgress(self) -> list[int]: return self._in_progress class DataSource: def __init__(self, providers=[]): self.providers: list[Provider] = [Provider(url) for url in providers] def DatabaseType(data_source) -> Type: class Database(QObject): def __init__(self, parent=None): super().__init__(parent) self.data_source: DataSource = data_source @pyqtProperty(list, constant=True) def Providers(self) -> list[Provider]: return self.data_source.providers return Database def load_config() -> list[str]: try: config_dir: str = os.path.join(os.environ["XDG_CONFIG_HOME"], "ikinuki") except: config_dir: str = os.path.join(os.environ["HOME"], ".config", "ikinuki") os.makedirs(config_dir, exist_ok=True) config_file: str = os.path.join(config_dir, "client.toml") try: config: dict = toml.load(config_file) except FileNotFoundError: print(f'Config file not found at "{config_file}"') print("Writing example config file. Please update and relaunch.") default_config = """# [[backends]] # address = "127.0.0.1" # port = 32520""" with open(config_file, "w") as f: f.write(default_config) sys.exit(-1) return [f'http://{b["address"]}:{b["port"]}/' for b in config["backends"]] def main(): app = QApplication(sys.argv) qmlRegisterType(MpvObject, "Ikinuki.Client", 1, 0, "Mpv") import locale locale.setlocale(locale.LC_NUMERIC, "C") try: backends: list[str] = load_config() except Exception as e: print(f"ERROR: Could not load config file: {repr(e)}") sys.exit(-1) data_source = DataSource(backends) qmlRegisterType(DatabaseType(data_source), "Ikinuki.Client", 1, 0, "Database") # qmlRegisterType(Provider, "Ikinuki.Client", 1, 0, "Provider") engine = QQmlApplicationEngine() for provider in data_source.providers: print(f"Registering provider with name {provider._name}") engine.addImageProvider( provider._name, provider.logo_provider, ) engine.load("layouts/ikinuki-default.qml") win = QObject() win = engine.rootObjects()[0] win.show() sys.exit(app.exec_()) # # # # app = QApplication([]) # # # window = QQmlApplicationEngine("layouts/mpv.qml") # window.run # # view = QQuickView() # url = QUrl("layouts/mpv.qml") # # import locale # # locale.setlocale(locale.LC_NUMERIC, 'C') # # view.setSource(url) # view.show() # app.exec_()