mirror of
https://github.com/HenkKalkwater/harbour-sailfin.git
synced 2025-09-04 01:42:44 +00:00
Rewire more of Sailfish frontend into new backend
This should encompass most simple things, besides some larger, trickier things, like the video streams and the now-broken userdata
This commit is contained in:
parent
df1e134821
commit
7b6c272aa9
47 changed files with 620 additions and 291 deletions
|
@ -18,7 +18,6 @@ set(harbour-sailfin_SOURCES
|
|||
src/harbour-sailfin.cpp)
|
||||
|
||||
set(sailfin_QML_SOURCES
|
||||
qml/ApiClient.qml
|
||||
qml/Constants.qml
|
||||
qml/Utils.js
|
||||
qml/components/music/NarrowAlbumCover.qml
|
||||
|
@ -27,8 +26,8 @@ set(sailfin_QML_SOURCES
|
|||
qml/components/videoplayer/VideoError.qml
|
||||
qml/components/videoplayer/VideoHud.qml
|
||||
qml/components/IconListItem.qml
|
||||
qml/components/JItem.qml
|
||||
qml/components/LibraryItemDelegate.qml
|
||||
qml/components/JItem.qml
|
||||
qml/components/LibraryItemDelegate.qml
|
||||
qml/components/MoreSection.qml
|
||||
qml/components/PlainLabel.qml
|
||||
qml/components/PlaybackBar.qml
|
||||
|
@ -60,8 +59,8 @@ set(sailfin_QML_SOURCES
|
|||
qml/pages/itemdetails/VideoPage.qml
|
||||
qml/pages/settings/DebugPage.qml
|
||||
qml/pages/setup/AddServerConnectingPage.qml
|
||||
qml/pages/setup/AddServerPage.qml
|
||||
qml/pages/setup/LoginDialog.qml
|
||||
qml/pages/setup/AddServerPage.qml
|
||||
qml/pages/setup/LoginDialog.qml
|
||||
qml/qmldir)
|
||||
|
||||
add_executable(harbour-sailfin ${harbour-sailfin_SOURCES} ${sailfin_QML_SOURCES})
|
||||
|
|
|
@ -3,7 +3,7 @@ Type=Application
|
|||
Version=1.1
|
||||
X-Nemo-Application-Type=silica-qt5
|
||||
Icon=harbour-sailfin
|
||||
Exec=harbour-sailfin
|
||||
Exec=harbour-sailfin --no-attempt-sandbox
|
||||
|
||||
|
||||
Name=Sailfin
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
pragma Singleton
|
||||
import QtQuick 2.6
|
||||
import nl.netsoj.chris.Jellyfin 1.0 as J
|
||||
|
||||
J.ApiClient {
|
||||
supportedCommands: [J.GeneralCommandType.Play, J.GeneralCommandType.DisplayContent, J.GeneralCommandType.DisplayMessage]
|
||||
}
|
|
@ -46,7 +46,7 @@ QtObject {
|
|||
}
|
||||
}
|
||||
|
||||
readonly property real libraryDelegatePosterHeight: libraryDelegateHeight * 1.6667
|
||||
readonly property real libraryDelegatePosterHeight: libraryDelegateHeight * 1.5 // 1.6667
|
||||
|
||||
readonly property real libraryProgressHeight: Theme.paddingMedium
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ import QtQuick 2.6
|
|||
import QtMultimedia 5.6
|
||||
import Sailfish.Silica 1.0
|
||||
|
||||
import nl.netsoj.chris.Jellyfin 1.0
|
||||
import nl.netsoj.chris.Jellyfin 1.0 as J
|
||||
|
||||
import "../"
|
||||
|
||||
|
@ -30,7 +30,7 @@ import "../"
|
|||
* +---+--------------------------------------+
|
||||
* |\ /| +---+ |
|
||||
* | \ / | Media title | | |
|
||||
* | X | | ⏸︎| |
|
||||
* | X | | ⏸︎ | |
|
||||
* | / \ | Artist 1, artist 2 | | |
|
||||
* |/ \| +---+ |
|
||||
* +-----+------------------------------------+
|
||||
|
@ -40,13 +40,15 @@ PanelBackground {
|
|||
height: Theme.itemSizeLarge
|
||||
width: parent.width
|
||||
y: parent.height - height
|
||||
property PlaybackManager manager
|
||||
//FIXME: Once QTBUG-10822 is resolved, change to J.PlaybackManager
|
||||
property var manager
|
||||
property bool open
|
||||
property real visibleSize: height
|
||||
property bool isFullPage: false
|
||||
property bool showQueue: false
|
||||
|
||||
property bool _pageWasShowingNavigationIndicator
|
||||
readonly property bool mediaLoading: [MediaPlayer.Loading, MediaPlayer.Buffering].indexOf(manager.mediaStatus) >= 0
|
||||
|
||||
|
||||
transform: Translate {id: playbackBarTranslate; y: 0}
|
||||
|
@ -72,10 +74,11 @@ PanelBackground {
|
|||
}
|
||||
source: largeAlbumArt.source
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
opacity: 1
|
||||
|
||||
Image {
|
||||
id: largeAlbumArt
|
||||
source: Utils.itemImageUrl(ApiClient.baseUrl, manager.item, "Primary")
|
||||
source: Utils.itemImageUrl(apiClient.baseUrl, manager.item, "Primary")
|
||||
fillMode: Image.PreserveAspectFit
|
||||
anchors.fill: parent
|
||||
opacity: 0
|
||||
|
@ -119,11 +122,11 @@ PanelBackground {
|
|||
Label {
|
||||
id: artists
|
||||
text: {
|
||||
if (manager.item == null) return qsTr("Play some media!")
|
||||
console.log(manager.item.type)
|
||||
switch(manager.item.type) {
|
||||
//return manager.item.mediaType;
|
||||
if (manager.item === null) return qsTr("Play some media!")
|
||||
switch(manager.item.mediaType) {
|
||||
case "Audio":
|
||||
return manager.item.artists.join(", ")
|
||||
return manager.item.artists //.join(", ")
|
||||
}
|
||||
return qsTr("Not audio")
|
||||
}
|
||||
|
@ -157,6 +160,7 @@ PanelBackground {
|
|||
icon.source: "image://theme/icon-m-previous"
|
||||
enabled: false
|
||||
opacity: 0
|
||||
onClicked: manager.previous()
|
||||
}
|
||||
|
||||
IconButton {
|
||||
|
@ -182,6 +186,7 @@ PanelBackground {
|
|||
icon.source: "image://theme/icon-m-next"
|
||||
enabled: false
|
||||
opacity: 0
|
||||
onClicked: manager.next()
|
||||
}
|
||||
IconButton {
|
||||
id: queueButton
|
||||
|
@ -206,7 +211,7 @@ PanelBackground {
|
|||
minimumValue: 0
|
||||
value: manager.position
|
||||
maximumValue: manager.duration
|
||||
indeterminate: [MediaPlayer.Loading, MediaPlayer.Buffering].indexOf(manager.mediaStatus) >= 0
|
||||
indeterminate: mediaLoading
|
||||
}
|
||||
|
||||
Slider {
|
||||
|
@ -352,7 +357,7 @@ PanelBackground {
|
|||
},
|
||||
State {
|
||||
name: "hidden"
|
||||
when: (manager.playbackState === MediaPlayer.StoppedState || "__hidePlaybackBar" in pageStack.currentPage) && !isFullPage
|
||||
when: ((manager.playbackState === MediaPlayer.StoppedState && !mediaLoading) || "__hidePlaybackBar" in pageStack.currentPage) && !isFullPage
|
||||
PropertyChanges {
|
||||
target: playbackBarTranslate
|
||||
// + small padding since the ProgressBar otherwise would stick out
|
||||
|
|
|
@ -20,7 +20,7 @@ import QtQuick 2.6
|
|||
import QtMultimedia 5.6
|
||||
import Sailfish.Silica 1.0
|
||||
|
||||
import nl.netsoj.chris.Jellyfin 1.0
|
||||
import nl.netsoj.chris.Jellyfin 1.0 as J
|
||||
|
||||
import "videoplayer"
|
||||
import "../"
|
||||
|
@ -31,7 +31,8 @@ import "../"
|
|||
|
||||
SilicaItem {
|
||||
id: playerRoot
|
||||
property JellyfinItem item
|
||||
//FIXME: Once QTBUG-10822 is resolved, change to J.Item
|
||||
property var item
|
||||
property string title: item.name
|
||||
property bool resume
|
||||
property int progress
|
||||
|
@ -39,7 +40,8 @@ SilicaItem {
|
|||
readonly property bool hudVisible: !hud.hidden || manager.error !== MediaPlayer.NoError
|
||||
property int audioTrack: 0
|
||||
property int subtitleTrack: 0
|
||||
property PlaybackManager manager;
|
||||
//FIXME: Once QTBUG-10822 is resolved, change to J.PlaybackManager
|
||||
property var manager;
|
||||
|
||||
// Blackground to prevent the ambience from leaking through
|
||||
Rectangle {
|
||||
|
@ -59,21 +61,6 @@ SilicaItem {
|
|||
manager: playerRoot.manager
|
||||
title: videoPlayer.title
|
||||
|
||||
Label {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.horizontalPageMargin
|
||||
text: item.jellyfinId + "\n" + appWindow.playbackManager.streamUrl + "\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
|
||||
}
|
||||
}
|
||||
|
||||
VideoError {
|
||||
|
@ -81,11 +68,35 @@ SilicaItem {
|
|||
player: manager
|
||||
}
|
||||
|
||||
Label {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.horizontalPageMargin
|
||||
text: item.jellyfinId + "\n" + appWindow.playbackManager.streamUrl + "\n"
|
||||
+ (manager.playMethod === J.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
|
||||
|
||||
MouseArea {
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
enabled: parent.visible
|
||||
onClicked: Clipboard.text = appWindow.playbackManager.streamUrl
|
||||
}
|
||||
}
|
||||
|
||||
function start() {
|
||||
manager.audioIndex = audioTrack
|
||||
manager.subtitleIndex = subtitleTrack
|
||||
manager.resumePlayback = resume
|
||||
manager.playItem(item.jellyfinId)
|
||||
manager.playItem(item)
|
||||
}
|
||||
|
||||
function stop() {
|
||||
|
|
|
@ -20,11 +20,12 @@ import QtQuick 2.6
|
|||
import Sailfish.Silica 1.0
|
||||
import QtMultimedia 5.6
|
||||
|
||||
import nl.netsoj.chris.Jellyfin 1.0
|
||||
import nl.netsoj.chris.Jellyfin 1.0 as J
|
||||
|
||||
Rectangle {
|
||||
id: videoError
|
||||
property PlaybackManager player
|
||||
//FIXME: Once QTBUG-10822 is resolved, change to J.PlaybackManager
|
||||
property var player
|
||||
color: pal.palette.overlayBackgroundColor
|
||||
opacity: player.error === MediaPlayer.NoError ? 0.0 : 1.0
|
||||
Behavior on opacity { FadeAnimator {} }
|
||||
|
|
|
@ -20,7 +20,7 @@ import QtQuick 2.6
|
|||
import QtMultimedia 5.6
|
||||
import Sailfish.Silica 1.0
|
||||
|
||||
import nl.netsoj.chris.Jellyfin 1.0
|
||||
import nl.netsoj.chris.Jellyfin 1.0 as J
|
||||
|
||||
import "../../Utils.js" as Utils
|
||||
|
||||
|
@ -30,7 +30,8 @@ import "../../Utils.js" as Utils
|
|||
*/
|
||||
Item {
|
||||
id: videoHud
|
||||
property PlaybackManager manager
|
||||
//FIXME: Once QTBUG-10822 is resolved, change to J.PlaybackManager
|
||||
property var manager
|
||||
property string title
|
||||
property bool _manuallyActivated: false
|
||||
readonly property bool hidden: opacity == 0.0
|
||||
|
|
|
@ -35,6 +35,7 @@ ApplicationWindow {
|
|||
// The global mediaPlayer instance
|
||||
//readonly property MediaPlayer mediaPlayer: _mediaPlayer
|
||||
readonly property PlaybackManager playbackManager: _playbackManager
|
||||
readonly property ApiClient apiClient: _apiClient
|
||||
|
||||
// Due QTBUG-10822, declarartions such as `property J.Item foo` are not possible.
|
||||
property QtObject itemData
|
||||
|
@ -46,20 +47,23 @@ ApplicationWindow {
|
|||
property bool _hidePlaybackBar: false
|
||||
|
||||
bottomMargin: playbackBar.visibleSize
|
||||
|
||||
|
||||
ApiClient {
|
||||
id: _apiClient
|
||||
objectName: "Test"
|
||||
supportedCommands: [J.GeneralCommandType.Play, J.GeneralCommandType.DisplayContent, J.GeneralCommandType.DisplayMessage]
|
||||
}
|
||||
|
||||
initialPage: Component {
|
||||
MainPage {
|
||||
Connections {
|
||||
target: D.ApiClient
|
||||
target: apiClient
|
||||
// Replace the MainPage if no server was set up.
|
||||
|
||||
}
|
||||
onStatusChanged: {
|
||||
if (status == PageStatus.Active && !_hasInitialized) {
|
||||
_hasInitialized = true;
|
||||
D.ApiClient.restoreSavedSession();
|
||||
apiClient.restoreSavedSession();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -87,14 +91,9 @@ ApplicationWindow {
|
|||
}
|
||||
}
|
||||
|
||||
/*MediaPlayer {
|
||||
id: _mediaPlayer
|
||||
autoPlay: true
|
||||
}*/
|
||||
|
||||
PlaybackManager {
|
||||
id: _playbackManager
|
||||
apiClient: D.ApiClient
|
||||
apiClient: appWindow.apiClient
|
||||
audioIndex: 0
|
||||
autoOpen: true
|
||||
}
|
||||
|
@ -111,14 +110,13 @@ ApplicationWindow {
|
|||
PlaybackBar {
|
||||
id: playbackBar
|
||||
manager: _playbackManager
|
||||
state: "hidden"
|
||||
// CTMBWSIU: Code That Might Break When Silica Is Updated
|
||||
Component.onCompleted: playbackBar.parent = __silica_applicationwindow_instance._rotatingItem
|
||||
}
|
||||
|
||||
//FIXME: proper error handling
|
||||
Connections {
|
||||
target: D.ApiClient
|
||||
target: apiClient
|
||||
onNetworkError: errorNotification.show("Network error: " + error)
|
||||
onConnectionFailed: errorNotification.show("Connect error: " + error)
|
||||
//onConnectionSuccess: errorNotification.show("Success: " + loginMessage)
|
||||
|
|
|
@ -54,7 +54,7 @@ Page {
|
|||
"Copyright © Chris Josten 2020</p>" +
|
||||
"<p>Sailfin is Free Software licensed under the <a href='lgpl'>LGPL-v2.1</a> or later, at your choice. " +
|
||||
"Parts of the code of Sailfin are from other libraries. <a href='3rdparty'>View their licenses here</a>.</p>")
|
||||
.arg(ApiClient.version)
|
||||
.arg(apiClient.version)
|
||||
textFormat: Text.StyledText
|
||||
color: Theme.secondaryHighlightColor
|
||||
linkColor: Theme.primaryColor
|
||||
|
|
|
@ -23,7 +23,6 @@ import nl.netsoj.chris.Jellyfin 1.0 as J
|
|||
|
||||
import "../components"
|
||||
import "../"
|
||||
import "../Utils.js" as Utils
|
||||
|
||||
/**
|
||||
* Main page, which simply shows some content of every library, as well as next items.
|
||||
|
@ -49,7 +48,7 @@ Page {
|
|||
text: qsTr("Reload")
|
||||
onClicked: loadModels(true)
|
||||
}
|
||||
busy: userViewsLoader.status === J.UsersViewsLoader.Loading
|
||||
busy: mediaLibraryLoader.status === J.UsersViewsLoader.Loading
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,7 +72,7 @@ Page {
|
|||
id: mediaLibraryModel
|
||||
loader: J.UsersViewsLoader {
|
||||
id: mediaLibraryLoader
|
||||
apiClient: ApiClient
|
||||
apiClient: appWindow.apiClient
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,7 +80,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 === J.ApiModel.Loading
|
||||
busy: userResumeLoader.status === J.ApiModel.Loading
|
||||
Loader {
|
||||
width: parent.width
|
||||
sourceComponent: carrouselView
|
||||
|
@ -90,10 +89,12 @@ Page {
|
|||
|
||||
J.ItemModel {
|
||||
id: userResumeModel
|
||||
// Resume model
|
||||
/*apiClient: ApiClient
|
||||
limit: 12
|
||||
recursive: true*/
|
||||
loader: J.ResumeItemsLoader {
|
||||
id: userResumeLoader
|
||||
apiClient: appWindow.apiClient
|
||||
limit: 12
|
||||
//recursive: true*/
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -111,7 +112,7 @@ Page {
|
|||
|
||||
J.ItemModel {
|
||||
id: showNextUpModel
|
||||
/*apiClient: ApiClient
|
||||
/*apiClient: appWindow.apiClient
|
||||
limit: 12*/
|
||||
}
|
||||
}
|
||||
|
@ -132,7 +133,7 @@ Page {
|
|||
J.ItemModel {
|
||||
id: userItemModel
|
||||
loader: J.LatestMediaLoader {
|
||||
apiClient: ApiClient
|
||||
apiClient: appWindow.apiClient
|
||||
parentId: jellyfinId
|
||||
}
|
||||
}
|
||||
|
@ -171,7 +172,7 @@ Page {
|
|||
}
|
||||
|
||||
Connections {
|
||||
target: ApiClient
|
||||
target: appWindow.apiClient
|
||||
onAuthenticatedChanged: loadModels(false)
|
||||
}
|
||||
|
||||
|
@ -181,7 +182,7 @@ Page {
|
|||
* even if loaded.
|
||||
*/
|
||||
function loadModels(force) {
|
||||
if (force || (ApiClient.authenticated && !_modelsLoaded)) {
|
||||
if (force || (appWindow.apiClient.authenticated && !_modelsLoaded)) {
|
||||
_modelsLoaded = true;
|
||||
mediaLibraryModel.reload()
|
||||
//userResumeModel.reload()
|
||||
|
@ -192,14 +193,15 @@ Page {
|
|||
Component {
|
||||
id: carrouselView
|
||||
SilicaListView {
|
||||
property bool isPortrait: Utils.usePortraitCover(collectionType)
|
||||
id: list
|
||||
clip: true
|
||||
height: {
|
||||
if (count > 0) {
|
||||
if (["tvshows", "movies"].indexOf(collectionType) == -1) {
|
||||
Constants.libraryDelegateHeight
|
||||
} else {
|
||||
if (isPortrait) {
|
||||
Constants.libraryDelegatePosterHeight
|
||||
} else {
|
||||
Constants.libraryDelegateHeight
|
||||
}
|
||||
} else {
|
||||
0
|
||||
|
@ -217,12 +219,12 @@ Page {
|
|||
delegate: LibraryItemDelegate {
|
||||
property string id: model.jellyfinId
|
||||
title: model.name
|
||||
poster: Utils.itemModelImageUrl(ApiClient.baseUrl, model.jellyfinId, model.imageTags["Primary"], "Primary", {"maxHeight": height})
|
||||
poster: Utils.itemModelImageUrl(appWindow.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)
|
||||
landscape: !isPortrait
|
||||
progress: (typeof model.userData !== "undefined") ? model.userData.playedPercentage / 100 : 0.0
|
||||
|
||||
onClicked: {
|
||||
|
|
|
@ -22,6 +22,7 @@ import Sailfish.Silica 1.0
|
|||
import nl.netsoj.chris.Jellyfin 1.0 as J
|
||||
|
||||
import "../components"
|
||||
import ".."
|
||||
|
||||
Page {
|
||||
id: settingsPage
|
||||
|
@ -69,7 +70,7 @@ Page {
|
|||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
}
|
||||
source: ApiClient.baseUrl + "/Users/" + ApiClient.userId + "/Images/Primary?tag=" + loggedInUser.primaryImageTag
|
||||
source: apiClient.baseUrl + "/Users/" + apiClient.userId + "/Images/Primary?tag=" + loggedInUser.primaryImageTag
|
||||
}
|
||||
|
||||
Label {
|
||||
|
@ -80,7 +81,7 @@ Page {
|
|||
bottom: parent.verticalCenter
|
||||
right: parent.right
|
||||
}
|
||||
text: loggedInUser.status == User.Ready ? loggedInUser.name : ApiClient.userId
|
||||
text: loggedInUser.status == User.Ready ? loggedInUser.name : apiClient.userId
|
||||
color: Theme.highlightColor
|
||||
}
|
||||
|
||||
|
@ -92,7 +93,7 @@ Page {
|
|||
top: parent.verticalCenter
|
||||
right: parent.right
|
||||
}
|
||||
text: ApiClient.baseUrl
|
||||
text: apiClient.baseUrl
|
||||
color: Theme.secondaryHighlightColor
|
||||
}
|
||||
|
||||
|
@ -104,21 +105,23 @@ Page {
|
|||
ButtonLayout {
|
||||
Button {
|
||||
text: qsTr("Log out")
|
||||
onClicked: remorse.execute(qsTr("Logging out"), ApiClient.deleteSession)
|
||||
onClicked: remorse.execute(qsTr("Logging out"), apiClient.deleteSession)
|
||||
}
|
||||
}
|
||||
|
||||
SectionHeader {
|
||||
//: Other settings
|
||||
//: Other settings menu item
|
||||
text: qsTr("Other")
|
||||
}
|
||||
|
||||
IconListItem {
|
||||
//: Debug information settings menu itemy
|
||||
text: qsTr("Debug information")
|
||||
iconSource: "image://theme/icon-s-developer"
|
||||
onClicked: pageStack.push(Qt.resolvedUrl("settings/DebugPage.qml"))
|
||||
}
|
||||
|
||||
//: About Sailfin settings menu itemy
|
||||
IconListItem {
|
||||
text: qsTr("About Sailfin")
|
||||
iconSource: "image://theme/icon-m-about"
|
||||
|
|
|
@ -21,7 +21,7 @@ import Sailfish.Silica 1.0
|
|||
|
||||
import "../components"
|
||||
|
||||
import nl.netsoj.chris.Jellyfin 1.0
|
||||
import nl.netsoj.chris.Jellyfin 1.0 as J
|
||||
|
||||
/**
|
||||
* Page only containing a video player.
|
||||
|
@ -33,7 +33,7 @@ Page {
|
|||
id: videoPage
|
||||
// PlaybackBar will hide itself when it encounters a page with such a property
|
||||
property bool __hidePlaybackBar: true
|
||||
property JellyfinItem itemData
|
||||
property var itemData
|
||||
property int audioTrack
|
||||
property int subtitleTrack
|
||||
property bool resume: true
|
||||
|
|
|
@ -22,7 +22,7 @@ import Sailfish.Silica 1.0
|
|||
import nl.netsoj.chris.Jellyfin 1.0 as J
|
||||
|
||||
import "../../components"
|
||||
import "../.."
|
||||
import "../../"
|
||||
|
||||
/**
|
||||
* This page displays details about a film, show, season, episode, and so on.
|
||||
|
@ -34,8 +34,6 @@ Page {
|
|||
id: pageRoot
|
||||
property string itemId: ""
|
||||
property alias itemData: jItemLoader.data
|
||||
//property string itemId: ""
|
||||
//property var itemData: ({})
|
||||
property bool _loading: jItemLoader.status === J.ItemLoader.Loading
|
||||
readonly property bool hasLogo: (typeof itemData.imageTags !== "undefined") && (typeof itemData.imageTags["Logo"] !== "undefined")
|
||||
property string _chosenBackdropImage: ""
|
||||
|
@ -46,10 +44,10 @@ Page {
|
|||
if (itemData.backdropImageTags.length > 0) {
|
||||
rand = Math.floor(Math.random() * (itemData.backdropImageTags.length - 0.001))
|
||||
console.log("Random: ", rand)
|
||||
_chosenBackdropImage = ApiClient.baseUrl + "/Items/" + itemId + "/Images/Backdrop/" + rand + "?tag=" +itemData.backdropImageTags[rand] + "&maxHeight" + height
|
||||
_chosenBackdropImage = apiClient.baseUrl + "/Items/" + itemId + "/Images/Backdrop/" + rand + "?tag=" +itemData.backdropImageTags[rand] + "&maxHeight" + height
|
||||
} else if (itemData.parentBackdropImageTags.length > 0) {
|
||||
rand = Math.floor(Math.random() * (itemData.parentBackdropImageTags.length - 0.001))
|
||||
_chosenBackdropImage = ApiClient.baseUrl + "/Items/" + itemData.parentBackdropItemId + "/Images/Backdrop/" + rand + "?tag=" + itemData.parentBackdropImageTags[0]
|
||||
_chosenBackdropImage = apiClient.baseUrl + "/Items/" + itemData.parentBackdropItemId + "/Images/Backdrop/" + rand + "?tag=" + itemData.parentBackdropImageTags[0]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,8 +84,9 @@ Page {
|
|||
|
||||
J.ItemLoader {
|
||||
id: jItemLoader
|
||||
apiClient: ApiClient
|
||||
apiClient: appWindow.apiClient
|
||||
itemId: pageRoot.itemId
|
||||
autoReload: false
|
||||
onStatusChanged: {
|
||||
console.log("Status changed: " + newStatus, JSON.stringify(jItemLoader.data))
|
||||
if (status === J.ItemLoader.Ready) {
|
||||
|
@ -96,15 +95,13 @@ Page {
|
|||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "ItemLoader status=%1, \nitemId=%2\nitemData=%3".arg(jItemLoader.status).arg(jItemLoader.itemId).arg(jItemLoader.data)
|
||||
}
|
||||
|
||||
onStatusChanged: {
|
||||
if (status == PageStatus.Deactivating) {
|
||||
if (status === PageStatus.Deactivating) {
|
||||
//appWindow.itemData = ({})
|
||||
}
|
||||
if (status == PageStatus.Active) {
|
||||
if (status === PageStatus.Active) {
|
||||
console.log("Page ready, ItemID: ", itemId, ", UserID: ", apiClient.userId)
|
||||
jItemLoader.autoReload = true
|
||||
//appWindow.itemData = jItemLoader.data
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,10 +29,13 @@ BaseDetailPage {
|
|||
|
||||
J.ItemModel {
|
||||
id: collectionModel
|
||||
//sortBy: ["SortName"]
|
||||
loader: J.UserItemsLoader {
|
||||
apiClient: ApiClient
|
||||
id: collectionLoader
|
||||
apiClient: appWindow.apiClient
|
||||
parentId: itemData.jellyfinId
|
||||
autoReload: itemData.jellyfinId.length > 0
|
||||
onParentIdChanged: if (parentId.length > 0) reload()
|
||||
sortBy: "SortName"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,7 +64,7 @@ BaseDetailPage {
|
|||
RemoteImage {
|
||||
id: itemImage
|
||||
anchors.fill: parent
|
||||
source: Utils.itemModelImageUrl(ApiClient.baseUrl, model.jellyfinId, model.imageTags.Primary, "Primary", {"maxWidth": width})
|
||||
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
|
||||
|
@ -138,20 +141,20 @@ BaseDetailPage {
|
|||
MenuItem {
|
||||
//: Sort order
|
||||
text: qsTr("Ascending")
|
||||
onClicked: apply(model.value, ApiModel.Ascending)
|
||||
onClicked: apply(model.value, "Ascending")
|
||||
}
|
||||
MenuItem {
|
||||
//: Sort order
|
||||
text: qsTr("Descending")
|
||||
onClicked: apply(model.value, ApiModel.Descending)
|
||||
onClicked: apply(model.value, "Descending")
|
||||
}
|
||||
}
|
||||
onClicked: openMenu()
|
||||
|
||||
function apply(field, order) {
|
||||
collectionModel.sortBy = [field];
|
||||
collectionModel.sortOrder = order;
|
||||
collectionModel.reload()
|
||||
collectionLoader.sortBy = field;
|
||||
collectionLoader.sortOrder = order;
|
||||
collectionLoader.reload()
|
||||
pageStack.pop()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,11 +36,12 @@ BaseDetailPage {
|
|||
J.ItemModel {
|
||||
id: collectionModel
|
||||
loader: J.UserItemsLoader {
|
||||
apiClient: ApiClient
|
||||
//sortBy: ["SortName"]
|
||||
apiClient: appWindow.apiClient
|
||||
sortBy: "SortName"
|
||||
//fields: ["ItemCounts","PrimaryImageAspectRatio","BasicSyncInfo","CanDelete","MediaSourceCount"]
|
||||
parentId: itemData.jellyfinId
|
||||
onParentIdChanged: reload()
|
||||
autoReload: itemData.jellyfinId.length > 0
|
||||
onParentIdChanged: if (parentId.length > 0) reload()
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
|
@ -52,9 +53,9 @@ BaseDetailPage {
|
|||
visible: _twoColumns
|
||||
Layout.minimumWidth: 1000 / Theme.pixelRatio
|
||||
Layout.fillHeight: true
|
||||
/*source: visible
|
||||
source: visible
|
||||
? "../../components/music/WideAlbumCover.qml" : ""
|
||||
onLoaded: bindAlbum(item)*/
|
||||
onLoaded: bindAlbum(item)
|
||||
}
|
||||
Item {height: 1; width: Theme.horizontalPageMargin; visible: wideAlbumCover.visible; }
|
||||
SilicaListView {
|
||||
|
@ -64,8 +65,8 @@ BaseDetailPage {
|
|||
model: collectionModel
|
||||
header: Loader {
|
||||
width: parent.width
|
||||
/*source: "../../components/music/NarrowAlbumCover.qml"
|
||||
onLoaded: bindAlbum(item)*/
|
||||
source: "../../components/music/NarrowAlbumCover.qml"
|
||||
onLoaded: bindAlbum(item)
|
||||
}
|
||||
section {
|
||||
property: "parentIndexNumber"
|
||||
|
@ -79,7 +80,7 @@ BaseDetailPage {
|
|||
artists: model.artists
|
||||
duration: model.runTimeTicks
|
||||
indexNumber: model.indexNumber
|
||||
onClicked: window.playbackManager.playItem(model.jellyfinId)
|
||||
onClicked: window.playbackManager.playItemInList(collectionModel, model.index)
|
||||
}
|
||||
|
||||
VerticalScrollDecorator {}
|
||||
|
@ -87,7 +88,7 @@ BaseDetailPage {
|
|||
}
|
||||
|
||||
function bindAlbum(item) {
|
||||
//item.albumArt = Qt.binding(function(){ return Utils.itemImageUrl(ApiClient.baseUrl, itemData, "Primary", {"maxWidth": parent.width})})
|
||||
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 itemData.albumArtist})
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import QtQuick 2.6
|
||||
import Sailfish.Silica 1.0
|
||||
|
||||
import nl.netsoj.chris.Jellyfin 1.0
|
||||
import nl.netsoj.chris.Jellyfin 1.0 as J
|
||||
|
||||
import "../../components"
|
||||
|
||||
|
@ -21,7 +21,7 @@ BaseDetailPage {
|
|||
|
||||
RemoteImage {
|
||||
id: image
|
||||
source: ApiClient.downloadUrl(itemId)
|
||||
source: apiClient.downloadUrl(itemId)
|
||||
fillMode: Image.PreserveAspectFit
|
||||
anchors.fill: parent
|
||||
|
||||
|
|
|
@ -19,19 +19,22 @@ 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
|
||||
import nl.netsoj.chris.Jellyfin 1.0 as J
|
||||
|
||||
import "../.."
|
||||
import "../../components"
|
||||
import ".."
|
||||
|
||||
BaseDetailPage {
|
||||
ShowEpisodesModel {
|
||||
J.ItemModel {
|
||||
id: episodeModel
|
||||
apiClient: ApiClient
|
||||
show: itemData.seriesId
|
||||
seasonId: itemData.jellyfinId
|
||||
fields: ["Overview"]
|
||||
loader: J.ShowEpisodesLoader {
|
||||
apiClient: appWindow.apiClient
|
||||
seriesId: itemData.seriesId
|
||||
seasonId: itemData.jellyfinId
|
||||
fields: [J.ItemFields.Overview]
|
||||
autoReload: itemData.jellyfinId.length > 0
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
|
@ -42,7 +45,7 @@ BaseDetailPage {
|
|||
SilicaListView {
|
||||
anchors.fill: parent
|
||||
contentHeight: content.height
|
||||
visible: itemData.status !== JellyfinItem.Error
|
||||
//visible: itemData.status !== JellyfinItem.Error
|
||||
|
||||
header: PageHeader {
|
||||
title: itemData.name
|
||||
|
@ -60,7 +63,7 @@ BaseDetailPage {
|
|||
}
|
||||
width: Constants.libraryDelegateWidth
|
||||
height: Constants.libraryDelegateHeight
|
||||
source: Utils.itemModelImageUrl(ApiClient.baseUrl, model.jellyfinId, 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
|
||||
|
@ -157,8 +160,8 @@ BaseDetailPage {
|
|||
onStatusChanged: {
|
||||
if (status == PageStatus.Active) {
|
||||
//console.log(JSON.stringify(itemData))
|
||||
episodeModel.show = itemData.seriesId
|
||||
episodeModel.seasonId = itemData.jellyfinId
|
||||
//episodeModel.show = itemData.seriesId
|
||||
//episodeModel.seasonId = itemData.jellyfinId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ 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
|
||||
import nl.netsoj.chris.Jellyfin 1.0 as J
|
||||
|
||||
import "../../components"
|
||||
import "../.."
|
||||
|
@ -28,7 +28,7 @@ BaseDetailPage {
|
|||
SilicaFlickable {
|
||||
anchors.fill: parent
|
||||
contentHeight: content.height
|
||||
visible: itemData.status !== JellyfinItem.Error
|
||||
//visible: itemData.status !== JellyfinItem.Error
|
||||
|
||||
Column {
|
||||
id: content
|
||||
|
@ -47,7 +47,7 @@ BaseDetailPage {
|
|||
RemoteImage {
|
||||
id: logoImage
|
||||
anchors.centerIn: parent
|
||||
source: Utils.itemImageUrl(ApiClient.baseUrl, itemData, "Logo")
|
||||
source: Utils.itemImageUrl(apiClient.baseUrl, itemData, "Logo")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,11 +63,14 @@ BaseDetailPage {
|
|||
text: qsTr("Seasons")
|
||||
}
|
||||
|
||||
ShowSeasonsModel {
|
||||
J.ItemModel {
|
||||
id: showSeasonsModel
|
||||
apiClient: ApiClient
|
||||
show: itemData.jellyfinId
|
||||
onShowChanged: reload()
|
||||
loader: J.ShowSeasonsLoader {
|
||||
id: showSeasonLoader
|
||||
apiClient: appWindow.apiClient
|
||||
seriesId: itemData.jellyfinId
|
||||
autoReload: itemData.jellyfinId.length > 0
|
||||
}
|
||||
}
|
||||
Connections {
|
||||
target: itemData
|
||||
|
@ -84,7 +87,7 @@ BaseDetailPage {
|
|||
leftMargin: Theme.horizontalPageMargin
|
||||
rightMargin: Theme.horizontalPageMargin
|
||||
delegate: LibraryItemDelegate {
|
||||
poster: Utils.itemModelImageUrl(ApiClient.baseUrl, model.jellyfinId, model.imageTags.Primary, "Primary", {"maxHeight": height})
|
||||
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.jellyfinId})
|
||||
|
|
|
@ -31,7 +31,7 @@ BaseDetailPage {
|
|||
|
||||
enabled: true
|
||||
text: qsTr("Item type (%1) unsupported").arg(itemData.type)
|
||||
hintText: qsTr("This is still an alpha version :)")
|
||||
hintText: qsTr("Fallback page for %2 not found either\nThis is still an alpha version :)").arg(itemData.mediaType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ BaseDetailPage {
|
|||
PlayToolbar {
|
||||
id: toolbar
|
||||
width: parent.width
|
||||
imageSource: Utils.itemImageUrl(ApiClient.baseUrl, itemData, "Primary", {"maxWidth": parent.width})
|
||||
imageSource: Utils.itemImageUrl(apiClient.baseUrl, itemData, "Primary", {"maxWidth": parent.width})
|
||||
imageAspectRatio: Constants.horizontalVideoAspectRatio
|
||||
imageBlurhash: itemData.imageBlurHashes["Primary"][itemData.imageTags["Primary"]]
|
||||
Binding on favourited {
|
||||
|
|
|
@ -54,7 +54,7 @@ Page {
|
|||
label: qsTr("Connection state")
|
||||
value: {
|
||||
var stateText
|
||||
switch(ApiClient.websocket.state) {
|
||||
switch(apiClient.websocket.state) {
|
||||
case 0:
|
||||
//- Socket state
|
||||
stateText = qsTr("Unconnected");
|
||||
|
@ -85,7 +85,7 @@ Page {
|
|||
break;
|
||||
}
|
||||
//- Socket state: "state no (state description)"
|
||||
qsTr("%1 (%2)").arg(ApiClient.websocket.state).arg(stateText)
|
||||
qsTr("%1 (%2)").arg(apiClient.websocket.state).arg(stateText)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,7 +105,7 @@ Page {
|
|||
Label {
|
||||
id: deviceProfile
|
||||
color: Theme.secondaryHighlightColor
|
||||
text: JSON.stringify(ApiClient.deviceProfile, null, '\t')
|
||||
text: JSON.stringify(apiClient.deviceProfile, null, '\t')
|
||||
}
|
||||
HorizontalScrollDecorator {}
|
||||
}
|
||||
|
|
|
@ -26,27 +26,26 @@ import "../.."
|
|||
* Page to indicate that the application is connecting to a server.
|
||||
*/
|
||||
Page {
|
||||
property string serverName
|
||||
property string serverAddress
|
||||
property Page firstPage
|
||||
|
||||
allowedOrientations: Orientation.All
|
||||
|
||||
|
||||
BusyLabel {
|
||||
text: qsTr("Connecting to %1").arg(serverName)
|
||||
running: true
|
||||
}
|
||||
|
||||
onStatusChanged: {
|
||||
if (status == PageStatus.Active) {
|
||||
console.log("Connecting page active");
|
||||
ApiClient.setupConnection();
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: ApiClient
|
||||
property string serverName
|
||||
property string serverAddress
|
||||
property Page firstPage
|
||||
|
||||
allowedOrientations: Orientation.All
|
||||
|
||||
|
||||
BusyLabel {
|
||||
text: qsTr("Connecting to %1").arg(serverName)
|
||||
running: true
|
||||
}
|
||||
|
||||
onStatusChanged: {
|
||||
if (status == PageStatus.Active) {
|
||||
apiClient.setupConnection();
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: apiClient
|
||||
onConnectionSuccess: {
|
||||
console.log("Login success: " + loginMessage);
|
||||
pageStack.replace(Qt.resolvedUrl("LoginDialog.qml"), {"loginMessage": loginMessage, "firstPage": firstPage});
|
||||
|
|
|
@ -28,40 +28,40 @@ import "../../"
|
|||
* This dialog allows manual address entry or use one of the addresses discovered via UDP broadcasts.
|
||||
*/
|
||||
Dialog {
|
||||
id: dialogRoot
|
||||
allowedOrientations: Orientation.All
|
||||
// Picks the address of the ComboBox if selected, otherwise the manual address entry
|
||||
readonly property string address: serverSelect.currentItem._address
|
||||
readonly property bool addressCorrect: serverSelect.currentIndex > 0 || manualAddress.acceptableInput
|
||||
readonly property string serverName: serverSelect.currentItem._name
|
||||
id: dialogRoot
|
||||
allowedOrientations: Orientation.All
|
||||
// Picks the address of the ComboBox if selected, otherwise the manual address entry
|
||||
readonly property string address: serverSelect.currentItem._address
|
||||
readonly property bool addressCorrect: serverSelect.currentIndex > 0 || manualAddress.acceptableInput
|
||||
readonly property string serverName: serverSelect.currentItem._name
|
||||
readonly property bool _isSetupPage: true
|
||||
|
||||
|
||||
acceptDestination: AddServerConnectingPage {
|
||||
id: connectingPage
|
||||
serverName: dialogRoot.serverName
|
||||
serverAddress: address
|
||||
firstPage: dialogRoot
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
DialogHeader {
|
||||
acceptText: qsTr("Connect")
|
||||
title: qsTr("Connect to Jellyfin")
|
||||
}
|
||||
|
||||
J.ServerDiscoveryModel {
|
||||
id: serverModel
|
||||
}
|
||||
|
||||
|
||||
ComboBox {
|
||||
id: serverSelect
|
||||
label: qsTr("Server")
|
||||
description: qsTr("Sailfin will try to search for Jellyfin servers on your local network automatically")
|
||||
|
||||
menu: ContextMenu {
|
||||
|
||||
acceptDestination: AddServerConnectingPage {
|
||||
id: connectingPage
|
||||
serverName: dialogRoot.serverName
|
||||
serverAddress: address
|
||||
firstPage: dialogRoot
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
DialogHeader {
|
||||
acceptText: qsTr("Connect")
|
||||
title: qsTr("Connect to Jellyfin")
|
||||
}
|
||||
|
||||
J.ServerDiscoveryModel {
|
||||
id: serverModel
|
||||
}
|
||||
|
||||
|
||||
ComboBox {
|
||||
id: serverSelect
|
||||
label: qsTr("Server")
|
||||
description: qsTr("Sailfin will try to search for Jellyfin servers on your local network automatically")
|
||||
|
||||
menu: ContextMenu {
|
||||
MenuItem {
|
||||
// Special values are cool, aren't they?
|
||||
readonly property string _address: manualAddress.text
|
||||
|
@ -106,8 +106,8 @@ Dialog {
|
|||
|
||||
function tryConnect() {
|
||||
console.log("Hi there!")
|
||||
ApiClient.baseUrl = address;
|
||||
//ApiClient.setupConnection()
|
||||
apiClient.baseUrl = address;
|
||||
//apiClient.setupConnection()
|
||||
//fakeTimer.start()
|
||||
}
|
||||
|
||||
|
|
|
@ -46,14 +46,14 @@ Dialog {
|
|||
}
|
||||
onStatusChanged: {
|
||||
if(status == PageStatus.Active) {
|
||||
ApiClient.authenticate(username.text, password.text, true)
|
||||
apiClient.authenticate(username.text, password.text, true)
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: ApiClient
|
||||
target: apiClient
|
||||
onAuthenticatedChanged: {
|
||||
if (ApiClient.authenticated) {
|
||||
if (apiClient.authenticated) {
|
||||
console.log("authenticated!")
|
||||
pageStack.replaceAbove(null, Qt.resolvedUrl("../MainPage.qml"))
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ Dialog {
|
|||
|
||||
/*PublicUserModel {
|
||||
id: userModel
|
||||
apiClient: ApiClient
|
||||
apiClient: appWindow.apiClient
|
||||
Component.onCompleted: reload();
|
||||
}*/
|
||||
|
||||
|
@ -103,7 +103,7 @@ Dialog {
|
|||
model: 0 //userModel
|
||||
delegate: UserGridDelegate {
|
||||
name: model.name
|
||||
image: model.primaryImageTag ? "%1/Users/%2/Images/Primary?tag=%3".arg(ApiClient.baseUrl).arg(model.jellyfinId).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) {
|
||||
|
|
|
@ -16,5 +16,4 @@
|
|||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
singleton Constants 1.0 Constants.qml
|
||||
singleton ApiClient 1.0 ApiClient.qml
|
||||
Utils 1.0 Utils.js
|
||||
|
|
|
@ -69,7 +69,7 @@ int main(int argc, char *argv[]) {
|
|||
if (canSanbox && !cmdParser.isSet(sandboxOption)) {
|
||||
qDebug() << "Restarting in sandbox mode";
|
||||
QProcess::execute(QString(SANDBOX_PROGRAM),
|
||||
QStringList() << "-p" << "harbour-sailfin.desktop" << "/usr/bin/harbour-sailfin");
|
||||
QStringList() << "-p" << "harbour-sailfin.desktop" << "harbour-sailfin");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue