2020-09-27 18:38:33 +00:00
|
|
|
/*
|
|
|
|
Sailfin: a Jellyfin client written using Qt
|
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
|
|
|
Copyright (C) 2020-2022 Chris Josten
|
2020-09-27 18:38:33 +00:00
|
|
|
|
|
|
|
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-15 14:53:13 +00:00
|
|
|
import QtQuick 2.0
|
|
|
|
import Sailfish.Silica 1.0
|
2020-09-25 12:46:39 +00:00
|
|
|
import QtMultimedia 5.6
|
2020-09-15 14:53:13 +00:00
|
|
|
import nl.netsoj.chris.Jellyfin 1.0
|
2020-09-27 18:51:07 +00:00
|
|
|
|
2021-09-08 19:44:42 +00:00
|
|
|
import Nemo.Configuration 1.0
|
2020-09-15 14:53:13 +00:00
|
|
|
import Nemo.Notifications 1.0
|
2020-09-27 18:51:07 +00:00
|
|
|
import Nemo.KeepAlive 1.2
|
2020-09-15 14:53:13 +00:00
|
|
|
|
|
|
|
import "components"
|
|
|
|
import "pages"
|
2021-09-10 01:08:40 +00:00
|
|
|
import "."
|
2020-09-15 14:53:13 +00:00
|
|
|
|
|
|
|
ApplicationWindow {
|
2020-09-26 22:48:13 +00:00
|
|
|
id: appWindow
|
2020-09-15 14:53:13 +00:00
|
|
|
property bool _hasInitialized: false
|
2020-09-25 13:21:08 +00:00
|
|
|
// The global mediaPlayer instance
|
2021-02-20 22:20:39 +00:00
|
|
|
//readonly property MediaPlayer mediaPlayer: _mediaPlayer
|
2021-02-14 12:29:30 +00:00
|
|
|
readonly property PlaybackManager playbackManager: _playbackManager
|
2021-08-11 21:35:33 +00:00
|
|
|
readonly property ApiClient apiClient: _apiClient
|
2020-09-27 14:54:45 +00:00
|
|
|
|
2021-07-31 13:06:17 +00:00
|
|
|
// Due QTBUG-10822, declarartions such as `property J.Item foo` are not possible.
|
2021-08-22 23:53:20 +00:00
|
|
|
property var itemData: pageStack.currentPage.itemData
|
2020-09-26 22:48:13 +00:00
|
|
|
|
2021-02-14 12:29:30 +00:00
|
|
|
// Bad way to implement settings, but it'll do for now.
|
2021-09-08 19:44:42 +00:00
|
|
|
property alias showDebugInfo: config.showDebugInfo
|
2021-02-14 17:40:46 +00:00
|
|
|
property bool _hidePlaybackBar: false
|
|
|
|
|
2021-09-08 19:44:42 +00:00
|
|
|
|
2021-02-14 17:40:46 +00:00
|
|
|
bottomMargin: playbackBar.visibleSize
|
2021-08-11 21:35:33 +00:00
|
|
|
ApiClient {
|
|
|
|
id: _apiClient
|
|
|
|
objectName: "Test"
|
2022-12-28 20:20:04 +00:00
|
|
|
appName: "Sailfin"
|
2024-01-02 14:14:23 +00:00
|
|
|
deviceType: DeviceType.Phone
|
2021-09-10 01:08:40 +00:00
|
|
|
supportedCommands: [GeneralCommandType.Play, GeneralCommandType.DisplayMessage]
|
2021-08-11 21:35:33 +00:00
|
|
|
}
|
2020-09-15 14:53:13 +00:00
|
|
|
|
2021-08-30 23:29:51 +00:00
|
|
|
PlatformMediaControl {
|
|
|
|
playbackManager: appWindow.playbackManager
|
2021-09-09 00:18:10 +00:00
|
|
|
canQuit: false
|
2021-08-30 23:29:51 +00:00
|
|
|
desktopFile: "harbour-sailfin"
|
|
|
|
playerName: "Sailfin"
|
|
|
|
canRaise: true
|
|
|
|
onRaiseRequested: appWindow.raise()
|
|
|
|
}
|
|
|
|
|
2022-04-26 17:53:12 +00:00
|
|
|
initialPage: ConnectingPage {
|
|
|
|
id: connectingPage
|
|
|
|
onStatusChanged: {
|
|
|
|
if (connectingPage.status == PageStatus.Active && !_hasInitialized) {
|
|
|
|
_hasInitialized = true;
|
|
|
|
apiClient.restoreSavedSession();
|
2020-09-26 22:48:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-08-22 23:53:20 +00:00
|
|
|
//cover: CoverBackground {CoverPlaceholder { icon.source: "icon.png"; text: "Sailfin"}}
|
|
|
|
cover: {
|
2021-08-21 22:29:44 +00:00
|
|
|
// Disabled due to buggy Loader behaviour
|
2023-01-11 22:11:02 +00:00
|
|
|
if ([MediaStatus.NoMedia, MediaStatus.InvalidMedia].indexOf(_playbackManager.mediaStatus) >= 0
|
2021-09-10 01:08:40 +00:00
|
|
|
|| _playbackManager.playbackState === MediaPlayer.StoppedState) {
|
2021-09-09 03:57:41 +00:00
|
|
|
return Qt.resolvedUrl("cover/CollectionPage.qml")
|
2021-08-22 23:53:20 +00:00
|
|
|
} else {
|
2021-09-09 03:57:41 +00:00
|
|
|
return Qt.resolvedUrl("cover/NowPlayingCover.qml")
|
2020-09-25 12:46:39 +00:00
|
|
|
}
|
2021-08-22 23:53:20 +00:00
|
|
|
}
|
2020-09-26 22:48:13 +00:00
|
|
|
|
|
|
|
Notification {
|
|
|
|
id: errorNotification
|
|
|
|
previewSummary: "foo"
|
|
|
|
isTransient: true
|
|
|
|
|
|
|
|
function show(data) {
|
|
|
|
previewSummary = data;
|
|
|
|
publish();
|
|
|
|
}
|
|
|
|
}
|
2020-09-27 18:51:07 +00:00
|
|
|
|
2021-09-09 00:18:10 +00:00
|
|
|
Notification {
|
|
|
|
id: serverNotification
|
|
|
|
//: The application name for the notification
|
|
|
|
appName: qsTr("Sailfin")
|
|
|
|
appIcon: "harbour-sailfin"
|
|
|
|
isTransient: true
|
|
|
|
}
|
|
|
|
|
|
|
|
Connections {
|
|
|
|
target: apiClient.eventbus
|
|
|
|
onDisplayMessage: {
|
|
|
|
serverNotification.summary = header
|
|
|
|
serverNotification.body = message
|
|
|
|
serverNotification.publish()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-14 12:29:30 +00:00
|
|
|
PlaybackManager {
|
|
|
|
id: _playbackManager
|
2021-08-11 21:35:33 +00:00
|
|
|
apiClient: appWindow.apiClient
|
2021-02-14 12:29:30 +00:00
|
|
|
audioIndex: 0
|
|
|
|
}
|
|
|
|
|
2021-08-22 23:53:20 +00:00
|
|
|
Connections {
|
|
|
|
target: pageStack
|
|
|
|
onCurrentPageChanged: {
|
|
|
|
if ("itemData" in pageStack.currentPage) {
|
|
|
|
appWindow.itemData = pageStack.currentPage.itemData
|
|
|
|
} else {
|
|
|
|
appWindow.itemData = null
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-27 18:51:07 +00:00
|
|
|
// Keep the sytem alive while playing media
|
|
|
|
KeepAlive {
|
2021-02-20 22:20:39 +00:00
|
|
|
enabled: playbackManager.playbackState === MediaPlayer.PlayingState
|
2020-09-27 18:51:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
DisplayBlanking {
|
2023-01-11 22:11:02 +00:00
|
|
|
preventBlanking: playbackManager.playbackState === MediaPlayer.PlayingState
|
|
|
|
&& playbackManager.hasVideo
|
|
|
|
&& playbackManager.controllingSessionLocal // Must be controlling a local session
|
2020-09-27 18:51:07 +00:00
|
|
|
}
|
2021-02-13 20:42:57 +00:00
|
|
|
|
2021-02-14 17:40:46 +00:00
|
|
|
PlaybackBar {
|
|
|
|
id: playbackBar
|
|
|
|
manager: _playbackManager
|
2021-02-16 16:25:11 +00:00
|
|
|
// CTMBWSIU: Code That Might Break When Silica Is Updated
|
|
|
|
Component.onCompleted: playbackBar.parent = __silica_applicationwindow_instance._rotatingItem
|
|
|
|
}
|
|
|
|
|
2021-09-08 19:44:42 +00:00
|
|
|
ConfigurationGroup {
|
|
|
|
id: config
|
|
|
|
path: "/nl/netsoj/chris/Sailfin"
|
|
|
|
property bool showDebugInfo: false
|
|
|
|
}
|
|
|
|
|
2021-09-10 01:08:40 +00:00
|
|
|
function navigateToItem(jellyfinId, mediaType, type, isFolder) {
|
2022-04-27 09:03:18 +00:00
|
|
|
if (mediaType === "Audio" && !isFolder) {
|
2021-09-10 01:08:40 +00:00
|
|
|
playbackManager.playItemId(jellyfinId)
|
|
|
|
} else {
|
2022-07-29 23:16:40 +00:00
|
|
|
var url = Utils.getPageUrl(mediaType, type, isFolder)
|
|
|
|
var properties = {"itemId": jellyfinId}
|
|
|
|
if ("__isPlaybackBar" in pageStack.currentPage) {
|
|
|
|
pageStack.replace(url, properties);
|
|
|
|
} else {
|
|
|
|
pageStack.push(url, properties);
|
|
|
|
}
|
2021-09-10 01:08:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-16 16:25:11 +00:00
|
|
|
//FIXME: proper error handling
|
|
|
|
Connections {
|
2021-08-11 21:35:33 +00:00
|
|
|
target: apiClient
|
2021-02-16 16:25:11 +00:00
|
|
|
onNetworkError: errorNotification.show("Network error: " + error)
|
|
|
|
onConnectionFailed: errorNotification.show("Connect error: " + error)
|
|
|
|
//onConnectionSuccess: errorNotification.show("Success: " + loginMessage)
|
|
|
|
onSetupRequired: {
|
2022-04-26 17:53:12 +00:00
|
|
|
console.log("Is in setup: " + isInSetup())
|
|
|
|
if (!isInSetup()) {
|
2021-02-16 16:25:11 +00:00
|
|
|
pageStack.replace(Qt.resolvedUrl("pages/setup/AddServerPage.qml"), {"backNavigation": false});
|
|
|
|
}
|
|
|
|
}
|
2022-04-26 17:53:12 +00:00
|
|
|
onAuthenticatedChanged: {
|
|
|
|
if (authenticated && !isInSetup()) {
|
|
|
|
console.log("Authenticated)")
|
|
|
|
loginAnimation.start()
|
|
|
|
//pageStack.replace(Qt.resolvedUrl("pages/MainPage.qml"))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delaying the creation of the MainPage prevents the application from freezing
|
|
|
|
// if connecting to the server over HTTPS
|
|
|
|
// TODO: figure out why the application actually freezes and remove this workaround
|
|
|
|
SequentialAnimation {
|
|
|
|
id: loginAnimation
|
|
|
|
|
|
|
|
PauseAnimation {
|
|
|
|
duration: 250
|
|
|
|
}
|
|
|
|
|
|
|
|
ScriptAction {
|
|
|
|
script: pageStack.replace(Qt.resolvedUrl("pages/MainPage.qml"), {}, PageStackAction.Immediate)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function isInSetup() {
|
|
|
|
return pageStack.find(function (page) { return typeof page._isSetupPage !== "undefined" }) !== null
|
2021-02-13 20:42:57 +00:00
|
|
|
}
|
2020-09-15 14:53:13 +00:00
|
|
|
}
|