270 lines
7.0 KiB
Python
270 lines
7.0 KiB
Python
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_()
|