1
0
Fork 0
mirror of https://github.com/HenkKalkwater/harbour-sailfin.git synced 2025-09-05 10:12:46 +00:00

Moved playback logic to C++-side (and refractoring)

This commit is contained in:
Chris Josten 2021-02-20 23:20:39 +01:00
parent 895731ae38
commit f7bca333c8
35 changed files with 1063 additions and 449 deletions

View file

@ -78,7 +78,7 @@ Page {
//- Section header for films and TV shows that an user hasn't completed yet.
text: qsTr("Resume watching")
clickable: false
busy: userResumeModel.status == ApiModel.Loading
busy: userResumeModel.status === ApiModel.Loading
Loader {
width: parent.width
sourceComponent: carrouselView
@ -97,7 +97,7 @@ Page {
//- Section header for next episodes in a TV show that an user was watching.
text: qsTr("Next up")
clickable: false
busy: showNextUpModel.status == ApiModel.Loading
busy: showNextUpModel.status === ApiModel.Loading
Loader {
width: parent.width
@ -121,9 +121,9 @@ Page {
model: mediaLibraryModel
MoreSection {
text: model.name
busy: userItemModel.status != ApiModel.Ready
busy: userItemModel.status !== ApiModel.Ready
onHeaderClicked: pageStack.push(Qt.resolvedUrl("itemdetails/CollectionPage.qml"), {"itemId": model.id})
onHeaderClicked: pageStack.push(Qt.resolvedUrl("itemdetails/CollectionPage.qml"), {"itemId": model.jellyfinId})
Loader {
width: parent.width
sourceComponent: carrouselView
@ -133,16 +133,12 @@ Page {
UserItemLatestModel {
id: userItemModel
apiClient: ApiClient
parentId: model.id
parentId: jellyfinId
limit: 16
}
Connections {
target: mediaLibraryModel
onStatusChanged: {
if (status == ApiModel.Ready) {
userItemModel.reload()
}
}
onReady: userItemModel.reload()
}
}
}
@ -154,7 +150,6 @@ Page {
anchors.fill: parent
visible: false
opacity: 0
contentHeight: errorColumn.height
Loader { sourceComponent: commonPullDownMenu; }
@ -220,15 +215,18 @@ Page {
rightMargin: Theme.horizontalPageMargin
spacing: Theme.paddingLarge
delegate: LibraryItemDelegate {
property string id: model.id
property string id: model.jellyfinId
title: model.name
poster: Utils.itemModelImageUrl(ApiClient.baseUrl, model.id, model.imageTags["primary"], "Primary", {"maxHeight": height})
blurhash: model.imageBlurHashes["primary"][model.imageTags["primary"]]
poster: Utils.itemModelImageUrl(ApiClient.baseUrl, model.jellyfinId, model.imageTags["Primary"], "Primary", {"maxHeight": height})
Binding on blurhash {
when: poster !== ""
value: model.imageBlurHashes["Primary"][model.imageTags["Primary"]]
}
landscape: !Utils.usePortraitCover(collectionType)
progress: (typeof model.userData !== "undefined") ? model.userData.playedPercentage / 100 : 0.0
onClicked: {
pageStack.push(Utils.getPageUrl(model.mediaType, model.type, model.isFolder), {"itemId": model.id})
pageStack.push(Utils.getPageUrl(model.mediaType, model.type, model.isFolder), {"itemId": model.jellyfinId, "itemData": model.qtObject})
}
}
}

View file

@ -44,7 +44,7 @@ Page {
VideoPlayer {
id: videoPlayer
anchors.fill: parent
player: appWindow.mediaPlayer
manager: appWindow.playbackManager
title: itemData.name
audioTrack: videoPage.audioTrack
subtitleTrack: videoPage.subtitleTrack
@ -61,7 +61,7 @@ Page {
onStatusChanged: {
switch(status) {
case PageStatus.Inactive:
case PageStatus.Deactivating:
videoPlayer.stop()
break;
case PageStatus.Active:

View file

@ -88,8 +88,7 @@ Page {
id: jItem
apiClient: ApiClient
onStatusChanged: {
console.log("Status changed: " + newStatus, JSON.stringify(jItem))
console.log(jItem.mediaStreams)
//console.log("Status changed: " + newStatus, JSON.stringify(jItem))
if (status == JellyfinItem.Ready) {
updateBackdrop()
}

View file

@ -40,7 +40,7 @@ BaseDetailPage {
anchors.fill: parent
model: collectionModel
cellWidth: Constants.libraryDelegateWidth
cellHeight: Utils.usePortraitCover(itemData.type) ? Constants.libraryDelegatePosterHeight
cellHeight: Utils.usePortraitCover(itemData.collectionType) ? Constants.libraryDelegatePosterHeight
: Constants.libraryDelegateHeight
visible: itemData.status !== JellyfinItem.Error
@ -54,14 +54,14 @@ BaseDetailPage {
text: qsTr("Sort by")
onClicked: pageStack.push(sortPageComponent)
}
busy: collectionModel.status == ApiModel.Loading
busy: collectionModel.status === ApiModel.Loading
}
delegate: GridItem {
RemoteImage {
id: itemImage
anchors.fill: parent
source: Utils.itemModelImageUrl(ApiClient.baseUrl, model.id, model.imageTags.primary, "Primary", {"maxWidth": width})
blurhash: model.imageBlurHashes.primary[model.imageTags.primary]
source: Utils.itemModelImageUrl(ApiClient.baseUrl, model.jellyfinId, model.imageTags.Primary, "Primary", {"maxWidth": width})
blurhash: model.imageBlurHashes.Primary[model.imageTags.Primary]
fallbackColor: Utils.colorFromString(model.name)
fillMode: Image.PreserveAspectCrop
clip: true
@ -90,7 +90,7 @@ BaseDetailPage {
horizontalAlignment: Text.AlignLeft
font.pixelSize: Theme.fontSizeSmall
}
onClicked: pageStack.push(Utils.getPageUrl(model.mediaType, model.type, model.isFolder), {"itemId": model.id})
onClicked: pageStack.push(Utils.getPageUrl(model.mediaType, model.type, model.isFolder), {"itemId": model.jellyfinId})
}
ViewPlaceholder {

View file

@ -29,7 +29,6 @@ import "../.."
BaseDetailPage {
id: albumPageRoot
readonly property int _songIndexWidth: 100
property string _albumArtistText: itemData.albumArtist
width: 800 * Theme.pixelRatio
readonly property bool _twoColumns: albumPageRoot.width / Theme.pixelRatio >= 800
@ -78,7 +77,7 @@ BaseDetailPage {
artists: model.artists
duration: model.runTimeTicks
indexNumber: model.indexNumber
onClicked: window.playbackManager.playItem(model.id)
onClicked: window.playbackManager.playItem(model.jellyfinId)
}
VerticalScrollDecorator {}
@ -88,11 +87,6 @@ BaseDetailPage {
Connections {
target: itemData
onAlbumArtistsChanged: {
console.log(itemData.albumArtists)
_albumArtistText = ""
for (var i = 0; i < itemData.albumArtists.length; i++) {
_albumArtistText += itemData.albumArtists[i]["name"]
}
}
}
@ -100,7 +94,7 @@ BaseDetailPage {
item.albumArt = Qt.binding(function(){ return Utils.itemImageUrl(ApiClient.baseUrl, itemData, "Primary", {"maxWidth": parent.width})})
item.name = Qt.binding(function(){ return itemData.name})
item.releaseYear = Qt.binding(function() { return itemData.productionYear})
item.albumArtist = Qt.binding(function() { return _albumArtistText})
item.albumArtist = Qt.binding(function() { return itemData.albumArtist})
item.duration = Qt.binding(function() { return itemData.runTimeTicks})
item.songCount = Qt.binding(function() { return itemData.childCount})
item.listview = Qt.binding(function() { return list})

View file

@ -60,7 +60,8 @@ BaseDetailPage {
}
width: Constants.libraryDelegateWidth
height: Constants.libraryDelegateHeight
source: Utils.itemModelImageUrl(ApiClient.baseUrl, model.id, model.imageTags.primary, "Primary", {"maxHeight": height})
source: Utils.itemModelImageUrl(ApiClient.baseUrl, model.jellyfinId, model.imageTags.Primary, "Primary", {"maxHeight": height})
blurhash: model.imageBlurHashes.Primary[model.imageTags.Primary]
fillMode: Image.PreserveAspectCrop
clip: true
@ -140,7 +141,7 @@ BaseDetailPage {
wrapMode: Text.WordWrap
elide: Text.ElideRight
}
onClicked: pageStack.push(Utils.getPageUrl(model.mediaType, model.type), {"itemId": model.id})
onClicked: pageStack.push(Utils.getPageUrl(model.mediaType, model.type), {"itemId": model.jellyfinId})
}
VerticalScrollDecorator {}

View file

@ -84,10 +84,10 @@ BaseDetailPage {
leftMargin: Theme.horizontalPageMargin
rightMargin: Theme.horizontalPageMargin
delegate: LibraryItemDelegate {
poster: Utils.itemModelImageUrl(ApiClient.baseUrl, model.id, model.imageTags.primary, "Primary", {"maxHeight": height})
blurhash: model.imageBlurHashes["primary"][model.imageTags.primary]
poster: Utils.itemModelImageUrl(ApiClient.baseUrl, model.jellyfinId, model.imageTags.Primary, "Primary", {"maxHeight": height})
blurhash: model.imageBlurHashes["Primary"][model.imageTags.Primary]
title: model.name
onClicked: pageStack.push(Utils.getPageUrl(model.mediaType, model.type), {"itemId": model.id})
onClicked: pageStack.push(Utils.getPageUrl(model.mediaType, model.type), {"itemId": model.jellyfinId})
}
}

View file

@ -33,6 +33,7 @@ BaseDetailPage {
property alias subtitle: pageHeader.description
default property alias _data: content.data
property real _playbackProsition: itemData.userData.playbackPositionTicks
readonly property bool _userdataReady: itemData.status == JellyfinItem.Ready && itemData.userData != null
SilicaFlickable {
anchors.fill: parent
contentHeight: content.height + Theme.paddingLarge
@ -57,8 +58,14 @@ BaseDetailPage {
imageSource: Utils.itemImageUrl(ApiClient.baseUrl, itemData, "Primary", {"maxWidth": parent.width})
imageAspectRatio: Constants.horizontalVideoAspectRatio
imageBlurhash: itemData.imageBlurHashes["Primary"][itemData.imageTags["Primary"]]
favourited: itemData.userData.isFavorite
playProgress: itemData.userData.playedPercentage / 100
Binding on favourited {
when: _userdataReady
value: itemData.userData.isFavorite
}
Binding on playProgress {
when: _userdataReady
value: itemData.userData.playedPercentage / 100
}
onPlayPressed: pageStack.push(Qt.resolvedUrl("../VideoPage.qml"),
{"itemData": itemData,
"audioTrack": trackSelector.audioTrack,

View file

@ -31,6 +31,7 @@ Dialog {
id: loginDialog
property string loginMessage
property Page firstPage
property User selectedUser: null
property string error
@ -92,16 +93,25 @@ Dialog {
width: parent.width
Flow {
id: userList
width: parent.width
Repeater {
id: userRepeater
model: userModel
delegate: UserGridDelegate {
name: model.name
image: model.primaryImageTag ? "%1/Users/%2/Images/Primary?tag=%3".arg(ApiClient.baseUrl).arg(model.id).arg(model.primaryImageTag) : ""
image: model.primaryImageTag ? "%1/Users/%2/Images/Primary?tag=%3".arg(ApiClient.baseUrl).arg(model.jellyfinId).arg(model.primaryImageTag) : ""
highlighted: model.name === username.text
onHighlightedChanged: {
if (highlighted) {
selectedUser = model.qtObject
}
}
onClicked: {
username.text = model.name
password.focus = true
if (!password.activeFocus) {
password.focus = true
}
}
}
}
@ -119,18 +129,23 @@ Dialog {
placeholderText: qsTr("Username")
label: placeholderText
errorHighlight: error
EnterKey.iconSource: "image://theme/icon-m-enter-next"
EnterKey.onClicked: password.focus = true
EnterKey.iconSource: "image://theme/icon-m-enter-" + (password.enabled ? "next" : "accept")
EnterKey.onClicked: password.enabled ? password.focus = true : accept()
onTextChanged: {
// Wil be executed before the onHighlightChanged of the UserDelegate
// This is done to update the UI after an user is selected and this field has
// been changed, to not let the UI be in an invalid state (e.g. no user selected, password field disabled)
selectedUser = null
}
}
TextField {
PasswordField {
id: password
width: parent.width
//: Label placeholder for password field
placeholderText: qsTr("Password")
label: placeholderText
echoMode: TextInput.Password
errorHighlight: error
EnterKey.iconSource: "image://theme/icon-m-enter-accept"
@ -169,4 +184,41 @@ Dialog {
}
}
canAccept: username.text
states: [
State {
name: "noUsers"
when: userRepeater.count == 0
PropertyChanges {
target: userList
visible: false
}
},
State {
name: "users"
when: userRepeater.count != 0 && selectedUser === null
PropertyChanges {
target: userList
visible: true
}
},
State {
name: "selectedUserPassword"
when: selectedUser !== null && selectedUser.hasPassword
extend: "users"
PropertyChanges {
target: password
enabled: true
}
},
State {
name: "selectedUserNoPassword"
when: selectedUser !== null && !selectedUser.hasPassword
extend: "users"
PropertyChanges {
target: password
enabled: false
}
}
]
}