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:
parent
895731ae38
commit
f7bca333c8
35 changed files with 1063 additions and 449 deletions
|
@ -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})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue