From b68da318f2154e0009c3c8d3ed61e67c3a13be3c Mon Sep 17 00:00:00 2001 From: Chris Josten Date: Tue, 29 Sep 2020 02:15:50 +0200 Subject: [PATCH] Restructured the way item "details" are loaded Previously, Items were displayed in one page, named DetailPage.qml. This page then would load a qml component, based on the page type. It also contained some components common for each detail page, like displaying the name of the item. This construction had as downside that modifying the page properties, adding a pulley menu or basing the page around a SilicaListview was not possible. So I already had created some other pages. The new construction uses a base page, named BaseDetailPage which does set some common properties and handle the loading of the items, so that that part does not have to be duplicated. Displaying the name of an item was a very trivial thing to do, so duplicating that part across files was not a problem. Anyway, the rest of the pages are now seperate, but all have BaseDetailsPage as the root, so they can use the common functionality by that page. Those subpages now can be based around GridViews, Carrousels, have pully menus and so on. To determine to which page to go to, based on the content type, a function named getPageUrl has been added to Utils.js, which takes a content type as argument and gives the page url back. --- harbour-sailfin.pro | 19 +-- qml/Utils.js | 22 ++- qml/components/PlayToolbar.qml | 61 ++++++++ .../{itemdetails => }/VideoTrackSelector.qml | 0 .../itemdetails/CollectionFolder.qml | 24 --- qml/components/itemdetails/EpisodeDetails.qml | 62 -------- qml/components/itemdetails/FilmDetails.qml | 53 ------- qml/components/itemdetails/PlayToolbar.qml | 43 ------ qml/components/itemdetails/SeasonDetails.qml | 122 ---------------- qml/components/itemdetails/SeriesDetails.qml | 68 --------- qml/pages/MainPage.qml | 4 +- .../BaseDetailPage.qml} | 82 ++--------- .../{ => itemdetails}/CollectionPage.qml | 55 +------ qml/pages/itemdetails/EpisodePage.qml | 75 ++++++++++ qml/pages/itemdetails/FilmPage.qml | 70 +++++++++ qml/pages/itemdetails/MusicAlbumPage.qml | 5 + qml/pages/itemdetails/SeasonPage.qml | 137 ++++++++++++++++++ qml/pages/itemdetails/SeriesPage.qml | 93 ++++++++++++ .../itemdetails/UnsupportedPage.qml} | 17 ++- qml/qmldir | 1 + translations/harbour-sailfin.ts | 21 +-- 21 files changed, 509 insertions(+), 525 deletions(-) create mode 100644 qml/components/PlayToolbar.qml rename qml/components/{itemdetails => }/VideoTrackSelector.qml (100%) delete mode 100644 qml/components/itemdetails/CollectionFolder.qml delete mode 100644 qml/components/itemdetails/EpisodeDetails.qml delete mode 100644 qml/components/itemdetails/FilmDetails.qml delete mode 100644 qml/components/itemdetails/PlayToolbar.qml delete mode 100644 qml/components/itemdetails/SeriesDetails.qml rename qml/pages/{DetailPage.qml => itemdetails/BaseDetailPage.qml} (57%) rename qml/pages/{ => itemdetails}/CollectionPage.qml (79%) create mode 100644 qml/pages/itemdetails/EpisodePage.qml create mode 100644 qml/pages/itemdetails/FilmPage.qml create mode 100644 qml/pages/itemdetails/MusicAlbumPage.qml create mode 100644 qml/pages/itemdetails/SeasonPage.qml create mode 100644 qml/pages/itemdetails/SeriesPage.qml rename qml/{components/itemdetails/UnsupportedDetails.qml => pages/itemdetails/UnsupportedPage.qml} (71%) diff --git a/harbour-sailfin.pro b/harbour-sailfin.pro index 0187e82..a665637 100644 --- a/harbour-sailfin.pro +++ b/harbour-sailfin.pro @@ -41,31 +41,32 @@ DISTFILES += \ qml/components/LibraryItemDelegate.qml \ qml/components/MoreSection.qml \ qml/components/PlainLabel.qml \ + qml/components/PlayToolbar.qml \ qml/components/RemoteImage.qml \ qml/components/Shim.qml \ qml/components/UserGridDelegate.qml \ qml/components/VideoPlayer.qml \ - qml/components/itemdetails/CollectionFolder.qml \ - qml/components/itemdetails/EpisodeDetails.qml \ - qml/components/itemdetails/FilmDetails.qml \ - qml/components/itemdetails/PlayToolbar.qml \ + qml/components/VideoTrackSelector.qml \ qml/components/itemdetails/SeasonDetails.qml \ - qml/components/itemdetails/SeriesDetails.qml \ - qml/components/itemdetails/UnsupportedDetails.qml \ - qml/components/itemdetails/VideoTrackSelector.qml \ qml/components/videoplayer/VideoError.qml \ qml/components/videoplayer/VideoHud.qml \ qml/cover/CoverPage.qml \ qml/cover/PosterCover.qml \ qml/cover/VideoCover.qml \ - qml/pages/CollectionPage.qml \ - qml/pages/DetailPage.qml \ qml/pages/LegalPage.qml \ qml/pages/MainPage.qml \ qml/pages/AboutPage.qml \ qml/harbour-sailfin.qml \ qml/pages/SettingsPage.qml \ qml/pages/VideoPage.qml \ + qml/pages/itemdetails/BaseDetailPage.qml \ + qml/pages/itemdetails/CollectionPage.qml \ + qml/pages/itemdetails/EpisodePage.qml \ + qml/pages/itemdetails/FilmPage.qml \ + qml/pages/itemdetails/MusicAlbumPage.qml \ + qml/pages/itemdetails/SeasonPage.qml \ + qml/pages/itemdetails/SeriesPage.qml \ + qml/pages/itemdetails/UnsupportedPage.qml \ qml/pages/setup/AddServerConnectingPage.qml \ qml/pages/setup/LoginDialog.qml \ qml/qmldir diff --git a/qml/Utils.js b/qml/Utils.js index 4f67621..efbe310 100644 --- a/qml/Utils.js +++ b/qml/Utils.js @@ -40,7 +40,7 @@ function ticksToText(ticks) { function itemImageUrl(baseUrl, item, type, options) { if (!item.ImageTags[type]) { return "" } return itemModelImageUrl(baseUrl, item.Id, item.ImageTags[type], type, options) - } +} function itemModelImageUrl(baseUrl, itemId, tag, type, options) { if (tag == undefined) return "" @@ -56,3 +56,23 @@ function itemModelImageUrl(baseUrl, itemId, tag, type, options) { function usePortraitCover(itemType) { return ["Series", "Movie", "tvshows", "movies"].indexOf(itemType) >= 0 } + +/** + * Returns the page url for a certain item type. + */ +function getPageUrl(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") + case "season": + return Qt.resolvedUrl("pages/itemdetails/SeasonPage.qml") + case "episode": + return Qt.resolvedUrl("pages/itemdetails/EpisodePage.qml") + default: + return Qt.resolvedUrl("pages/itemdetails/UnsupportedPage.qml") + } +} diff --git a/qml/components/PlayToolbar.qml b/qml/components/PlayToolbar.qml new file mode 100644 index 0000000..d1b6e1f --- /dev/null +++ b/qml/components/PlayToolbar.qml @@ -0,0 +1,61 @@ +/* +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 + +Column { + property alias imageSource : playImage.source + property real imageAspectRatio: 1.0 + signal playPressed() + spacing: Theme.paddingLarge + + BackgroundItem { + width: parent.width + height: width / imageAspectRatio + HighlightImage { + id: playImage + anchors.fill: parent + fillMode: Image.PreserveAspectCrop + color: Theme.overlayBackgroundColor + clip: true + } + Icon { + id: playButton + source: "image://theme/icon-l-play" + anchors.centerIn: parent + highlighted: parent.highlighted + } + onClicked: playPressed() + } + Row { + anchors { + //left: parent.left + right: parent.right + leftMargin: Theme.horizontalPageMargin + rightMargin: Theme.horizontalPageMargin + } + spacing: Theme.paddingMedium + IconButton { + id: favouriteButton + icon.source: "image://theme/icon-m-favorite" + } + + } +} diff --git a/qml/components/itemdetails/VideoTrackSelector.qml b/qml/components/VideoTrackSelector.qml similarity index 100% rename from qml/components/itemdetails/VideoTrackSelector.qml rename to qml/components/VideoTrackSelector.qml diff --git a/qml/components/itemdetails/CollectionFolder.qml b/qml/components/itemdetails/CollectionFolder.qml deleted file mode 100644 index 4f0aa37..0000000 --- a/qml/components/itemdetails/CollectionFolder.qml +++ /dev/null @@ -1,24 +0,0 @@ -/* -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 - -Item { - -} diff --git a/qml/components/itemdetails/EpisodeDetails.qml b/qml/components/itemdetails/EpisodeDetails.qml deleted file mode 100644 index 8331545..0000000 --- a/qml/components/itemdetails/EpisodeDetails.qml +++ /dev/null @@ -1,62 +0,0 @@ -/* -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 ".." -import "../../Utils.js" as Utils - -Column { - property var itemData - spacing: Theme.paddingMedium - - PlayToolbar { - 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 - } - - PlainLabel { - id: tinyDetails - text: { - if (typeof itemData.IndexNumberEnd !== "undefined") { - qsTr("Episode %1–%2 Season %3").arg(itemData.IndexNumber) - .arg(itemData.IndexNumberEnd) - .arg(itemData.ParentIndexNumber) - } else { - qsTr("Episode %1 Season %2").arg(itemData.IndexNumber).arg(itemData.ParentIndexNumber) - } - } - } - - PlainLabel { - id: overviewText - text: itemData.Overview - font.pixelSize: Theme.fontSizeSmall - color: Theme.secondaryHighlightColor - } -} diff --git a/qml/components/itemdetails/FilmDetails.qml b/qml/components/itemdetails/FilmDetails.qml deleted file mode 100644 index bc4850a..0000000 --- a/qml/components/itemdetails/FilmDetails.qml +++ /dev/null @@ -1,53 +0,0 @@ -/* -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 "../" -import "../../Utils.js" as Utils - -Column { - property var itemData - spacing: Theme.paddingMedium - - PlayToolbar { - 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 - } - - PlainLabel { - id: tinyDetails - text: qsTr("Released: %1 — Run time: %2").arg(itemData.ProductionYear).arg(Utils.ticksToText(itemData.RunTimeTicks)) - } - - PlainLabel { - id: overviewText - text: itemData.Overview - font.pixelSize: Theme.fontSizeSmall - color: Theme.secondaryHighlightColor - } -} diff --git a/qml/components/itemdetails/PlayToolbar.qml b/qml/components/itemdetails/PlayToolbar.qml deleted file mode 100644 index 7262294..0000000 --- a/qml/components/itemdetails/PlayToolbar.qml +++ /dev/null @@ -1,43 +0,0 @@ -/* -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 - -Row { - signal playPressed() - - anchors { - //left: parent.left - right: parent.right - leftMargin: Theme.horizontalPageMargin - rightMargin: Theme.horizontalPageMargin - } - spacing: Theme.paddingMedium - IconButton { - id: favouriteButton - icon.source: "image://theme/icon-m-favorite" - } - IconButton { - id: playButton - icon.source: "image://theme/icon-l-play" - onPressed: playPressed() - } - -} diff --git a/qml/components/itemdetails/SeasonDetails.qml b/qml/components/itemdetails/SeasonDetails.qml index 7353b34..8b13789 100644 --- a/qml/components/itemdetails/SeasonDetails.qml +++ b/qml/components/itemdetails/SeasonDetails.qml @@ -1,123 +1 @@ -/* -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 "../../Utils.js" as Utils -import "../.." -import ".." - -Column { - property var itemData - - ShowEpisodesModel { - id: episodeModel - apiClient: ApiClient - show: itemData.SeriesId - seasonId: itemData.Id - fields: ["Overview"] - } - - ColumnView { - model: episodeModel - itemHeight: Constants.libraryDelegateHeight - delegate: BackgroundItem { - height: Constants.libraryDelegateHeight - RemoteImage { - id: episodeImage - anchors { - top: parent.top - left: parent.left - bottom: parent.bottom - } - width: Constants.libraryDelegateWidth - height: Constants.libraryDelegateHeight - source: Utils.itemModelImageUrl(ApiClient.baseUrl, model.id, model.imageTags["Primary"], "Primary", {"maxHeight": height}) - fillMode: Image.PreserveAspectCrop - clip: true - - // Makes the progress bar stand out more - Shim { - anchors { - left: parent.left - bottom: parent.bottom - right: parent.right - } - height: parent.height / 3 - shimColor: Theme.overlayBackgroundColor - shimOpacity: Theme.opacityOverlay - //width: model.userData.PlayedPercentage * parent.width / 100 - visible: episodeProgress.width > 0 // It doesn't look nice when it's visible on every image - } - - Rectangle { - id: episodeProgress - anchors { - left: parent.left - bottom: parent.bottom - } - height: Theme.paddingMedium - width: model.userData.PlayedPercentage * parent.width / 100 - color: Theme.highlightColor - } - } - - Label { - id: episodeTitle - anchors { - left: episodeImage.right - leftMargin: Theme.paddingLarge - top: parent.top - right: parent.right - rightMargin: Theme.horizontalPageMargin - } - text: model.name - truncationMode: TruncationMode.Fade - horizontalAlignment: Text.AlignLeft - } - - Label { - id: episodeOverview - anchors { - left: episodeImage.right - leftMargin: Theme.paddingLarge - right: parent.right - rightMargin: Theme.horizontalPageMargin - top: episodeTitle.bottom - bottom: parent.bottom - } - color: highlighted ? Theme.secondaryHighlightColor: Theme.secondaryColor - font.pixelSize: Theme.fontSizeExtraSmall - //: No overview/summary text of an episode available - text: model.overview || qsTr("No overview available") - wrapMode: Text.WordWrap - elide: Text.ElideRight - } - onClicked: pageStack.push(Qt.resolvedUrl("../../pages/DetailPage.qml"), {"itemId": model.id}) - } - } - onItemDataChanged: { - console.log(JSON.stringify(itemData)) - episodeModel.show = itemData.SeriesId - episodeModel.seasonId = itemData.Id - episodeModel.reload() - } -} diff --git a/qml/components/itemdetails/SeriesDetails.qml b/qml/components/itemdetails/SeriesDetails.qml deleted file mode 100644 index f5e24fa..0000000 --- a/qml/components/itemdetails/SeriesDetails.qml +++ /dev/null @@ -1,68 +0,0 @@ -/* -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 "../" -import "../../Utils.js" as Utils - -Column { - property var itemData - - PlainLabel { - id: overviewText - text: itemData.Overview - font.pixelSize: Theme.fontSizeSmall - color: Theme.secondaryHighlightColor - } - - SectionHeader { - //: Seasons of a (TV) show - text: qsTr("Seasons") - } - - ShowSeasonsModel { - id: showSeasonsModel - apiClient: ApiClient - show: itemData.Id - } - - SilicaListView { - model: showSeasonsModel - clip: true - width: parent.width - height: Screen.width / 2 - orientation: ListView.Horizontal - spacing: Theme.paddingLarge - leftMargin: Theme.horizontalPageMargin - rightMargin: Theme.horizontalPageMargin - delegate: LibraryItemDelegate { - poster: Utils.itemModelImageUrl(ApiClient.baseUrl, model.id, model.imageTags["Primary"], "Primary", {"maxHeight": height}) - title: model.name - onClicked: pageStack.push(Qt.resolvedUrl("../../pages/DetailPage.qml"), {"itemId": model.id}) - } - } - - onItemDataChanged: { - showSeasonsModel.show = itemData.Id - showSeasonsModel.reload() - } -} diff --git a/qml/pages/MainPage.qml b/qml/pages/MainPage.qml index 8645b84..afd82ab 100644 --- a/qml/pages/MainPage.qml +++ b/qml/pages/MainPage.qml @@ -94,7 +94,7 @@ Page { text: model.name busy: userItemModel.status != ApiModel.Ready - onHeaderClicked: pageStack.push(Qt.resolvedUrl("CollectionPage.qml"), {"itemId": model.id}) + onHeaderClicked: pageStack.push(Qt.resolvedUrl("itemdetails/CollectionPage.qml"), {"itemId": model.id}) Loader { width: parent.width sourceComponent: carrouselView @@ -202,7 +202,7 @@ Page { progress: model.userData.PlayedPercentage / 100 onClicked: { - pageStack.push(Qt.resolvedUrl("DetailPage.qml"), {"itemId": model.id}) + pageStack.push(Utils.getPageUrl(model.type), {"itemId": model.id}) } } } diff --git a/qml/pages/DetailPage.qml b/qml/pages/itemdetails/BaseDetailPage.qml similarity index 57% rename from qml/pages/DetailPage.qml rename to qml/pages/itemdetails/BaseDetailPage.qml index c85070b..7ce36df 100644 --- a/qml/pages/DetailPage.qml +++ b/qml/pages/itemdetails/BaseDetailPage.qml @@ -21,8 +21,7 @@ import Sailfish.Silica 1.0 import nl.netsoj.chris.Jellyfin 1.0 -import "../components" -import "../components/itemdetails" +import "../../components" /** * This page displays details about a film, show, season, episode, and so on. @@ -35,11 +34,11 @@ Page { property string itemId: "" property var itemData: ({}) property bool _loading: true - readonly property bool _hasLogo: itemData.ImageTags.Logo !== undefined - readonly property string _logo: itemData.ImageTags.Logo + readonly property bool hasLogo: (typeof itemData.ImageTags !== "undefined") && (typeof itemData.ImageTags["Logo"] !== "undefined") readonly property var _backdropImages: itemData.BackdropImageTags readonly property var _parentBackdropImages: itemData.ParentBackdropImageTags readonly property string parentId: itemData.ParentId || "" + property alias backdrop: backdrop on_BackdropImagesChanged: updateBackdrop() on_ParentBackdropImagesChanged: updateBackdrop() @@ -62,72 +61,6 @@ Page { anchors.fill: parent } - SilicaFlickable { - anchors.fill: parent - contentHeight: content.height - - Column { - id: content - width: parent.width - - PageHeader { - title: itemData.Name || qsTr("Loading") - visible: !_hasLogo - } - - Column { - width: parent.width - Item { - width: 1 - height: Theme.paddingLarge - } - RemoteImage { - anchors { - horizontalCenter: parent.horizontalCenter - } - source: _hasLogo ? ApiClient.baseUrl + "/Items/" + itemId + "/Images/Logo?tag=" + _logo : "" - } - Item { - width: 1 - height: Theme.paddingLarge - } - visible: _hasLogo - } - - Item { - width: 1 - height: Theme.paddingLarge - } - - Loader { - active: itemData != undefined - asynchronous: true - width: parent.width - source: { - switch (itemData.Type){ - case "Movie": - return Qt.resolvedUrl("../components/itemdetails/FilmDetails.qml") - case "Series": - return Qt.resolvedUrl("../components/itemdetails/SeriesDetails.qml") - case "Season": - return Qt.resolvedUrl("../components/itemdetails/SeasonDetails.qml") - case "Episode": - return Qt.resolvedUrl("../components/itemdetails/EpisodeDetails.qml") - case undefined: - return "" - default: - return Qt.resolvedUrl("../components/itemdetails/UnsupportedDetails.qml") - } - } - onLoaded: { - item.itemData = Qt.binding(function() { return pageRoot.itemData; }) - } - } - - - } - } - PageBusyIndicator { running: pageRoot._loading } @@ -160,8 +93,13 @@ Page { //console.log(JSON.stringify(result)) pageRoot.itemData = result pageRoot._loading = false - if (status == PageStatus.Active) - appWindow.itemData = result + if (status == PageStatus.Active) { + if (itemData.Type === "CollectionFolder") { + appWindow.collectionId = itemData.Id + } else { + appWindow.itemData = result + } + } } } } diff --git a/qml/pages/CollectionPage.qml b/qml/pages/itemdetails/CollectionPage.qml similarity index 79% rename from qml/pages/CollectionPage.qml rename to qml/pages/itemdetails/CollectionPage.qml index 2585608..50d82e1 100644 --- a/qml/pages/CollectionPage.qml +++ b/qml/pages/itemdetails/CollectionPage.qml @@ -21,21 +21,18 @@ import Sailfish.Silica 1.0 import nl.netsoj.chris.Jellyfin 1.0 -import ".." -import "../components" -import "../Utils.js" as Utils +import "../.." +import "../../components" -Page { +BaseDetailPage { id: pageRoot - property var itemId - property var itemData - property bool _loading: true UserItemModel { id: collectionModel apiClient: ApiClient parentId: itemData.Id || "" sortBy: ["SortName"] + onParentIdChanged: reload() } SilicaGridView { @@ -99,7 +96,7 @@ Page { pageStack.push(Qt.resolvedUrl("CollectionPage.qml"), {"itemId": model.id}) break; default: - pageStack.push(Qt.resolvedUrl("DetailPage.qml"), {"itemId": model.id}) + pageStack.push(Utils.getPageUrl(model.type), {"itemId": model.id}) } } } @@ -113,48 +110,6 @@ Page { VerticalScrollDecorator {} } - PageBusyIndicator { - running: pageRoot._loading - } - - onItemIdChanged: { - itemData = {} - if (itemId.length && PageStatus.Active) { - pageRoot._loading = true - ApiClient.fetchItem(itemId) - } - } - - onStatusChanged: { - if (status == PageStatus.Deactivating) { - backdrop.clear() - } - if (status == PageStatus.Active) { - if (itemId && !itemData) { - ApiClient.fetchItem(itemId) - appWindow.collectionId = itemId - } - - } - } - - Connections { - target: ApiClient - onItemFetched: { - if (itemId === pageRoot.itemId) { - pageRoot.itemData = result - pageRoot._loading = false - console.log(JSON.stringify(result)) - collectionModel.parentId = result.Id - collectionModel.reload() - if (status == PageStatus.Active) { - appWindow.itemData = null - appWindow.collectionId = itemId - } - } - } - } - Component { id: sortPageComponent Page { diff --git a/qml/pages/itemdetails/EpisodePage.qml b/qml/pages/itemdetails/EpisodePage.qml new file mode 100644 index 0000000..ade3cb0 --- /dev/null +++ b/qml/pages/itemdetails/EpisodePage.qml @@ -0,0 +1,75 @@ +/* +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 "../../" + +BaseDetailPage { + SilicaFlickable { + anchors.fill: parent + contentHeight: content.height + Column { + id: content + width: parent.width + + PageHeader { + title: itemData.Name + description: { + if (typeof itemData.IndexNumberEnd !== "undefined") { + qsTr("Episode %1–%2 | %3").arg(itemData.IndexNumber) + .arg(itemData.IndexNumberEnd) + .arg(itemData.SeasonName) + } else { + qsTr("Episode %1 | %2").arg(itemData.IndexNumber).arg(itemData.SeasonName) + } + } + } + + PlayToolbar { + imageSource: Utils.itemImageUrl(ApiClient.baseUrl, itemData, "Primary", {"maxWidth": parent.width}) + imageAspectRatio: itemData.PrimaryImageAspectRatio + onPlayPressed: pageStack.push(Qt.resolvedUrl("../VideoPage.qml"), + {"itemId": itemId, "itemData": itemData, "audioTrack": trackSelector.audioTrack, + "subtitleTrack": trackSelector.subtitleTrack }) + width: parent.width + } + + VideoTrackSelector { + id: trackSelector + width: parent.width + tracks: itemData.MediaStreams + } + + SectionHeader { + text: qsTr("Overview") + } + + PlainLabel { + id: overviewText + text: itemData.Overview + font.pixelSize: Theme.fontSizeSmall + color: Theme.secondaryHighlightColor + } + } + } +} diff --git a/qml/pages/itemdetails/FilmPage.qml b/qml/pages/itemdetails/FilmPage.qml new file mode 100644 index 0000000..e02160a --- /dev/null +++ b/qml/pages/itemdetails/FilmPage.qml @@ -0,0 +1,70 @@ +/* +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 "../.." + +BaseDetailPage { + SilicaFlickable { + anchors.fill: parent + contentHeight: content.height + + Column { + id: content + width: parent.width + spacing: Theme.paddingMedium + + PageHeader { + title: itemData.Name + description: qsTr("Released: %1 — Run time: %2").arg(itemData.ProductionYear).arg(Utils.ticksToText(itemData.RunTimeTicks)) + } + + PlayToolbar { + width: parent.width + imageSource: Utils.itemImageUrl(ApiClient.baseUrl, itemData, "Primary", {"maxWidth": parent.width}) + imageAspectRatio: 1.66666 //itemData.PrimaryImageAspectRatio + 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 + } + + SectionHeader { + text: qsTr("Overview") + } + + PlainLabel { + id: overviewText + text: itemData.Overview + font.pixelSize: Theme.fontSizeSmall + color: Theme.secondaryHighlightColor + } + } + } +} diff --git a/qml/pages/itemdetails/MusicAlbumPage.qml b/qml/pages/itemdetails/MusicAlbumPage.qml new file mode 100644 index 0000000..9c36e13 --- /dev/null +++ b/qml/pages/itemdetails/MusicAlbumPage.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 + +Item { + +} diff --git a/qml/pages/itemdetails/SeasonPage.qml b/qml/pages/itemdetails/SeasonPage.qml new file mode 100644 index 0000000..e983c2d --- /dev/null +++ b/qml/pages/itemdetails/SeasonPage.qml @@ -0,0 +1,137 @@ +/* +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 "../.." +import "../../components" +import ".." + +BaseDetailPage { + SilicaFlickable { + anchors.fill: parent + contentHeight: content.height + + Column { + id: content + width: parent.width + + PageHeader { + title: itemData.Name + description: itemData.SeriesName + } + + ShowEpisodesModel { + id: episodeModel + apiClient: ApiClient + show: itemData.SeriesId + seasonId: itemData.Id + fields: ["Overview"] + } + + ColumnView { + model: episodeModel + itemHeight: Constants.libraryDelegateHeight + delegate: BackgroundItem { + height: Constants.libraryDelegateHeight + RemoteImage { + id: episodeImage + anchors { + top: parent.top + left: parent.left + bottom: parent.bottom + } + width: Constants.libraryDelegateWidth + height: Constants.libraryDelegateHeight + source: Utils.itemModelImageUrl(ApiClient.baseUrl, model.id, model.imageTags["Primary"], "Primary", {"maxHeight": height}) + fillMode: Image.PreserveAspectCrop + clip: true + + // Makes the progress bar stand out more + Shim { + anchors { + left: parent.left + bottom: parent.bottom + right: parent.right + } + height: parent.height / 3 + shimColor: Theme.overlayBackgroundColor + shimOpacity: Theme.opacityOverlay + //width: model.userData.PlayedPercentage * parent.width / 100 + visible: episodeProgress.width > 0 // It doesn't look nice when it's visible on every image + } + + Rectangle { + id: episodeProgress + anchors { + left: parent.left + bottom: parent.bottom + } + height: Theme.paddingMedium + width: model.userData.PlayedPercentage * parent.width / 100 + color: Theme.highlightColor + } + } + + Label { + id: episodeTitle + anchors { + left: episodeImage.right + leftMargin: Theme.paddingLarge + top: parent.top + right: parent.right + rightMargin: Theme.horizontalPageMargin + } + text: model.name + truncationMode: TruncationMode.Fade + horizontalAlignment: Text.AlignLeft + } + + Label { + id: episodeOverview + anchors { + left: episodeImage.right + leftMargin: Theme.paddingLarge + right: parent.right + rightMargin: Theme.horizontalPageMargin + top: episodeTitle.bottom + bottom: parent.bottom + } + color: highlighted ? Theme.secondaryHighlightColor: Theme.secondaryColor + font.pixelSize: Theme.fontSizeExtraSmall + //: No overview/summary text of an episode available + text: model.overview || qsTr("No overview available") + wrapMode: Text.WordWrap + elide: Text.ElideRight + } + onClicked: pageStack.push(Utils.getPageUrl(model.type), {"itemId": model.id}) + } + } + + } + } + onItemDataChanged: { + console.log(JSON.stringify(itemData)) + episodeModel.show = itemData.SeriesId + episodeModel.seasonId = itemData.Id + episodeModel.reload() + } +} diff --git a/qml/pages/itemdetails/SeriesPage.qml b/qml/pages/itemdetails/SeriesPage.qml new file mode 100644 index 0000000..be5c45d --- /dev/null +++ b/qml/pages/itemdetails/SeriesPage.qml @@ -0,0 +1,93 @@ +/* +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 "../.." + +BaseDetailPage { + SilicaFlickable { + anchors.fill: parent + contentHeight: content.height + + Column { + id: content + width: parent.width + + PageHeader { + id: header + title: itemData.Name + visible: !hasLogo + } + + Item { + visible: hasLogo + width: parent.width + height: Math.max(logoImage.height, header.height) + 2 * Theme.paddingLarge + RemoteImage { + id: logoImage + anchors.centerIn: parent + source: Utils.itemImageUrl(ApiClient.baseUrl, itemData, "Logo") + } + } + + PlainLabel { + id: overviewText + text: itemData.Overview + font.pixelSize: Theme.fontSizeSmall + color: Theme.secondaryHighlightColor + } + + SectionHeader { + //: Seasons of a (TV) show + text: qsTr("Seasons") + } + + ShowSeasonsModel { + id: showSeasonsModel + apiClient: ApiClient + show: itemData.Id + } + + SilicaListView { + model: showSeasonsModel + clip: true + width: parent.width + height: Screen.width / 2 + orientation: ListView.Horizontal + spacing: Theme.paddingLarge + leftMargin: Theme.horizontalPageMargin + rightMargin: Theme.horizontalPageMargin + 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}) + } + } + + } + } + onItemDataChanged: { + showSeasonsModel.show = itemData.Id + showSeasonsModel.reload() + } +} diff --git a/qml/components/itemdetails/UnsupportedDetails.qml b/qml/pages/itemdetails/UnsupportedPage.qml similarity index 71% rename from qml/components/itemdetails/UnsupportedDetails.qml rename to qml/pages/itemdetails/UnsupportedPage.qml index 32881c2..f0d70d7 100644 --- a/qml/components/itemdetails/UnsupportedDetails.qml +++ b/qml/pages/itemdetails/UnsupportedPage.qml @@ -20,10 +20,17 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA import QtQuick 2.6 import Sailfish.Silica 1.0 -ViewPlaceholder { - property var itemData +BaseDetailPage { + SilicaFlickable { + anchors.fill: parent + PageHeader { + title: itemData.Name + } + ViewPlaceholder { - enabled: true - text: qsTr("Item type (%1) unsupported").arg(itemData.Type) - hintText: qsTr("This is still an alpha version :)") + enabled: true + text: qsTr("Item type (%1) unsupported").arg(itemData.Type) + hintText: qsTr("This is still an alpha version :)") + } + } } diff --git a/qml/qmldir b/qml/qmldir index c6de29c..0b84cf2 100644 --- a/qml/qmldir +++ b/qml/qmldir @@ -16,3 +16,4 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA singleton Constants 1.0 Constants.qml +Utils 1.0 Utils.js diff --git a/translations/harbour-sailfin.ts b/translations/harbour-sailfin.ts index 51d1f19..56ee869 100644 --- a/translations/harbour-sailfin.ts +++ b/translations/harbour-sailfin.ts @@ -102,14 +102,7 @@ - DetailPage - - Loading - - - - - EpisodeDetails + EpisodePage Episode %1–%2 Season %3 @@ -120,7 +113,7 @@ - FilmDetails + FilmPage Released: %1 — Run time: %2 @@ -205,7 +198,7 @@ - SeasonDetails + SeasonPage No overview available No overview/summary text of an episode available @@ -213,7 +206,7 @@ - SeriesDetails + SeriesPage Seasons Seasons of a (TV) show @@ -258,13 +251,13 @@ - UnsupportedDetails + UnsupportedPage - This is still an alpha version :) + Item type (%1) unsupported - Item type (%1) unsupported + This is still an alpha version :)