mirror of
https://github.com/HenkKalkwater/harbour-sailfin.git
synced 2025-09-06 02:32:44 +00:00
Deserialized a list! Restructured project!
I finally got deserializing lists working. Exposing them to QML was not a trivial task either. Note that I didn't do it the clean way. Nested lists are not supported. But it works! Because I got so frustarted at one point trying to implement things the right way, I restructured the project to seperate the Sailfish code from the Qt code and created a new, empty desktop project. The Qt code has been transformed into a happy little library, to which the Sailfish OS application links. Note that QMake doesn't seem to strip the library for some reason.
This commit is contained in:
parent
4e3395c4e5
commit
1e80ceb697
77 changed files with 507 additions and 213 deletions
71
sailfish/qml/components/GlassyBackground.qml
Normal file
71
sailfish/qml/components/GlassyBackground.qml
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
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
|
||||
*/
|
||||
|
||||
/*
|
||||
File taken and adapted from hutspot. Licensed under the MIT license.
|
||||
Copyright (c) 2019 sailfish-spotify contributors
|
||||
View ../licenses/MIT.txt for the full license.
|
||||
*/
|
||||
import QtQuick 2.6
|
||||
import Sailfish.Silica 1.0
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
/**
|
||||
* A silica-like background for displaying backdrops.
|
||||
*/
|
||||
Rectangle {
|
||||
property alias source: backgroundImage.source
|
||||
property alias sourceSize: backgroundImage.sourceSize
|
||||
property real dimmedOpacity: Theme.opacityFaint
|
||||
readonly property alias status: backgroundImage.status
|
||||
color: Theme.colorScheme == Theme.DarkOnLight ? "#fff" : "#000"
|
||||
z: -1
|
||||
opacity: status == Image.Ready ? 1.0 : 0.0
|
||||
Behavior on opacity { FadeAnimator {} }
|
||||
|
||||
Image {
|
||||
id: backgroundImage
|
||||
cache: true
|
||||
smooth: false
|
||||
asynchronous: true
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
anchors.fill: parent
|
||||
visible: false
|
||||
}
|
||||
|
||||
FastBlur {
|
||||
cached: true
|
||||
anchors.fill: backgroundImage
|
||||
source: backgroundImage
|
||||
opacity: dimmedOpacity
|
||||
radius: 100
|
||||
}
|
||||
|
||||
Image {
|
||||
anchors.fill: parent
|
||||
fillMode: Image.Tile
|
||||
source: "image://theme/graphic-shader-texture"
|
||||
opacity: 0.1
|
||||
visible: parent.visible
|
||||
}
|
||||
|
||||
function clear() {
|
||||
//source = ""
|
||||
}
|
||||
}
|
45
sailfish/qml/components/IconListItem.qml
Normal file
45
sailfish/qml/components/IconListItem.qml
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
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
|
||||
*/
|
||||
import QtQuick 2.0
|
||||
import Sailfish.Silica 1.0
|
||||
|
||||
BackgroundItem {
|
||||
property alias text: label.text
|
||||
property alias iconSource: icon.source
|
||||
HighlightImage {
|
||||
id: icon
|
||||
anchors {
|
||||
top: parent.top
|
||||
topMargin: Theme.paddingMedium
|
||||
left: parent.left
|
||||
leftMargin: Theme.horizontalPageMargin
|
||||
bottom: parent.bottom
|
||||
bottomMargin: Theme.paddingMedium
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
id: label
|
||||
anchors {
|
||||
left: icon.right
|
||||
leftMargin: Theme.paddingMedium
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
}
|
90
sailfish/qml/components/LibraryItemDelegate.qml
Normal file
90
sailfish/qml/components/LibraryItemDelegate.qml
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
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
|
||||
*/
|
||||
import QtQuick 2.6
|
||||
import Sailfish.Silica 1.0
|
||||
|
||||
import ".."
|
||||
|
||||
/**
|
||||
* Delegate for displaying an item in the library.
|
||||
*/
|
||||
BackgroundItem {
|
||||
id: root
|
||||
property alias poster: posterImage.source
|
||||
property alias title: titleText.text
|
||||
property bool landscape: false
|
||||
property real progress: 0.0
|
||||
|
||||
width: Constants.libraryDelegateWidth
|
||||
height: landscape ? Constants.libraryDelegateHeight : Constants.libraryDelegatePosterHeight
|
||||
|
||||
RemoteImage {
|
||||
id: posterImage
|
||||
clip: true
|
||||
anchors {
|
||||
left: parent.left
|
||||
top: parent.top
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
fallbackColor: Utils.colorFromString(title)
|
||||
}
|
||||
|
||||
/*Rectangle {
|
||||
anchors.fill: posterImage
|
||||
color: Theme.rgba(Theme.highlightBackgroundColor, Theme.highlightBackgroundOpacity)
|
||||
visible: root.highlighted
|
||||
}*/
|
||||
|
||||
Shim {
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
height: titleText.height * 1.5 + Theme.paddingSmall * 2
|
||||
|
||||
}
|
||||
|
||||
Label {
|
||||
id: titleText
|
||||
anchors {
|
||||
left: parent.left
|
||||
bottom: parent.bottom
|
||||
right: parent.right
|
||||
leftMargin: Theme.paddingMedium
|
||||
rightMargin: Theme.paddingMedium
|
||||
bottomMargin: Theme.paddingSmall
|
||||
}
|
||||
truncationMode: TruncationMode.Fade
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: progress
|
||||
anchors {
|
||||
left: parent.left
|
||||
bottom: parent.bottom
|
||||
}
|
||||
height: Theme.paddingSmall
|
||||
color: Theme.highlightColor
|
||||
width: root.progress * parent.width
|
||||
}
|
||||
}
|
114
sailfish/qml/components/MoreSection.qml
Normal file
114
sailfish/qml/components/MoreSection.qml
Normal file
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
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
|
||||
*/
|
||||
|
||||
/*
|
||||
File taken and adapted from Storeman. Licensed under the MIT license.
|
||||
Copyright (c) 2017 Petr Tsymbarovich
|
||||
View ../licenses/MIT.txt for the full license.
|
||||
*/
|
||||
|
||||
import QtQuick 2.0
|
||||
import Sailfish.Silica 1.0
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property alias text: label.text
|
||||
property alias textAlignment: label.horizontalAlignment
|
||||
property bool busy: false
|
||||
property bool clickable: true
|
||||
property int depth: 0
|
||||
readonly property color _color: {
|
||||
if (!clickable) {
|
||||
Theme.primaryColor
|
||||
} else if (enabled) {
|
||||
if (highlighted) {
|
||||
Theme.highlightColor
|
||||
} else {
|
||||
Theme.primaryColor
|
||||
}
|
||||
} else {
|
||||
Theme.secondaryColor
|
||||
}
|
||||
}
|
||||
default property alias content: container.data
|
||||
|
||||
signal headerClicked()
|
||||
|
||||
implicitHeight: backgroundItem.height + container.height
|
||||
width: parent.width
|
||||
|
||||
BackgroundItem {
|
||||
id: backgroundItem
|
||||
enabled: parent.enabled && parent.clickable
|
||||
width: parent.width
|
||||
height: Theme.itemSizeMedium
|
||||
onClicked: root.headerClicked()
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 0.0; color: Theme.rgba(Theme.highlightBackgroundColor, 0.15) }
|
||||
GradientStop { position: 1.0; color: "transparent" }
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
id: label
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: image.left
|
||||
verticalCenter: parent.verticalCenter
|
||||
leftMargin: Theme.horizontalPageMargin + depth * Theme.paddingLarge
|
||||
rightMargin: Theme.paddingMedium
|
||||
}
|
||||
horizontalAlignment: Text.AlignRight
|
||||
truncationMode: TruncationMode.Fade
|
||||
color: _color
|
||||
}
|
||||
|
||||
Image {
|
||||
id: image
|
||||
anchors {
|
||||
right: parent.right
|
||||
verticalCenter: parent.verticalCenter
|
||||
rightMargin: Theme.horizontalPageMargin
|
||||
}
|
||||
visible: root.enabled && root.clickable && !root.busy
|
||||
source: "image://theme/icon-m-right?" + _color
|
||||
}
|
||||
|
||||
BusyIndicator {
|
||||
id: busyIndicator
|
||||
running: root.busy
|
||||
anchors.centerIn: image
|
||||
size: BusyIndicatorSize.Small
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: container
|
||||
anchors {
|
||||
top: backgroundItem.bottom
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
width: parent.width
|
||||
height: children.length > 0 ? children[0].height : 0
|
||||
}
|
||||
}
|
37
sailfish/qml/components/PlainLabel.qml
Normal file
37
sailfish/qml/components/PlainLabel.qml
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
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
|
||||
*/
|
||||
|
||||
import QtQuick 2.6
|
||||
import Sailfish.Silica 1.0
|
||||
|
||||
/**
|
||||
* A label with the most commonly used settings set
|
||||
*/
|
||||
Label {
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
leftMargin: Theme.horizontalPageMargin
|
||||
rightMargin: Theme.horizontalPageMargin
|
||||
}
|
||||
color: Theme.highlightColor
|
||||
linkColor: Theme.primaryColor
|
||||
onLinkActivated: Qt.openUrlExternally(link)
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
80
sailfish/qml/components/PlayToolbar.qml
Normal file
80
sailfish/qml/components/PlayToolbar.qml
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
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
|
||||
*/
|
||||
|
||||
import QtQuick 2.6
|
||||
import Sailfish.Silica 1.0
|
||||
|
||||
Column {
|
||||
property alias imageSource : playImage.source
|
||||
property real imageAspectRatio: 1.0
|
||||
property real playProgress: 0.0
|
||||
signal playPressed(bool startFromBeginning)
|
||||
spacing: Theme.paddingLarge
|
||||
|
||||
BackgroundItem {
|
||||
width: parent.width
|
||||
height: width / imageAspectRatio
|
||||
HighlightImage {
|
||||
id: playImage
|
||||
anchors.fill: parent
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
clip: true
|
||||
}
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Theme.rgba(Theme.overlayBackgroundColor, Theme.opacityLow)
|
||||
}
|
||||
Icon {
|
||||
id: playButton
|
||||
source: "image://theme/icon-l-play"
|
||||
anchors.centerIn: parent
|
||||
highlighted: parent.highlighted
|
||||
}
|
||||
Rectangle {
|
||||
anchors {
|
||||
left: parent.left
|
||||
bottom: parent.bottom
|
||||
}
|
||||
height: Theme.paddingMedium
|
||||
color: Theme.highlightColor
|
||||
width: parent.width * playProgress
|
||||
}
|
||||
onClicked: playPressed(false)
|
||||
}
|
||||
Row {
|
||||
anchors {
|
||||
//left: parent.left
|
||||
right: parent.right
|
||||
leftMargin: Theme.horizontalPageMargin
|
||||
rightMargin: Theme.horizontalPageMargin
|
||||
}
|
||||
spacing: Theme.paddingMedium
|
||||
IconButton {
|
||||
id: playFromBeginning
|
||||
icon.source: "image://theme/icon-m-backup"
|
||||
visible: playProgress > 0
|
||||
onClicked: playPressed(true)
|
||||
}
|
||||
IconButton {
|
||||
id: favouriteButton
|
||||
icon.source: "image://theme/icon-m-favorite"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
53
sailfish/qml/components/RemoteImage.qml
Normal file
53
sailfish/qml/components/RemoteImage.qml
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
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
|
||||
*/
|
||||
|
||||
import QtQuick 2.6
|
||||
import Sailfish.Silica 1.0
|
||||
|
||||
/**
|
||||
* An image for "remote" images (loaded over e.g. http), with a spinner and a fallback image
|
||||
*/
|
||||
HighlightImage {
|
||||
property string fallbackImage
|
||||
property bool usingFallbackImage
|
||||
property color fallbackColor: Theme.highlightColor
|
||||
asynchronous: true
|
||||
|
||||
Rectangle {
|
||||
id: fallbackBackground
|
||||
anchors.fill: parent
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 0.0; color: fallbackColor; }
|
||||
GradientStop { position: 1.0; color: Theme.highlightDimmerFromColor(fallbackColor, Theme.colorScheme); }
|
||||
}
|
||||
visible: parent.status == Image.Error || parent.status == Image.Null || parent.status == Image.Loading
|
||||
}
|
||||
|
||||
BusyIndicator {
|
||||
anchors.centerIn: parent
|
||||
running: parent.status == Image.Loading
|
||||
}
|
||||
|
||||
HighlightImage {
|
||||
id: fallbackImageItem
|
||||
anchors.centerIn: parent
|
||||
visible: parent.status == Image.Error || parent.status == Image.Null
|
||||
source: fallbackImage ? fallbackImage : "image://theme/icon-m-question"
|
||||
}
|
||||
}
|
32
sailfish/qml/components/Shim.qml
Normal file
32
sailfish/qml/components/Shim.qml
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
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
|
||||
*/
|
||||
|
||||
import QtQuick 2.6
|
||||
import Sailfish.Silica 1.0
|
||||
|
||||
Rectangle {
|
||||
property real shimOpacity: 1.0
|
||||
property color shimColor: Theme.overlayBackgroundColor
|
||||
property bool upsideDown: false
|
||||
gradient: Gradient {
|
||||
GradientStop { position: upsideDown ? 1.0 : 0.0; color: Theme.rgba(shimColor, 0.0); }
|
||||
GradientStop { position: upsideDown ? 0.0 : 1.0; color: Theme.rgba(shimColor, shimOpacity); }
|
||||
}
|
||||
|
||||
}
|
54
sailfish/qml/components/UserGridDelegate.qml
Normal file
54
sailfish/qml/components/UserGridDelegate.qml
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
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
|
||||
*/
|
||||
|
||||
import QtQuick 2.6
|
||||
import Sailfish.Silica 1.0
|
||||
|
||||
GridItem {
|
||||
id: root
|
||||
property string image
|
||||
property alias name: nameLabel.text
|
||||
RemoteImage {
|
||||
id: userImage
|
||||
anchors.fill: parent
|
||||
source: root.image ? root.image : "image://theme/icon-m-contact?" + ((root.highlighted || root.down) ? Theme.highlightColor : Theme.primaryColor)
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
clip: true
|
||||
}
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 0.0; color: "transparent" }
|
||||
GradientStop { position: 1.0; color: Theme.overlayBackgroundColor }
|
||||
}
|
||||
}
|
||||
Label {
|
||||
id: nameLabel
|
||||
anchors {
|
||||
leftMargin: Theme.horizontalPageMargin
|
||||
rightMargin: Theme.horizontalPageMargin
|
||||
right: parent.right
|
||||
left: parent.left
|
||||
bottom: parent.bottom
|
||||
bottomMargin: Theme.paddingSmall
|
||||
}
|
||||
text: qsTr("Other account")
|
||||
color: (root.highlighted || root.down) ? Theme.highlightColor : Theme.secondaryColor
|
||||
}
|
||||
}
|
122
sailfish/qml/components/VideoPlayer.qml
Normal file
122
sailfish/qml/components/VideoPlayer.qml
Normal file
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
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
|
||||
*/
|
||||
import QtQuick 2.6
|
||||
import QtMultimedia 5.6
|
||||
import Sailfish.Silica 1.0
|
||||
|
||||
import nl.netsoj.chris.Jellyfin 1.0
|
||||
|
||||
import "videoplayer"
|
||||
import "../"
|
||||
|
||||
/**
|
||||
* A videoPlayer for Jellyfin videos
|
||||
*/
|
||||
|
||||
SilicaItem {
|
||||
id: playerRoot
|
||||
property string itemId
|
||||
property string title
|
||||
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
|
||||
property alias audioTrack: mediaSource.audioIndex
|
||||
property alias subtitleTrack: mediaSource.subtitleIndex
|
||||
property int startTicks: 0
|
||||
|
||||
// Force a Light on Dark theme since I doubt that there are persons who are willing to watch a Video
|
||||
// on a white background.
|
||||
palette.colorScheme: Theme.LightOnDark
|
||||
|
||||
// Blackground to prevent the ambience from leaking through
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "black"
|
||||
}
|
||||
|
||||
PlaybackManager {
|
||||
id: mediaSource
|
||||
apiClient: ApiClient
|
||||
itemId: playerRoot.itemId
|
||||
autoOpen: true
|
||||
onStreamUrlChanged: {
|
||||
if (mediaSource.streamUrl != "") {
|
||||
player.source = streamUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: player
|
||||
onPlaybackStateChanged: mediaSource.state = player.playbackState
|
||||
onPositionChanged: mediaSource.position = Utils.msToTicks(player.position)
|
||||
}
|
||||
|
||||
|
||||
VideoOutput {
|
||||
id: videoOutput
|
||||
source: player
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
VideoHud {
|
||||
id: hud
|
||||
anchors.fill: parent
|
||||
player: playerRoot.player
|
||||
title: videoPlayer.title
|
||||
|
||||
Label {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.horizontalPageMargin
|
||||
text: itemId + "\n" + mediaSource.streamUrl + "\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"
|
||||
font.pixelSize: Theme.fontSizeExtraSmall
|
||||
wrapMode: "WordWrap"
|
||||
visible: false
|
||||
}
|
||||
}
|
||||
|
||||
VideoError {
|
||||
anchors.fill: videoOutput
|
||||
player: playerRoot.player
|
||||
}
|
||||
|
||||
function stop() {
|
||||
player.stop()
|
||||
}
|
||||
|
||||
Connections {
|
||||
property bool enabled: true
|
||||
id: playerReadyToSeek
|
||||
target: player
|
||||
onPlaybackStateChanged: {
|
||||
if (!enabled) return;
|
||||
if (startTicks > 0 && player.playbackState == MediaPlayer.PlayingState) {
|
||||
console.log("Seeking to " + Utils.ticksToMs(startTicks))
|
||||
player.seek(Utils.ticksToMs(startTicks))
|
||||
playerReadyToSeek.enabled = false // Only seek the first time this property changes
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
95
sailfish/qml/components/VideoTrackSelector.qml
Normal file
95
sailfish/qml/components/VideoTrackSelector.qml
Normal file
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
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
|
||||
*/
|
||||
import QtQuick 2.6
|
||||
import Sailfish.Silica 1.0
|
||||
|
||||
import nl.netsoj.chris.Jellyfin 1.0
|
||||
|
||||
Column {
|
||||
property var tracks
|
||||
readonly property int audioTrack: audioSelector.currentItem ? audioSelector.currentItem._index : 0
|
||||
readonly property int subtitleTrack: subitleSelector.currentItem._index
|
||||
|
||||
ListModel {
|
||||
id: audioModel
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: subtitleModel
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: audioSelector
|
||||
label: qsTr("Audio track")
|
||||
enabled: audioModel.count > 1
|
||||
menu: ContextMenu {
|
||||
Repeater {
|
||||
model: audioModel
|
||||
MenuItem {
|
||||
readonly property int _index: model.index
|
||||
text: model.displayTitle
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: subitleSelector
|
||||
label: qsTr("Subtitle track")
|
||||
enabled: subtitleModel.count > 0
|
||||
menu: ContextMenu {
|
||||
MenuItem {
|
||||
readonly property int _index: -1
|
||||
//: Value in ComboBox to disable subtitles
|
||||
text: qsTr("Off")
|
||||
}
|
||||
Repeater {
|
||||
model: subtitleModel
|
||||
MenuItem {
|
||||
readonly property int _index: model.index
|
||||
text: model.displayTitle
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onTracksChanged: {
|
||||
audioModel.clear()
|
||||
subtitleModel.clear()
|
||||
if (typeof tracks === "undefined") {
|
||||
console.log("tracks undefined")
|
||||
return
|
||||
}
|
||||
console.log(tracks)
|
||||
for(var i = 0; i < tracks.length; i++) {
|
||||
var track = tracks[i];
|
||||
switch(track.type) {
|
||||
case MediaStream.Audio:
|
||||
audioModel.append(track)
|
||||
break;
|
||||
case MediaStream.Subtitle:
|
||||
subtitleModel.append(track)
|
||||
break;
|
||||
default:
|
||||
console.log("Ignored " + track.displayTitle + "(" + track.type + ")")
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
91
sailfish/qml/components/videoplayer/VideoError.qml
Normal file
91
sailfish/qml/components/videoplayer/VideoError.qml
Normal file
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
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
|
||||
*/
|
||||
import QtQuick 2.6
|
||||
import Sailfish.Silica 1.0
|
||||
import QtMultimedia 5.6
|
||||
|
||||
Rectangle {
|
||||
id: videoError
|
||||
property MediaPlayer player
|
||||
color: pal.palette.overlayBackgroundColor
|
||||
opacity: player.error === MediaPlayer.NoError ? 0.0 : 1.0
|
||||
Behavior on opacity { FadeAnimator {} }
|
||||
|
||||
SilicaItem {
|
||||
id: pal
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.centerIn: parent
|
||||
anchors.margins: Theme.horizontalPageMargin
|
||||
|
||||
Label {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
font.pixelSize: Theme.fontSizeExtraLarge
|
||||
color: Theme.errorColor
|
||||
text: {
|
||||
switch(player.error) {
|
||||
case MediaPlayer.NoError:
|
||||
//: Just to be complete if the application shows a video playback error when there's no error.
|
||||
qsTr("No error");
|
||||
break;
|
||||
case MediaPlayer.ResourceError:
|
||||
//: Video playback error: out of resources
|
||||
qsTr("Resource allocation error")
|
||||
break;
|
||||
case MediaPlayer.FormatError:
|
||||
//: Video playback error: unsupported format/codec
|
||||
qsTr("Video format unsupported")
|
||||
break;
|
||||
case MediaPlayer.NetworkError:
|
||||
//: Video playback error: network error
|
||||
qsTr("Network error")
|
||||
break;
|
||||
case MediaPlayer.AccessDenied:
|
||||
//: Video playback error: access denied
|
||||
qsTr("Access denied")
|
||||
break;
|
||||
case MediaPlayer.ServiceMissing:
|
||||
//: Video playback error: the media cannot be played because the media service could not be instantiated.
|
||||
qsTr("Media service missing")
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
wrapMode: Text.WordWrap
|
||||
text: player.errorString
|
||||
color: Theme.errorColor
|
||||
width: videoError.width - Theme.horizontalPageMargin * 2
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
Item { width: 1; height: Theme.paddingLarge; }
|
||||
|
||||
ButtonLayout {
|
||||
Button {
|
||||
//: Button to retry loading a video after a failure
|
||||
text: qsTr("Retry")
|
||||
onClicked: player.play()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
188
sailfish/qml/components/videoplayer/VideoHud.qml
Normal file
188
sailfish/qml/components/videoplayer/VideoHud.qml
Normal file
|
@ -0,0 +1,188 @@
|
|||
/*
|
||||
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
|
||||
*/
|
||||
import QtQuick 2.6
|
||||
import QtMultimedia 5.6
|
||||
import Sailfish.Silica 1.0
|
||||
|
||||
import "../../Utils.js" as Utils
|
||||
|
||||
/**
|
||||
* The video "hud" or controls. This is the overlay displayed over a video player, which contains controls
|
||||
* and playback information.
|
||||
*/
|
||||
Item {
|
||||
id: videoHud
|
||||
property MediaPlayer player
|
||||
property string title
|
||||
property bool _manuallyActivated: false
|
||||
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
|
||||
onClicked: {
|
||||
hidden ? videoHud.show(true) : videoHud.hide(true)
|
||||
console.log("Trying")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
BusyIndicator {
|
||||
id: busyIndicator
|
||||
anchors.centerIn: parent
|
||||
size: BusyIndicatorSize.Medium
|
||||
running: [MediaPlayer.Loading, MediaPlayer.Stalled].indexOf(player.status) >= 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"
|
||||
onClicked: {
|
||||
if (player.playbackState == MediaPlayer.PlayingState) {
|
||||
player.pause()
|
||||
} else {
|
||||
player.play()
|
||||
}
|
||||
}
|
||||
visible: !busyIndicator.running
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: bottomBar
|
||||
anchors.bottom: parent.bottom
|
||||
width: parent.width
|
||||
height: progress.height
|
||||
visible: [MediaPlayer.Unavailable, MediaPlayer.Loading, MediaPlayer.NoMedia].indexOf(player.status) == -1
|
||||
|
||||
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
|
||||
text: Utils.timeToText(player.position)
|
||||
}
|
||||
|
||||
Slider {
|
||||
id: progressSlider
|
||||
enabled: player.seekable
|
||||
value: player.position
|
||||
maximumValue: player.duration
|
||||
stepSize: 1000
|
||||
anchors.left: playedTime.right
|
||||
anchors.right: totalTime.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
onDownChanged: if (!down) { player.seek(value) }
|
||||
}
|
||||
|
||||
Label {
|
||||
id: totalTime
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Theme.horizontalPageMargin
|
||||
anchors.verticalCenter: progress.verticalCenter
|
||||
text: Utils.timeToText(player.duration)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Connections {
|
||||
target: player
|
||||
onStatusChanged: {
|
||||
console.log("New mediaPlayer status: " + player.status)
|
||||
switch(player.status) {
|
||||
case MediaPlayer.Loaded:
|
||||
case MediaPlayer.Buffering:
|
||||
show(false)
|
||||
break;
|
||||
case MediaPlayer.Buffered:
|
||||
hide(false)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function show(manual) {
|
||||
_manuallyActivated = manual
|
||||
if (manual) {
|
||||
inactivityTimer.restart()
|
||||
} else {
|
||||
inactivityTimer.stop()
|
||||
}
|
||||
opacity = 1
|
||||
}
|
||||
|
||||
function hide(manual) {
|
||||
// Don't hide if the user decided on their own to show the hud
|
||||
//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;*/
|
||||
opacity = 0
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: inactivityTimer
|
||||
interval: 5000
|
||||
onTriggered: {
|
||||
hide(true)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue