Add episode browser

This commit is contained in:
2022-09-13 21:33:26 -06:00
parent 1e7d9db9cc
commit a38221cca2
9 changed files with 254 additions and 18 deletions
+48 -9
View File
@@ -3,6 +3,8 @@ from typing import Type
import sys import sys
from urllib import parse from urllib import parse
from collections import defaultdict
from PyQt5.QtWidgets import QApplication from PyQt5.QtWidgets import QApplication
from PyQt5.QtQml import qmlRegisterType, QQmlApplicationEngine from PyQt5.QtQml import qmlRegisterType, QQmlApplicationEngine
from PyQt5.QtQuick import QQuickImageProvider from PyQt5.QtQuick import QQuickImageProvider
@@ -20,11 +22,34 @@ from .qtmpv import MpvObject
import requests import requests
class Show(QObject): class Episode(QObject):
def __init__(self, source, parent=None): def __init__(self, source, parent=None):
super().__init__(parent) super().__init__(parent)
self._source = source 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) @pyqtProperty("QString", constant=True)
def title(self) -> str: def title(self) -> str:
return self._source["title"] return self._source["title"]
@@ -37,10 +62,6 @@ class Show(QObject):
def description(self) -> str: def description(self) -> str:
return self._source["description"] return self._source["description"]
@pyqtProperty(int, constant=True)
def episodes(self) -> int:
return self._source["episodes"]
@pyqtProperty(int, constant=True) @pyqtProperty(int, constant=True)
def watched(self) -> int: def watched(self) -> int:
return self._source["watched"] return self._source["watched"]
@@ -49,6 +70,10 @@ class Show(QObject):
def poster(self) -> str: def poster(self) -> str:
return self._source["poster"] return self._source["poster"]
@pyqtProperty(list, constant=True)
def episodes(self) -> list[Episode]:
return self._episodes
class ProviderImageProvider(QQuickImageProvider): class ProviderImageProvider(QQuickImageProvider):
def __init__(self, icon_data): def __init__(self, icon_data):
@@ -89,8 +114,20 @@ class Provider(QObject):
describe["icon"] 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") shows: dict = getUrl(self.url, "shows")
self._shows: dict[int, Show] = {e["id"]: Show(e) for e in shows["data"]}
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") recently_added: dict = getUrl(self.url, "recently_added")
self._recently_added: list[int] = recently_added["data"] self._recently_added: list[int] = recently_added["data"]
@@ -159,11 +196,13 @@ def main():
data_source = DataSource( data_source = DataSource(
[ [
"http://127.0.0.1:8080/a/", # "http://127.0.0.1:8080/a/",
"http://127.0.0.1:8080/b/", # "http://127.0.0.1:8080/b/",
"http://127.0.0.1:8080/c/", # "http://127.0.0.1:8080/c/",
"http://127.0.0.1:32520/",
] ]
) )
# data_source = DataSource([])
qmlRegisterType(DatabaseType(data_source), "Ikinuki.Client", 1, 0, "Database") qmlRegisterType(DatabaseType(data_source), "Ikinuki.Client", 1, 0, "Database")
# qmlRegisterType(Provider, "Ikinuki.Client", 1, 0, "Provider") # qmlRegisterType(Provider, "Ikinuki.Client", 1, 0, "Provider")
+1 -1
View File
@@ -18,7 +18,7 @@ Row {
ContentView { ContentView {
id: view id: view
viewSelected: selectedView == 1 viewSelected: selectedView == 1
currentIndex: selectedProvider + (browse ? db.Providers.length : 0) parentIndex: selectedProvider + (browse ? db.Providers.length : 0)
providers: db.Providers providers: db.Providers
} }
function mod(n, m) { function mod(n, m) {
+36 -7
View File
@@ -11,8 +11,15 @@ StackLayout {
id: tabView id: tabView
property var providers: [] property var providers: []
property bool viewSelected property bool viewSelected
state: viewSelected ? "selected" : "deselected" property int parentIndex
property int ySelect: 0 property int ySelect: 0
property bool showViewActive: false
currentIndex: showViewActive ? tabView.children.length - 3 : parentIndex
//currentIndex: showView ? tabView.children.length - 3 : parentIndex
state: viewSelected ? "selected" : "deselected"
width: parent.width * viewSelected ? 0.95 : 0.8 width: parent.width * viewSelected ? 0.95 : 0.8
height: parent.height height: parent.height
@@ -28,6 +35,10 @@ StackLayout {
provider: modelData provider: modelData
} }
} }
ShowView {
id: showView
show: providers[0].getShow(providers[0].showsAlphabetic[0])
}
states: [ states: [
State { State {
name: "deselected" name: "deselected"
@@ -54,14 +65,32 @@ StackLayout {
} }
] ]
function getCurrentIndex() {
if (showViewActive) {
var x = tabView.children.length - 1;
} else {
var x = currentIndex > (providers.length - 1) ? currentIndex + 1 : currentIndex;
}
return x;
}
Keys.onPressed: (event)=> { Keys.onPressed: (event)=> {
var x = currentIndex > (providers.length - 1) ? currentIndex + 1 : currentIndex; var x = getCurrentIndex()
//tabView.children[currentIndex].Keys.pressed(event);
tabView.children[x].Keys.pressed(event); tabView.children[x].Keys.pressed(event);
if (tabView.children[x].viewExit) { if (tabView.children[x].enterShow) {
tabView.children[x].viewExit = false; showViewActive = true;
parent.browse = false; showView.show = tabView.children[x].enterShowShow;
parent.selectedView = 0; } else if (tabView.children[x].viewExit) {
if (showViewActive) {
showViewActive = false;
tabView.children[x].xIndex = 0;
tabView.children[x].viewExit = false;
tabView.children[getCurrentIndex()].enterShow = false;
} else {
tabView.children[x].viewExit = false;
parent.browse = false;
parent.selectedView = 0;
}
} }
event.accepted = true; event.accepted = true;
} }
@@ -0,0 +1,34 @@
import QtQuick 2.12
import QtQuick.Layouts 1.12
import QtQuick.Controls 1.4
import QtQuick.Controls 2.15
import Ikinuki.Client 1.0
import "./ShowView"
ScrollView {
id: root
property var episodeModel
property int scrollIndex: 0
contentHeight: root.height * 0.1 * episodeModel.length
ScrollBar.vertical.position: scrollIndex / (episodeModel.length)
clip: true
Behavior on ScrollBar.vertical.position {
NumberAnimation {
duration: 200
easing.type: Easing.Linear
}
}
Column {
Repeater {
model: episodeModel
Episode {
episode: modelData
selected: index == xIndex
height: root.height * 0.1
width: root.width
}
}
}
}
@@ -11,6 +11,8 @@ Rectangle {
id: root id: root
property var provider property var provider
property bool viewExit: false property bool viewExit: false
property bool enterShow: false
property var enterShowShow
property int currentView: 0 property int currentView: 0
color: "#22282A" color: "#22282A"
Row { Row {
@@ -76,6 +78,9 @@ Rectangle {
coverGrid.scrollIndex--; coverGrid.scrollIndex--;
} }
} }
} else if (event.key == Qt.Key_Return) {
enterShowShow = root.provider.getShow(root.provider.showsAlphabetic[(coverGrid.xIndex + coverGrid.yIndex * coverGrid.numColumns)])
enterShow = true;
} }
} else if (currentView == 1) { // alphabet } else if (currentView == 1) { // alphabet
if (event.key == Qt.Key_Left) { if (event.key == Qt.Key_Left) {
@@ -11,8 +11,9 @@ Rectangle {
id: root id: root
property var provider property var provider
property int ySelect: 0 property int ySelect: 0
property int xSelect: 0
property bool viewExit: false property bool viewExit: false
property bool enterShow: false
property var enterShowShow
color: "#22282A" color: "#22282A"
Row { Row {
Item { Item {
@@ -82,6 +83,9 @@ Rectangle {
ySelect++; ySelect++;
} else if (event.key == Qt.Key_Up) { } else if (event.key == Qt.Key_Up) {
ySelect--; ySelect--;
} else if (event.key == Qt.Key_Return) {
enterShowShow = provider.getShow(elementColumn.children[ySelect].showId)
enterShow = true;
} }
event.accepted = true; event.accepted = true;
} }
@@ -0,0 +1,76 @@
import QtQuick 2.12
import QtQuick.Layouts 1.12
import QtQuick.Controls 1.4
import QtQuick.Controls 2.15
import Ikinuki.Client 1.0
import "./ShowView"
Rectangle {
property var show
property bool viewExit: false
property bool enterShow: false
property int xIndex: 0
color: "#22282A"
Column {
anchors.fill: parent
anchors.leftMargin: parent.width * 0.01
anchors.rightMargin: parent.width * 0.01
anchors.topMargin: parent.height * 0.05
anchors.bottomMargin: parent.height * 0.01
Text { // header
height: parent.height * 0.1
width: parent.width
text: show.title
font.pointSize: 20
color: "#cdd7d9"
}
Row { // main view
height: parent.height * 0.8
width: parent.width
spacing: parent.width * 0.01
Item {
height: parent.height
width: height * 0.68
Image {
source: show.poster
anchors.fill: parent
mipmap: true
}
}
EpisodeView {
id: episodeView
height: parent.height
width: parent.width * 0.65
episodeModel: show.episodes
}
}
Item {
height: parent.height * 0.1
width: parent.width
}
}
Keys.onPressed: (event)=> {
if (event.key == Qt.Key_Up) {
if (xIndex > 0) {
xIndex--;
if (episodeView.scrollIndex > xIndex) {
episodeView.scrollIndex--;
}
}
} else if (event.key == Qt.Key_Down) {
if (xIndex < show.episodes.length - 1) {
if (episodeView.scrollIndex < (xIndex - 8)) {
episodeView.scrollIndex++;
}
xIndex++;
}
} else if (event.key == Qt.Key_Escape) {
viewExit = true;
}
event.accepted = true;
}
}
@@ -0,0 +1,49 @@
import QtQuick 2.12
import QtQuick.Layouts 1.12
import QtQuick.Controls 1.4
import QtQuick.Controls 2.15
import Ikinuki.Client 1.0
Item {
property var episode
property bool selected
Rectangle {
anchors.fill: parent
color: "white"
visible: selected
radius: 10
}
Row {
anchors.fill: parent
Item {
height: parent.height
width: parent.width * 0.04
}
Column {
anchors.verticalCenter: parent.verticalCenter
anchors.verticalCenterOffset: 4
Text {
text: {
if (modelData.season == 0) {
return "S" + modelData.episode + " " + modelData.title
} else {
return String(modelData.episode).padStart(2, "0") + ". " + modelData.title
}
}
font.pointSize: 20
color: selected ? "#3c3c3c" : "#99afb4"
}
Item {
height: parent.height * 0.1
width: parent.width
}
Text {
text: " air date"
font.pointSize: 12
color: selected ? "#3c3c3c" : "#99afb4"
}
}
}
}