1
0
Fork 0
mirror of https://github.com/HenkKalkwater/harbour-sailfin.git synced 2025-09-05 18:22: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

@ -0,0 +1,16 @@
import QtQuick 2.6
import Sailfish.Silica 1.0
import nl.netsoj.chris.Jellyfin 1.0
import "music"
SilicaListView {
header: SectionHeader { text: qsTr("Play queue") }
delegate: SongDelegate {
artists: model.artists
name: model.name
width: parent.width
indexNumber: ListView.index
}
}

View file

@ -43,6 +43,8 @@ PanelBackground {
property PlaybackManager manager
property bool open
property real visibleSize: height
property bool isFullPage: false
property bool showQueue: false
property bool _pageWasShowingNavigationIndicator
@ -53,7 +55,7 @@ PanelBackground {
id: backgroundItem
width: parent.width
height: parent.height
onClicked: playbackBar.state = (playbackBar.state == "large" ? "open" : "large")
onClicked: playbackBar.state = "large"
RemoteImage {
@ -64,7 +66,10 @@ PanelBackground {
top: parent.top
}
width: height
blurhash: manager.item.imageBlurHashes["Primary"][manager.item.imageTags["Primary"]]
Binding on blurhash {
when: manager.item !== null && "Primary" in manager.item.imageBlurHashes
value: manager.item.imageBlurHashes["Primary"][manager.item.imageTags["Primary"]]
}
source: largeAlbumArt.source
fillMode: Image.PreserveAspectCrop
@ -77,6 +82,20 @@ PanelBackground {
Behavior on opacity { FadeAnimation {} }
}
}
Loader {
id: queueLoader
source: Qt.resolvedUrl("PlayQueue.qml")
anchors.fill: albumArt
active: false
visible: false
Binding {
when: queueLoader.item !== null
target: queueLoader.item
property: "model"
value: manager.queue
//currentIndex: manager.queueIndex
}
}
Column {
id: artistInfo
@ -106,6 +125,7 @@ PanelBackground {
case "Audio":
return manager.item.artists.join(", ")
}
return qsTr("Not audio")
}
width: Math.min(contentWidth, parent.width)
font.pixelSize: Theme.fontSizeSmall
@ -146,11 +166,11 @@ PanelBackground {
rightMargin: Theme.paddingMedium
verticalCenter: parent.verticalCenter
}
icon.source: appWindow.mediaPlayer.playbackState === MediaPlayer.PlayingState
icon.source: manager.playbackState === MediaPlayer.PlayingState
? "image://theme/icon-m-pause" : "image://theme/icon-m-play"
onClicked: appWindow.mediaPlayer.playbackState === MediaPlayer.PlayingState
? appWindow.mediaPlayer.pause()
: appWindow.mediaPlayer.play()
onClicked: manager.playbackState === MediaPlayer.PlayingState
? manager.pause()
: manager.play()
}
IconButton {
id: nextButton
@ -171,8 +191,10 @@ PanelBackground {
verticalCenter: playButton.verticalCenter
}
icon.source: "image://theme/icon-m-menu"
icon.highlighted: showQueue
enabled: false
opacity: 0
onClicked: showQueue = !showQueue
}
ProgressBar {
@ -182,9 +204,9 @@ PanelBackground {
leftMargin: Theme.itemSizeLarge
rightMargin: 0
minimumValue: 0
value: appWindow.mediaPlayer.position
maximumValue: appWindow.mediaPlayer.duration
indeterminate: [MediaPlayer.Loading, MediaPlayer.Buffering].indexOf(appWindow.mediaPlayer.status) >= 0
value: manager.position
maximumValue: manager.duration
indeterminate: [MediaPlayer.Loading, MediaPlayer.Buffering].indexOf(manager.mediaStatus) >= 0
}
Slider {
@ -192,17 +214,17 @@ PanelBackground {
animateValue: false
anchors.verticalCenter: progressBar.top
minimumValue: 0
value: appWindow.mediaPlayer.position
maximumValue: appWindow.mediaPlayer.duration
value: manager.position
maximumValue: manager.duration
width: parent.width
stepSize: 1000
valueText: Utils.timeToText(value)
enabled: false
visible: false
onDownChanged: { if (!down) {
appWindow.mediaPlayer.seek(value);
manager.seek(value);
// For some reason, the binding breaks when dragging the slider.
value = Qt.binding(function() { return appWindow.mediaPlayer.position})
value = Qt.binding(function() { return manager.position})
}
}
}
@ -212,7 +234,7 @@ PanelBackground {
states: [
State {
name: ""
when: appWindow.mediaPlayer.playbackState !== MediaPlayer.StoppedState && state != "page" && !("__hidePlaybackBar" in pageStack.currentPage)
when: manager.playbackState !== MediaPlayer.StoppedState && !isFullPage && !("__hidePlaybackBar" in pageStack.currentPage)
},
State {
name: "large"
@ -328,27 +350,46 @@ PanelBackground {
}
},
State {
name: "hidden"
when: (appWindow.mediaPlayer.playbackState === MediaPlayer.StoppedState || "__hidePlaybackBar" in pageStack.currentPage) && state != "page"
PropertyChanges {
target: playbackBarTranslate
// + small padding since the ProgressBar otherwise would stick out
y: playbackBar.height + Theme.paddingSmall
}
PropertyChanges {
target: playbackBar
visibleSize: 0
}
PropertyChanges {
target: albumArt
source: ""
}
},
State {
name: "page"
extend: "large"
}
State {
name: "hidden"
when: (manager.playbackState === MediaPlayer.StoppedState || "__hidePlaybackBar" in pageStack.currentPage) && !isFullPage
PropertyChanges {
target: playbackBarTranslate
// + small padding since the ProgressBar otherwise would stick out
y: playbackBar.height + Theme.paddingSmall
}
PropertyChanges {
target: playbackBar
visibleSize: 0
}
PropertyChanges {
target: albumArt
source: ""
}
},
State {
name: "page"
when: isFullPage && !showQueue
extend: "large"
PropertyChanges {
target: queueLoader
active: true
}
},
State {
name: "pageQueue"
when: isFullPage && showQueue
extend: "page"
PropertyChanges {
target: queueLoader
visible: true
}
PropertyChanges {
target: largeAlbumArt
opacity: 0
visible: false
}
}
]
Component {
@ -371,7 +412,7 @@ PanelBackground {
}
Loader {
Component.onCompleted: setSource(Qt.resolvedUrl("PlaybackBar.qml"),
{"state": "page", "manager": manager, "y": 0})
{"isFullPage": true, "manager": manager, "y": 0})
anchors.fill: parent
}
}
@ -421,23 +462,19 @@ PanelBackground {
},
Transition {
from: "hidden"
SequentialAnimation {
ParallelAnimation {
NumberAnimation {
targets: [playbackBarTranslate, playbackBar]
properties: "y,visibileSize"
duration: 250
easing.type: Easing.OutQuad
}
NumberAnimation {
targets: [playbackBarTranslate, playbackBar]
properties: "y,visibileSize"
duration: 250
easing.type: Easing.OutQuad
}
NumberAnimation {
target: appWindow
property: "bottomMargin"
duration: 250
to: Theme.itemSizeLarge
easing.type: Easing.OutQuad
}
}
NumberAnimation {
target: appWindow
property: "bottomMargin"
duration: 250
to: Theme.itemSizeLarge
easing.type: Easing.OutQuad
}
},
Transition {

View file

@ -36,10 +36,10 @@ SilicaItem {
property bool resume
property int progress
readonly property bool landscape: videoOutput.contentRect.width > videoOutput.contentRect.height
property MediaPlayer player
readonly property bool hudVisible: !hud.hidden || player.error !== MediaPlayer.NoError
readonly property bool hudVisible: !hud.hidden || manager.error !== MediaPlayer.NoError
property int audioTrack: 0
property int subtitleTrack: 0
property PlaybackManager manager;
// Blackground to prevent the ambience from leaking through
Rectangle {
@ -49,27 +49,27 @@ SilicaItem {
VideoOutput {
id: videoOutput
source: player
source: manager
anchors.fill: parent
}
VideoHud {
id: hud
anchors.fill: parent
player: playerRoot.player
manager: playerRoot.manager
title: videoPlayer.title
Label {
anchors.fill: parent
anchors.margins: Theme.horizontalPageMargin
text: item.jellyfinId + "\n" + appWindow.playbackManager.streamUrl + "\n"
+ (appWindow.playbackManager.playMethod == PlaybackManager.DirectPlay ? "Direct Play" : "Transcoding") + "\n"
+ player.position + "\n"
+ player.status + "\n"
+ player.bufferProgress + "\n"
+ player.metaData.videoCodec + "@" + player.metaData.videoFrameRate + "(" + player.metaData.videoBitRate + ")" + "\n"
+ player.metaData.audioCodec + "(" + player.metaData.audioBitRate + ")" + "\n"
+ player.errorString + "\n"
+ (manager.playMethod === PlaybackManager.DirectPlay ? "Direct Play" : "Transcoding") + "\n"
+ manager.position + "\n"
+ manager.mediaStatus + "\n"
// + player.bufferProgress + "\n"
// + player.metaData.videoCodec + "@" + player.metaData.videoFrameRate + "(" + player.metaData.videoBitRate + ")" + "\n"
// + player.metaData.audioCodec + "(" + player.metaData.audioBitRate + ")" + "\n"
// + player.errorString + "\n"
font.pixelSize: Theme.fontSizeExtraSmall
wrapMode: "WordWrap"
visible: appWindow.showDebugInfo
@ -78,17 +78,17 @@ SilicaItem {
VideoError {
anchors.fill: videoOutput
player: playerRoot.player
player: manager
}
function start() {
appWindow.playbackManager.audioIndex = audioTrack
appWindow.playbackManager.subtitleIndex = subtitleTrack
appWindow.playbackManager.resumePlayback = resume
appWindow.playbackManager.item = item
manager.audioIndex = audioTrack
manager.subtitleIndex = subtitleTrack
manager.resumePlayback = resume
manager.playItem(item.jellyfinId)
}
function stop() {
player.stop()
manager.stop();
}
}

View file

@ -20,9 +20,11 @@ import QtQuick 2.6
import Sailfish.Silica 1.0
import QtMultimedia 5.6
import nl.netsoj.chris.Jellyfin 1.0
Rectangle {
id: videoError
property MediaPlayer player
property PlaybackManager player
color: pal.palette.overlayBackgroundColor
opacity: player.error === MediaPlayer.NoError ? 0.0 : 1.0
Behavior on opacity { FadeAnimator {} }

View file

@ -20,6 +20,8 @@ import QtQuick 2.6
import QtMultimedia 5.6
import Sailfish.Silica 1.0
import nl.netsoj.chris.Jellyfin 1.0
import "../../Utils.js" as Utils
/**
@ -28,7 +30,7 @@ import "../../Utils.js" as Utils
*/
Item {
id: videoHud
property MediaPlayer player
property PlaybackManager manager
property string title
property bool _manuallyActivated: false
readonly property bool hidden: opacity == 0.0
@ -76,19 +78,19 @@ Item {
id: busyIndicator
anchors.centerIn: parent
size: BusyIndicatorSize.Medium
running: [MediaPlayer.Loading, MediaPlayer.Stalled].indexOf(player.status) >= 0
running: [MediaPlayer.Loading, MediaPlayer.Stalled].indexOf(manager.mediaStatus) >= 0
}
IconButton {
id: playPause
enabled: !hidden
anchors.centerIn: parent
icon.source: player.playbackState == MediaPlayer.PausedState ? "image://theme/icon-l-play" : "image://theme/icon-l-pause"
icon.source: manager.playbackState === MediaPlayer.PausedState ? "image://theme/icon-l-play" : "image://theme/icon-l-pause"
onClicked: {
if (player.playbackState == MediaPlayer.PlayingState) {
player.pause()
if (manager.playbackState === MediaPlayer.PlayingState) {
manager.pause()
} else {
player.play()
manager.play()
}
}
visible: !busyIndicator.running
@ -99,7 +101,7 @@ Item {
anchors.bottom: parent.bottom
width: parent.width
height: progress.height
visible: [MediaPlayer.Unavailable, MediaPlayer.Loading, MediaPlayer.NoMedia].indexOf(player.status) == -1
visible: [MediaPlayer.Unavailable, MediaPlayer.Loading, MediaPlayer.NoMedia].indexOf(manager.mediaStatus) == -1
gradient: Gradient {
GradientStop { position: 0.0; color: Theme.rgba(palette.overlayBackgroundColor, 0.15); }
@ -116,19 +118,19 @@ Item {
anchors.left: parent.left
anchors.leftMargin: Theme.horizontalPageMargin
anchors.verticalCenter: progressSlider.verticalCenter
text: Utils.timeToText(player.position)
text: Utils.timeToText(manager.position)
}
Slider {
id: progressSlider
enabled: player.seekable
value: player.position
maximumValue: player.duration
enabled: manager.seekable
value: manager.position
maximumValue: manager.duration
stepSize: 1000
anchors.left: playedTime.right
anchors.right: totalTime.left
anchors.verticalCenter: parent.verticalCenter
onDownChanged: if (!down) { player.seek(value) }
onDownChanged: if (!down) { manager.seek(value) }
}
Label {
@ -136,7 +138,7 @@ Item {
anchors.right: parent.right
anchors.rightMargin: Theme.horizontalPageMargin
anchors.verticalCenter: progress.verticalCenter
text: Utils.timeToText(player.duration)
text: Utils.timeToText(manager.duration)
}
}
}
@ -144,10 +146,10 @@ Item {
Connections {
target: player
onStatusChanged: {
console.log("New mediaPlayer status: " + player.status)
switch(player.status) {
target: manager
onMediaStatusChanged: {
console.log("New mediaPlayer status: " + manager.mediaStatus)
switch(manager.mediaStatus) {
case MediaPlayer.Loaded:
case MediaPlayer.Buffering:
show(false)