mirror of
https://github.com/HenkKalkwater/harbour-sailfin.git
synced 2024-11-22 01:05:17 +00:00
Added SortOrder and fallback pages.
Once again I couldn't stop myself from being sidetracked. [Collections]: Added: allow specifying sort order (Ascending, Descending) [General]: Improved: Added video fallback page to allow unknown video types to be played, although without extra metadata. [General]: Improved: Added folder fallback, so unknown collection types are at least displayed without metadata.
This commit is contained in:
parent
b68da318f2
commit
5057867ade
|
@ -67,6 +67,7 @@ DISTFILES += \
|
|||
qml/pages/itemdetails/SeasonPage.qml \
|
||||
qml/pages/itemdetails/SeriesPage.qml \
|
||||
qml/pages/itemdetails/UnsupportedPage.qml \
|
||||
qml/pages/itemdetails/VideoPage.qml \
|
||||
qml/pages/setup/AddServerConnectingPage.qml \
|
||||
qml/pages/setup/LoginDialog.qml \
|
||||
qml/qmldir
|
||||
|
|
|
@ -27,5 +27,8 @@ QtObject {
|
|||
readonly property real libraryDelegateHeight: Screen.width / 3
|
||||
|
||||
readonly property real libraryDelegatePosterHeight: Screen.width / 2
|
||||
|
||||
readonly property real libraryProgressHeight: Theme.paddingMedium
|
||||
|
||||
readonly property real horizontalVideoAspectRatio: 1.66666 //itemData.PrimaryImageAspectRatio
|
||||
}
|
||||
|
|
11
qml/Utils.js
11
qml/Utils.js
|
@ -60,19 +60,26 @@ function usePortraitCover(itemType) {
|
|||
/**
|
||||
* Returns the page url for a certain item type.
|
||||
*/
|
||||
function getPageUrl(itemType) {
|
||||
function getPageUrl(mediaType, itemType) {
|
||||
switch (itemType.toLowerCase()) {
|
||||
case "series":
|
||||
return Qt.resolvedUrl("pages/itemdetails/SeriesPage.qml")
|
||||
case "movie":
|
||||
return Qt.resolvedUrl("pages/itemdetails/FilmPage.qml")
|
||||
case "collection":
|
||||
return Qt.resolvedUrl("pages/itemdetails/ColectionPage.qml")
|
||||
return Qt.resolvedUrl("pages/itemdetails/CollectionPage.qml")
|
||||
case "season":
|
||||
return Qt.resolvedUrl("pages/itemdetails/SeasonPage.qml")
|
||||
case "episode":
|
||||
return Qt.resolvedUrl("pages/itemdetails/EpisodePage.qml")
|
||||
default:
|
||||
switch (mediaType ? mediaType.toLowerCase() : "folder") {
|
||||
case "folder":
|
||||
return Qt.resolvedUrl("pages/itemdetails/CollectionPage.qml")
|
||||
case "video":
|
||||
return Qt.resolvedUrl("pages/itemdetails/VideoPage.qml")
|
||||
default:
|
||||
return Qt.resolvedUrl("pages/itemdetails/UnsupportedPage.qml")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -202,7 +202,7 @@ Page {
|
|||
progress: model.userData.PlayedPercentage / 100
|
||||
|
||||
onClicked: {
|
||||
pageStack.push(Utils.getPageUrl(model.type), {"itemId": model.id})
|
||||
pageStack.push(Utils.getPageUrl(model.mediaType, model.type), {"itemId": model.id})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,17 +62,13 @@ BaseDetailPage {
|
|||
fillMode: Image.PreserveAspectCrop
|
||||
clip: true
|
||||
}
|
||||
Rectangle {
|
||||
Shim {
|
||||
anchors {
|
||||
left: parent.left
|
||||
bottom: parent.bottom
|
||||
right: parent.right
|
||||
}
|
||||
height: itemName.height + Theme.paddingSmall * 2
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 0.0; color: "transparent" }
|
||||
GradientStop { position: 1.0; color: Theme.highlightDimmerColor }
|
||||
}
|
||||
visible: itemImage.status !== Image.Null
|
||||
}
|
||||
Label {
|
||||
|
@ -96,7 +92,7 @@ BaseDetailPage {
|
|||
pageStack.push(Qt.resolvedUrl("CollectionPage.qml"), {"itemId": model.id})
|
||||
break;
|
||||
default:
|
||||
pageStack.push(Utils.getPageUrl(model.type), {"itemId": model.id})
|
||||
pageStack.push(Utils.getPageUrl(model.mediaType, model.type), {"itemId": model.id})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -110,6 +106,8 @@ BaseDetailPage {
|
|||
VerticalScrollDecorator {}
|
||||
}
|
||||
|
||||
// The page for selecting a sort order
|
||||
|
||||
Component {
|
||||
id: sortPageComponent
|
||||
Page {
|
||||
|
@ -139,8 +137,23 @@ BaseDetailPage {
|
|||
}
|
||||
text: model.name
|
||||
}
|
||||
onClicked: {
|
||||
collectionModel.sortBy = [model.value]
|
||||
menu: ContextMenu {
|
||||
MenuItem {
|
||||
//: Sort order
|
||||
text: qsTr("Ascending")
|
||||
onClicked: apply(model.value, ApiModel.Ascending)
|
||||
}
|
||||
MenuItem {
|
||||
//: Sort order
|
||||
text: qsTr("Descending")
|
||||
onClicked: apply(model.value, ApiModel.Descending)
|
||||
}
|
||||
}
|
||||
onClicked: openMenu()
|
||||
|
||||
function apply(field, order) {
|
||||
collectionModel.sortBy = [field];
|
||||
collectionModel.sortOrder = order;
|
||||
collectionModel.reload()
|
||||
pageStack.pop()
|
||||
}
|
||||
|
|
|
@ -122,7 +122,7 @@ BaseDetailPage {
|
|||
wrapMode: Text.WordWrap
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
onClicked: pageStack.push(Utils.getPageUrl(model.type), {"itemId": model.id})
|
||||
onClicked: pageStack.push(Utils.getPageUrl(model.mediaType, model.type), {"itemId": model.id})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -80,7 +80,7 @@ BaseDetailPage {
|
|||
delegate: LibraryItemDelegate {
|
||||
poster: Utils.itemModelImageUrl(ApiClient.baseUrl, model.id, model.imageTags["Primary"], "Primary", {"maxHeight": height})
|
||||
title: model.name
|
||||
onClicked: pageStack.push(Utils.getPageUrl(model.type), {"itemId": model.id})
|
||||
onClicked: pageStack.push(Utils.getPageUrl(model.mediaType, model.type), {"itemId": model.id})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
63
qml/pages/itemdetails/VideoPage.qml
Normal file
63
qml/pages/itemdetails/VideoPage.qml
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
Sailfin: a Jellyfin client written using Qt
|
||||
Copyright (C) 2020 Chris Josten
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import QtQuick 2.6
|
||||
import Sailfish.Silica 1.0
|
||||
|
||||
import nl.netsoj.chris.Jellyfin 1.0
|
||||
|
||||
import "../../components"
|
||||
import "../.."
|
||||
|
||||
/**
|
||||
* Fallback page for everything that's a video, but we haven't a more specific page for, like
|
||||
* the FilmPage or EpisodePage.
|
||||
*/
|
||||
BaseDetailPage {
|
||||
SilicaFlickable {
|
||||
anchors.fill: parent
|
||||
contentHeight: content.height
|
||||
|
||||
Column {
|
||||
id: content
|
||||
width: parent.width
|
||||
spacing: Theme.paddingMedium
|
||||
|
||||
PageHeader {
|
||||
title: itemData.Name
|
||||
description: qsTr("Run time: %2").arg(Utils.ticksToText(itemData.RunTimeTicks))
|
||||
}
|
||||
|
||||
PlayToolbar {
|
||||
width: parent.width
|
||||
imageSource: Utils.itemImageUrl(ApiClient.baseUrl, itemData, "Primary", {"maxWidth": parent.width})
|
||||
imageAspectRatio: Constants.horizontalVideoAspectRatio
|
||||
onPlayPressed: pageStack.push(Qt.resolvedUrl("../../pages/VideoPage.qml"),
|
||||
{"itemId": itemId, "itemData": itemData, "audioTrack": trackSelector.audioTrack,
|
||||
"subtitleTrack": trackSelector.subtitleTrack })
|
||||
}
|
||||
|
||||
VideoTrackSelector {
|
||||
id: trackSelector
|
||||
width: parent.width
|
||||
tracks: itemData.MediaStreams
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -60,6 +60,9 @@ void ApiModel::load(LoadType type) {
|
|||
if (!m_sortBy.empty()) {
|
||||
query.addQueryItem("SortBy", m_sortBy.join(","));
|
||||
}
|
||||
if (m_sortOrder != Unspecified) {
|
||||
query.addQueryItem("SortOrder", m_sortOrder == Ascending ? "Ascending" : "Descending");
|
||||
}
|
||||
if (!m_imageTypes.empty()) {
|
||||
query.addQueryItem("ImageTypes", m_imageTypes.join(","));
|
||||
}
|
||||
|
@ -208,7 +211,7 @@ void ApiModel::addQueryParameters(QUrlQuery &query) { Q_UNUSED(query)}
|
|||
|
||||
void registerModels(const char *URI) {
|
||||
qmlRegisterUncreatableType<ApiModel>(URI, 1, 0, "ApiModel", "Is enum and base class");
|
||||
qmlRegisterUncreatableType<SortOrder>(URI, 1, 0, "SortOrder", "Is enum");
|
||||
qmlRegisterUncreatableType<SortOptions>(URI, 1, 0, "SortOptions", "Is enum");
|
||||
qmlRegisterType<PublicUserModel>(URI, 1, 0, "PublicUserModel");
|
||||
qmlRegisterType<UserViewModel>(URI, 1, 0, "UserViewModel");
|
||||
qmlRegisterType<UserItemModel>(URI, 1, 0, "UserItemModel");
|
||||
|
|
|
@ -31,9 +31,10 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|||
#include "jellyfinapiclient.h"
|
||||
|
||||
namespace Jellyfin {
|
||||
class SortOrder {
|
||||
Q_GADGET
|
||||
class SortOptions : public QObject{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SortOptions (QObject *parent = nullptr) : QObject(parent) {}
|
||||
enum SortBy {
|
||||
Album,
|
||||
AlbumArtist,
|
||||
|
@ -54,6 +55,7 @@ public:
|
|||
Q_ENUM(SortBy)
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Abstract model for displaying a REST JSON collection. Role names will be based on the fields encountered in the
|
||||
* first record.
|
||||
|
@ -90,6 +92,13 @@ public:
|
|||
};
|
||||
Q_ENUM(ModelStatus)
|
||||
|
||||
enum SortOrder {
|
||||
Unspecified,
|
||||
Ascending,
|
||||
Descending
|
||||
};
|
||||
Q_ENUM(SortOrder)
|
||||
|
||||
/**
|
||||
* @brief Creates a new basemodel
|
||||
* @param path The path (relative to the baseUrl of JellyfinApiClient) to make the call to.
|
||||
|
@ -130,6 +139,7 @@ public:
|
|||
Q_PROPERTY(QString seasonId MEMBER m_seasonId NOTIFY seasonIdChanged)
|
||||
Q_PROPERTY(QList<QString> imageTypes MEMBER m_imageTypes NOTIFY imageTypesChanged)
|
||||
Q_PROPERTY(bool recursive MEMBER m_recursive)
|
||||
Q_PROPERTY(SortOrder sortOrder MEMBER m_sortOrder NOTIFY sortOrderChanged)
|
||||
|
||||
// Path properties
|
||||
Q_PROPERTY(QString show MEMBER m_show NOTIFY showChanged)
|
||||
|
@ -164,6 +174,7 @@ signals:
|
|||
void limitChanged(int newLimit);
|
||||
void parentIdChanged(QString newParentId);
|
||||
void sortByChanged(QList<QString> newSortOrder);
|
||||
void sortOrderChanged(SortOrder newSortOrder);
|
||||
void showChanged(QString newShow);
|
||||
void seasonIdChanged(QString newSeasonId);
|
||||
void fieldsChanged(QList<QString> newFields);
|
||||
|
@ -214,6 +225,7 @@ protected:
|
|||
QList<QString> m_fields;
|
||||
QList<QString> m_imageTypes;
|
||||
QList<QString> m_sortBy = {};
|
||||
SortOrder m_sortOrder = Unspecified;
|
||||
bool m_recursive = false;
|
||||
|
||||
QHash<int, QByteArray> m_roles;
|
||||
|
@ -228,7 +240,7 @@ private:
|
|||
* @brief Generates roleNames based on the first record in m_array.
|
||||
*/
|
||||
void generateFields();
|
||||
QString sortByToString(SortOrder::SortBy sortBy);
|
||||
QString sortByToString(SortOptions::SortBy sortBy);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -93,6 +93,16 @@
|
|||
<source>Date added</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ascending</source>
|
||||
<extracomment>Sort order</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Descending</source>
|
||||
<extracomment>Sort order</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>CoverPage</name>
|
||||
|
@ -104,11 +114,15 @@
|
|||
<context>
|
||||
<name>EpisodePage</name>
|
||||
<message>
|
||||
<source>Episode %1–%2 Season %3</source>
|
||||
<source>Episode %1–%2 | %3</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Episode %1 Season %2</source>
|
||||
<source>Episode %1 | %2</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Overview</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
|
@ -118,6 +132,10 @@
|
|||
<source>Released: %1 — Run time: %2</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Overview</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>LegalPage</name>
|
||||
|
@ -301,6 +319,13 @@
|
|||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>VideoPage</name>
|
||||
<message>
|
||||
<source>Run time: %2</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>VideoTrackSelector</name>
|
||||
<message>
|
||||
|
|
Loading…
Reference in a new issue