2020-09-27 18:38:33 +00:00
|
|
|
/*
|
|
|
|
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
|
|
|
|
*/
|
2020-09-25 12:46:39 +00:00
|
|
|
import QtQuick 2.6
|
|
|
|
import QtMultimedia 5.6
|
|
|
|
import Sailfish.Silica 1.0
|
|
|
|
|
2021-08-11 21:35:33 +00:00
|
|
|
import nl.netsoj.chris.Jellyfin 1.0 as J
|
2021-02-20 22:20:39 +00:00
|
|
|
|
2020-09-25 15:14:44 +00:00
|
|
|
import "../../Utils.js" as Utils
|
|
|
|
|
2020-09-25 13:21:08 +00:00
|
|
|
/**
|
|
|
|
* The video "hud" or controls. This is the overlay displayed over a video player, which contains controls
|
|
|
|
* and playback information.
|
|
|
|
*/
|
2020-09-25 12:46:39 +00:00
|
|
|
Item {
|
|
|
|
id: videoHud
|
2021-08-11 21:35:33 +00:00
|
|
|
//FIXME: Once QTBUG-10822 is resolved, change to J.PlaybackManager
|
|
|
|
property var manager
|
2020-09-25 12:46:39 +00:00
|
|
|
property string title
|
|
|
|
property bool _manuallyActivated: false
|
2023-01-11 22:11:02 +00:00
|
|
|
/// Don't allow the HUD to hide
|
|
|
|
property bool alwaysVisible: false
|
2020-09-25 12:46:39 +00:00
|
|
|
readonly property bool hidden: opacity == 0.0
|
|
|
|
|
|
|
|
Behavior on opacity { FadeAnimator {} }
|
|
|
|
Rectangle {
|
|
|
|
id: topBar
|
|
|
|
anchors.top: parent.top
|
|
|
|
anchors.left: parent.left
|
|
|
|
anchors.right: parent.right
|
|
|
|
height: pageTitle.height
|
|
|
|
|
|
|
|
gradient: Gradient {
|
|
|
|
GradientStop { position: 1.0; color: Theme.rgba(palette.overlayBackgroundColor, 0.15); }
|
|
|
|
GradientStop { position: 0.0; color: Theme.rgba(palette.overlayBackgroundColor, 0.30); }
|
|
|
|
}
|
|
|
|
PageHeader {
|
|
|
|
id: pageTitle
|
|
|
|
title: videoHud.title
|
|
|
|
anchors.fill: parent
|
|
|
|
titleColor: palette.primaryColor
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Rectangle {
|
|
|
|
anchors.top: topBar.bottom
|
|
|
|
anchors.bottom: bottomBar.top
|
|
|
|
anchors.left: parent.left
|
|
|
|
anchors.right: parent.right
|
|
|
|
color: Theme.rgba(palette.overlayBackgroundColor, 0.15)
|
|
|
|
}
|
|
|
|
|
|
|
|
MouseArea {
|
|
|
|
id: wakeupArea
|
|
|
|
enabled: true
|
|
|
|
anchors.fill: parent
|
2020-10-01 19:45:34 +00:00
|
|
|
onClicked: {
|
|
|
|
hidden ? videoHud.show(true) : videoHud.hide(true)
|
|
|
|
console.log("Trying")
|
|
|
|
}
|
|
|
|
|
2020-09-25 12:46:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
BusyIndicator {
|
|
|
|
id: busyIndicator
|
|
|
|
anchors.centerIn: parent
|
|
|
|
size: BusyIndicatorSize.Medium
|
core: Split PlaybackManager up into smaller parts
The PlaybackManager was a giant class that handled UI bindings, fetching
stream URLS, playback logic.
It now has been split up into:
- ViewModel::PlaybackManager, which handles UI interfacing and allowing
to swap out the Model::Playback implementation on the fly.
- Model::PlaybackManager, which is an interface for what a
PlaybackManager must do, handling queues/playlists, and controlling a
player.
- Model::LocalPlaybackManager, which is an Model::PlaybackManager
implementation for playing back Jellyfin media within the application.
- Model::PlaybackReporter, which reports the current playback state to
the Jellyfin server, for keeping track of played items.
- Model::Player, which handles playing back media from an URL and
the usual play/pause et cetera.
In a future commit, this would allow for introducing a
Model::RemoteJellyfinPlaybackManager, to control other Jellyfin
instances.
2022-01-05 20:24:52 +00:00
|
|
|
running: [J.MediaStatus.Loading, J.MediaStatus.Stalled].indexOf(manager.mediaStatus) >= 0
|
2020-09-25 12:46:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
IconButton {
|
|
|
|
id: playPause
|
|
|
|
enabled: !hidden
|
|
|
|
anchors.centerIn: parent
|
core: Split PlaybackManager up into smaller parts
The PlaybackManager was a giant class that handled UI bindings, fetching
stream URLS, playback logic.
It now has been split up into:
- ViewModel::PlaybackManager, which handles UI interfacing and allowing
to swap out the Model::Playback implementation on the fly.
- Model::PlaybackManager, which is an interface for what a
PlaybackManager must do, handling queues/playlists, and controlling a
player.
- Model::LocalPlaybackManager, which is an Model::PlaybackManager
implementation for playing back Jellyfin media within the application.
- Model::PlaybackReporter, which reports the current playback state to
the Jellyfin server, for keeping track of played items.
- Model::Player, which handles playing back media from an URL and
the usual play/pause et cetera.
In a future commit, this would allow for introducing a
Model::RemoteJellyfinPlaybackManager, to control other Jellyfin
instances.
2022-01-05 20:24:52 +00:00
|
|
|
icon.source: manager.playbackState === J.PlayerState.Paused ? "image://theme/icon-l-play" : "image://theme/icon-l-pause"
|
2020-09-25 12:46:39 +00:00
|
|
|
onClicked: {
|
core: Split PlaybackManager up into smaller parts
The PlaybackManager was a giant class that handled UI bindings, fetching
stream URLS, playback logic.
It now has been split up into:
- ViewModel::PlaybackManager, which handles UI interfacing and allowing
to swap out the Model::Playback implementation on the fly.
- Model::PlaybackManager, which is an interface for what a
PlaybackManager must do, handling queues/playlists, and controlling a
player.
- Model::LocalPlaybackManager, which is an Model::PlaybackManager
implementation for playing back Jellyfin media within the application.
- Model::PlaybackReporter, which reports the current playback state to
the Jellyfin server, for keeping track of played items.
- Model::Player, which handles playing back media from an URL and
the usual play/pause et cetera.
In a future commit, this would allow for introducing a
Model::RemoteJellyfinPlaybackManager, to control other Jellyfin
instances.
2022-01-05 20:24:52 +00:00
|
|
|
console.log(manager.playbackState)
|
|
|
|
if (manager.playbackState === J.PlayerState.Playing) {
|
2021-02-20 22:20:39 +00:00
|
|
|
manager.pause()
|
2020-09-25 12:46:39 +00:00
|
|
|
} else {
|
2021-02-20 22:20:39 +00:00
|
|
|
manager.play()
|
2020-09-25 12:46:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
visible: !busyIndicator.running
|
|
|
|
}
|
|
|
|
|
|
|
|
Rectangle {
|
|
|
|
id: bottomBar
|
|
|
|
anchors.bottom: parent.bottom
|
|
|
|
width: parent.width
|
|
|
|
height: progress.height
|
core: Split PlaybackManager up into smaller parts
The PlaybackManager was a giant class that handled UI bindings, fetching
stream URLS, playback logic.
It now has been split up into:
- ViewModel::PlaybackManager, which handles UI interfacing and allowing
to swap out the Model::Playback implementation on the fly.
- Model::PlaybackManager, which is an interface for what a
PlaybackManager must do, handling queues/playlists, and controlling a
player.
- Model::LocalPlaybackManager, which is an Model::PlaybackManager
implementation for playing back Jellyfin media within the application.
- Model::PlaybackReporter, which reports the current playback state to
the Jellyfin server, for keeping track of played items.
- Model::Player, which handles playing back media from an URL and
the usual play/pause et cetera.
In a future commit, this would allow for introducing a
Model::RemoteJellyfinPlaybackManager, to control other Jellyfin
instances.
2022-01-05 20:24:52 +00:00
|
|
|
visible: [J.MediaStatus.Unavailable, J.MediaStatus.Loading, J.MediaStatus.NoMedia].indexOf(manager.mediaStatus) == -1
|
2020-09-25 12:46:39 +00:00
|
|
|
|
|
|
|
gradient: Gradient {
|
|
|
|
GradientStop { position: 0.0; color: Theme.rgba(palette.overlayBackgroundColor, 0.15); }
|
|
|
|
GradientStop { position: 1.0; color: Theme.rgba(palette.overlayBackgroundColor, 0.30); }
|
|
|
|
}
|
|
|
|
|
|
|
|
Item {
|
|
|
|
id: progress
|
|
|
|
height: progressSlider.height + 2 * Theme.paddingMedium
|
|
|
|
width: parent.width
|
|
|
|
|
|
|
|
Label {
|
|
|
|
id: playedTime
|
|
|
|
anchors.left: parent.left
|
|
|
|
anchors.leftMargin: Theme.horizontalPageMargin
|
|
|
|
anchors.verticalCenter: progressSlider.verticalCenter
|
2021-02-20 22:20:39 +00:00
|
|
|
text: Utils.timeToText(manager.position)
|
2020-09-25 12:46:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Slider {
|
|
|
|
id: progressSlider
|
2021-02-20 22:20:39 +00:00
|
|
|
enabled: manager.seekable
|
|
|
|
value: manager.position
|
|
|
|
maximumValue: manager.duration
|
2020-09-25 12:46:39 +00:00
|
|
|
stepSize: 1000
|
|
|
|
anchors.left: playedTime.right
|
|
|
|
anchors.right: totalTime.left
|
|
|
|
anchors.verticalCenter: parent.verticalCenter
|
2021-02-20 22:20:39 +00:00
|
|
|
onDownChanged: if (!down) { manager.seek(value) }
|
2020-09-25 12:46:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Label {
|
|
|
|
id: totalTime
|
|
|
|
anchors.right: parent.right
|
|
|
|
anchors.rightMargin: Theme.horizontalPageMargin
|
|
|
|
anchors.verticalCenter: progress.verticalCenter
|
2021-02-20 22:20:39 +00:00
|
|
|
text: Utils.timeToText(manager.duration)
|
2020-09-25 12:46:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Connections {
|
2021-02-20 22:20:39 +00:00
|
|
|
target: manager
|
|
|
|
onMediaStatusChanged: {
|
2024-05-08 18:25:59 +00:00
|
|
|
if (manager.mediaStatus == J.MediaStatus.Loaded || manager.mediaStatus == J.MediaStatus.Buffered) {
|
2020-09-25 12:46:39 +00:00
|
|
|
hide(false)
|
2024-05-08 18:25:59 +00:00
|
|
|
} else if (manager.mediaStatus == J.MediaStatus.Buffering || manager.mediaStatus == J.MediaStatus.Stalled) {
|
|
|
|
show(false)
|
2020-09-25 12:46:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function show(manual) {
|
2020-10-01 19:45:34 +00:00
|
|
|
_manuallyActivated = manual
|
2020-09-25 12:46:39 +00:00
|
|
|
if (manual) {
|
|
|
|
inactivityTimer.restart()
|
|
|
|
} else {
|
2020-10-01 19:45:34 +00:00
|
|
|
inactivityTimer.stop()
|
2020-09-25 12:46:39 +00:00
|
|
|
}
|
|
|
|
opacity = 1
|
|
|
|
}
|
|
|
|
|
|
|
|
function hide(manual) {
|
2023-01-11 22:11:02 +00:00
|
|
|
if (alwaysVisible) return
|
2020-09-25 12:46:39 +00:00
|
|
|
// Don't hide if the user decided on their own to show the hud
|
2020-10-01 19:45:34 +00:00
|
|
|
//if (!manual && _manuallyActivated) return;
|
|
|
|
// Don't give in to the user if they want to hide the hud while it was forced upon them
|
|
|
|
/*if (!_manuallyActivated && manual) return;
|
|
|
|
_manuallyActivated = false;*/
|
2020-09-25 12:46:39 +00:00
|
|
|
opacity = 0
|
|
|
|
}
|
|
|
|
|
|
|
|
Timer {
|
|
|
|
id: inactivityTimer
|
|
|
|
interval: 5000
|
|
|
|
onTriggered: {
|
|
|
|
hide(true)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|