1
0
Fork 0
mirror of https://github.com/HenkKalkwater/harbour-sailfin.git synced 2025-09-04 01:42:44 +00:00

Add music library page

This commit is contained in:
Chris Josten 2022-07-29 14:26:25 +02:00
parent 0c0b91dc4b
commit dc9c3ea1b8
11 changed files with 284 additions and 102 deletions

View file

@ -26,6 +26,7 @@ set(sailfin_QML_SOURCES
qml/components/videoplayer/VideoError.qml
qml/components/videoplayer/VideoHud.qml
qml/components/IconListItem.qml
qml/components/ItemChildrenShowcase.qml
qml/components/JItem.qml
qml/components/LibraryItemDelegate.qml
qml/components/MoreSection.qml
@ -53,7 +54,8 @@ set(sailfin_QML_SOURCES
qml/pages/itemdetails/EpisodePage.qml
qml/pages/itemdetails/FilmPage.qml
qml/pages/itemdetails/MusicAlbumPage.qml
qml/pages/itemdetails/MusicArtistPage.qml
qml/pages/itemdetails/MusicArtistPage.qml
qml/pages/itemdetails/MusicLibraryPage.qml
qml/pages/itemdetails/PhotoPage.qml
qml/pages/itemdetails/SeasonPage.qml
qml/pages/itemdetails/SeriesPage.qml

View file

@ -112,6 +112,13 @@ function getPageUrl(mediaType, itemType, isFolder) {
return Qt.resolvedUrl("pages/itemdetails/MusicAlbumPage.qml")
case "photo":
return Qt.resolvedUrl("pages/itemdetails/PhotoPage.qml")
case "collectionfolder":
// TODO: support for other collection folders
switch(mediaType.toLowerCase()) {
case "music":
return Qt.resolvedUrl("pages/itemdetails/MusicLibraryPage.qml")
}
// FALLTRHOUGH
default:
switch (mediaType ? mediaType.toLowerCase() : isFolder ? "folder" : "") {
case "folder":

View file

@ -0,0 +1,60 @@
import QtQuick 2.6
import Sailfish.Silica 1.0
import nl.netsoj.chris.Jellyfin 1.0 as J
import "../"
MoreSection {
id: header
busy: itemModel.loader.status === J.ModelStatus.Loading || extraBusy
property bool extraBusy: false
property alias loader: itemModel.loader
property string collectionType
property bool collapseWhenEmpty: true
J.ItemModel {
id: itemModel
}
SilicaListView {
readonly property bool isPortrait: Utils.usePortraitCover(collectionType)
id: list
clip: true
height: {
if (count > 0 || !collapseWhenEmpty) {
if (isPortrait) {
Constants.libraryDelegatePosterHeight
} else {
Constants.libraryDelegateHeight
}
} else {
0
}
}
Behavior on height {
NumberAnimation { easing.type: Easing.OutQuad; duration: 300 }
}
model: itemModel
width: parent.width
orientation: ListView.Horizontal
leftMargin: Theme.horizontalPageMargin
rightMargin: Theme.horizontalPageMargin
spacing: Theme.paddingLarge
delegate: LibraryItemDelegate {
property string id: model.jellyfinId
title: model.name
poster: Utils.itemModelImageUrl(appWindow.apiClient.baseUrl, model.jellyfinId, model.imageTags["Primary"], "Primary", {"height": height})
Binding on blurhash {
when: poster != ""
value: model.imageBlurHashes["Primary"][model.imageTags["Primary"]]
}
landscape: !list.isPortrait
progress: (typeof model.userDataPlayedProgress !== 0.0) ? model.userDataPlayedPercentage / 100 : 0.0
onClicked: {
appWindow.navigateToItem(model.jellyfinId, model.mediaType, model.type, model.isFolder);
}
}
}
}

View file

@ -78,73 +78,40 @@ Page {
}
}
MoreSection {
ItemChildrenShowcase {
//- Section header for films and TV shows that an user hasn't completed yet.
text: qsTr("Resume watching")
clickable: false
busy: userResumeLoader.status === J.ModelStatus.Loading
Loader {
width: parent.width
sourceComponent: carrouselView
property alias itemModel: userResumeModel
property string collectionType: "series"
J.ItemModel {
id: userResumeModel
loader: J.ResumeItemsLoader {
id: userResumeLoader
apiClient: appWindow.apiClient
limit: 12
//recursive: true
}
}
loader: J.ResumeItemsLoader {
id: userResumeLoader
apiClient: appWindow.apiClient
limit: 12
//recursive: true
}
}
MoreSection {
ItemChildrenShowcase {
//- Section header for next episodes in a TV show that an user was watching.
text: qsTr("Next up")
clickable: false
busy: showNextUpLoader.status === J.ModelStatus.Loading
Loader {
width: parent.width
sourceComponent: carrouselView
property alias itemModel: showNextUpModel
property string collectionType: "series"
J.ItemModel {
id: showNextUpModel
loader: J.NextUpLoader {
id: showNextUpLoader
apiClient: appWindow.apiClient
enableUserData: true
}
}
loader: J.NextUpLoader {
id: showNextUpLoader
apiClient: appWindow.apiClient
enableUserData: true
}
}
Repeater {
model: mediaLibraryModel
MoreSection {
ItemChildrenShowcase {
text: model.name
busy: userItemModel.status !== J.UsersViewsLoader.Ready
onHeaderClicked: appWindow.navigateToItem(model.jellyfinId, model.mediaType, model.type, model.isFolder);
Loader {
width: parent.width
sourceComponent: carrouselView
property alias itemModel: userItemModel
property string collectionType: model.collectionType || ""
J.ItemModel {
id: userItemModel
loader: J.LatestMediaLoader {
apiClient: appWindow.apiClient
parentId: jellyfinId
}
}
Connections {
target: mediaLibraryLoader
onReady: userItemModel.reload()
}
onHeaderClicked: appWindow.navigateToItem(model.jellyfinId, model.collectionType, model.type, model.isFolder);
collectionType: model.collectionType || ""
loader: J.LatestMediaLoader {
apiClient: appWindow.apiClient
parentId: jellyfinId
}
Connections {
target: mediaLibraryLoader
onReady: loader.reload()
}
}
}
@ -194,50 +161,6 @@ Page {
}
}
Component {
id: carrouselView
SilicaListView {
property bool isPortrait: Utils.usePortraitCover(collectionType)
id: list
clip: true
height: {
if (count > 0) {
if (isPortrait) {
Constants.libraryDelegatePosterHeight
} else {
Constants.libraryDelegateHeight
}
} else {
0
}
}
Behavior on height {
NumberAnimation { easing.type: Easing.OutQuad; duration: 300 }
}
model: itemModel
width: parent.width
orientation: ListView.Horizontal
leftMargin: Theme.horizontalPageMargin
rightMargin: Theme.horizontalPageMargin
spacing: Theme.paddingLarge
delegate: LibraryItemDelegate {
property string id: model.jellyfinId
title: model.name
poster: Utils.itemModelImageUrl(appWindow.apiClient.baseUrl, model.jellyfinId, model.imageTags["Primary"], "Primary", {"height": height})
Binding on blurhash {
when: poster !== ""
value: model.imageBlurHashes["Primary"][model.imageTags["Primary"]]
}
landscape: !isPortrait
progress: (typeof model.userDataPlayedProgress !== 0.0) ? model.userDataPlayedPercentage / 100 : 0.0
onClicked: {
appWindow.navigateToItem(model.jellyfinId, model.mediaType, model.type, model.isFolder);
}
}
}
}
state: "default"
states: [
State {

View file

@ -73,10 +73,9 @@ BaseDetailPage {
}
PullDownMenu {
id: downMenu
visible: visibleChildren.length > 0
visible: pageRoot.allowSort
MenuItem {
id: sortMenuItem
visible: pageRoot.allowSort
//: Menu item for selecting the sort order of a collection
text: qsTr("Sort by")
onClicked: pageStack.push(sortPageComponent)

View file

@ -0,0 +1,138 @@
import QtQuick 2.6
import Sailfish.Silica 1.0
import nl.netsoj.chris.Jellyfin 1.0 as J
import "../../components"
import "../.."
BaseDetailPage {
id: musicLibraryPage
property bool _firstTimeLoaded: false
onStatusChanged: {
if (status == PageStatus.Active) {
_firstTimeLoaded = true
}
}
SilicaFlickable {
anchors.fill: parent
contentHeight: content.height
Component {
id: albumArtistLoaderComponent
J.AlbumArtistLoader {
apiClient: appWindow.apiClient
parentId: itemData.jellyfinId
autoReload: false
}
}
Component {
id: albumLoaderComponent
J.UserItemsLoader {
apiClient: appWindow.apiClient
parentId: itemData.jellyfinId
includeItemTypes: "MusicAlbum"
recursive: true
sortBy: "SortName"
autoReload: false
}
}
Component {
id: playlistLoaderComponent
J.UserItemsLoader {
apiClient: appWindow.apiClient
parentId: itemData.jellyfinId
includeItemTypes: "Playlist"
recursive: true
sortBy: "SortName"
autoReload: false
}
}
Column {
id: content
width: parent.width
PageHeader {
title: itemData.name
}
ItemChildrenShowcase {
//: Header on music library: Recently added music albums
text: qsTr("Recently added")
//collapseWhenEmpty: false
extraBusy: !_firstTimeLoaded
clickable: false
loader: J.LatestMediaLoader {
apiClient: appWindow.apiClient
parentId: itemData.jellyfinId
autoReload: _firstTimeLoaded && itemData.jellyfinId.length > 0
includeItemTypes: "Audio"
limit: 12
}
}
ItemChildrenShowcase {
text: qsTr("Albums")
//collapseWhenEmpty: false
extraBusy: !_firstTimeLoaded
loader: J.UserItemsLoader {
apiClient: appWindow.apiClient
parentId: itemData.jellyfinId
includeItemTypes: "MusicAlbum"
autoReload: _firstTimeLoaded && itemData.jellyfinId.length > 0
sortBy: "Random"
recursive: true
limit: 12
}
onHeaderClicked: pageStack.push(Qt.resolvedUrl("CollectionPage.qml"), {
"loader": albumLoaderComponent.createObject(musicLibraryPage),
//: Page title for the list of all albums within the music library
"pageTitle": qsTr("Albums")
})
}
ItemChildrenShowcase {
text: qsTr("Playlists")
//collapseWhenEmpty: false
extraBusy: !_firstTimeLoaded
loader: J.UserItemsLoader {
apiClient: appWindow.apiClient
parentId: itemData.jellyfinId
includeItemTypes: "Playlist"
autoReload: _firstTimeLoaded && itemData.jellyfinId.length > 0
sortBy: "Random"
recursive: true
limit: 12
}
onHeaderClicked: pageStack.push(Qt.resolvedUrl("CollectionPage.qml"), {
"loader": playlistLoaderComponent.createObject(musicLibraryPage),
//: Page title for the list of all playlists within the music library
"pageTitle": qsTr("Playlists")
})
}
ItemChildrenShowcase {
//: Header for music artists
text: qsTr("Artists")
//collapseWhenEmpty: false
extraBusy: !_firstTimeLoaded
loader: J.AlbumArtistLoader {
apiClient: appWindow.apiClient
parentId: itemData.jellyfinId
autoReload: _firstTimeLoaded && itemData.jellyfinId.length > 0
limit: 12
}
onHeaderClicked: pageStack.push(Qt.resolvedUrl("CollectionPage.qml"), {
"loader": albumArtistLoaderComponent.createObject(musicLibraryPage),
"allowSort": false,
//: Page title for the list of all artists within the music library
"pageTitle": qsTr("Artists")
})
}
}
}
}