From 64ad37707c56dde26d78819c65ce6805cb548bb5 Mon Sep 17 00:00:00 2001 From: Henk Kalkwater Date: Sat, 4 Sep 2021 22:23:54 +0200 Subject: [PATCH] Make libJellyfinQt a proper qml plugin --- CMakeLists.txt | 10 +- core/CMakeLists.txt | 27 +- core/JellyfinQt.qmltypes | 1828 ++++++++++++++++++++++++++++ core/include/JellyfinQt/jellyfin.h | 33 +- core/qmldir | 2 + core/src/jellyfin.cpp | 27 +- qtquick/src/main.cpp | 6 +- sailfish/CMakeLists.txt | 43 +- sailfish/src/harbour-sailfin.cpp | 5 - 9 files changed, 1923 insertions(+), 58 deletions(-) create mode 100644 core/JellyfinQt.qmltypes diff --git a/CMakeLists.txt b/CMakeLists.txt index 8047fcb..ed71c47 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,16 +14,20 @@ option(BUILD_PRECOMPILED_HEADERS "Build with precompiled headers for faster comp option(USE_MLITE "Build with mlite (MeeGo lite library), only available on Linux-based platforms. Used to store settings using DConf." OFF) if (NOT SAILFIN_VERSION) - set(SAILFIN_VERSION "1.0.0") + set(SAILFIN_VERSION "1.0.0") endif() if(PLATFORM_SAILFISHOS) # Hardcode this less? - set(CMAKE_INSTALL_LIBDIR "${CMAKE_INSTALL_PREFIX}/share/harbour-sailfin/lib") - set(USE_MLITE ON) + set(CMAKE_INSTALL_LIBDIR "${CMAKE_INSTALL_PREFIX}/share/harbour-sailfin/nl/netsoj/chris/Jellyfin") + set(USE_MLITE ON) endif() +if (PLATFORM_SAILFISHOS) + set(CMAKE_BUILD_SHARED_LIBS NO) +endif() add_subdirectory(core) + if(PLATFORM_SAILFISHOS) add_subdirectory(sailfish) elseif(PLATFORM_QTQUICK) diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index b339c3f..f14f161 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -101,7 +101,7 @@ endif() -add_library(JellyfinQt ${JellyfinQt_SOURCES} ${JellyfinQt_HEADERS}) +add_library(JellyfinQt ${JellyfinQt_SOURCES} ${JellyfinQt_HEADERS} qmldir JellyfinQt.qmltypes) if(${CMAKE_VERSION} VERSION_GREATER "3.16.0") if(BUILD_PRECOMPILED_HEADERS) @@ -128,4 +128,29 @@ install(TARGETS JellyfinQt INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") +add_custom_target(qmltypes + COMMAND qmlplugindump -nonrelocatable nl.netsoj.chris.Jellyfin 1.0 > $$PWD/JellyfinQt.qmltypes) + +add_custom_command( + TARGET JellyfinQt + POST_BUILD + COMMAND + ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_LIST_DIR}/qmldir + $/qmldir +) + +add_custom_command( + TARGET JellyfinQt + POST_BUILD + COMMAND + ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_LIST_DIR}/JellyfinQt.qmltypes + $/JellyfinQt.qmltypes +) + + +set(QML_IMPORT_PATH $ CACHE PATH "") +install(FILES $/qmldir DESTINATION ${CMAKE_INSTALL_LIBDIR}) + export(TARGETS JellyfinQt FILE JellyfinQtConfig.cmake) diff --git a/core/JellyfinQt.qmltypes b/core/JellyfinQt.qmltypes new file mode 100644 index 0000000..a6dca09 --- /dev/null +++ b/core/JellyfinQt.qmltypes @@ -0,0 +1,1828 @@ +import QtQuick.tooling 1.2 + +// This file describes the plugin-supplied types contained in the library. +// It is used for QML tooling purposes only. +// +// This file was auto-generated by: +// 'qmlplugindump -v nl.netsoj.chris.Jellyfin 1.0 imports' + +Module { + dependencies: ["QtQuick 2.0"] + Component { + name: "Jellyfin::ApiClient" + prototype: "QObject" + exports: ["ApiClient 1.0"] + exportMetaObjectRevisions: [0] + Enum { + name: "ApiError" + values: { + "JSON_ERROR": 0, + "UNEXPECTED_REPLY": 1, + "UNEXPECTED_STATUS": 2, + "INVALID_PASSWORD": 3 + } + } + Property { name: "baseUrl"; type: "string" } + Property { name: "authenticated"; type: "bool" } + Property { name: "userId"; type: "string"; isReadonly: true } + Property { name: "deviceProfile"; type: "QJsonObject"; isReadonly: true } + Property { name: "version"; type: "string"; isReadonly: true } + Property { name: "eventbus"; type: "EventBus"; isReadonly: true; isPointer: true } + Property { name: "websocket"; type: "Jellyfin::WebSocket"; isReadonly: true; isPointer: true } + Property { name: "supportedCommands"; type: "QVariantList" } + Property { + name: "settings" + type: "Jellyfin::ViewModel::Settings" + isReadonly: true + isPointer: true + } + Property { name: "online"; type: "bool"; isReadonly: true } + Signal { name: "authenticationRequired" } + Signal { + name: "authenticationError" + Parameter { name: "error"; type: "ApiError" } + } + Signal { + name: "connectionFailed" + Parameter { name: "error"; type: "ApiError" } + } + Signal { + name: "connectionSuccess" + Parameter { name: "loginMessage"; type: "string" } + } + Signal { + name: "networkError" + Parameter { name: "error"; type: "QNetworkReply::NetworkError" } + } + Signal { + name: "authenticatedChanged" + Parameter { name: "authenticated"; type: "bool" } + } + Signal { + name: "baseUrlChanged" + Parameter { name: "baseUrl"; type: "string" } + } + Signal { name: "setupRequired" } + Signal { + name: "userIdChanged" + Parameter { name: "userId"; type: "string" } + } + Signal { + name: "userDataChanged" + Parameter { name: "itemId"; type: "string" } + Parameter { name: "userData"; type: "UserData"; isPointer: true } + } + Method { name: "restoreSavedSession" } + Method { name: "setupConnection" } + Method { + name: "authenticate" + Parameter { name: "username"; type: "string" } + Parameter { name: "password"; type: "string" } + Parameter { name: "storeCredentials"; type: "bool" } + } + Method { + name: "authenticate" + Parameter { name: "username"; type: "string" } + Parameter { name: "password"; type: "string" } + } + Method { name: "deleteSession" } + Method { name: "postCapabilities" } + Method { + name: "downloadUrl" + type: "string" + Parameter { name: "itemId"; type: "string" } + } + } + Component { + name: "Jellyfin::BaseApiModel" + prototype: "QAbstractListModel" + exports: ["BaseApiModel 1.0"] + isCreatable: false + exportMetaObjectRevisions: [0] + Property { name: "loader"; type: "BaseModelLoader"; isPointer: true } + Method { name: "reload" } + Method { name: "clear" } + } + Component { + name: "Jellyfin::BaseModelLoader" + prototype: "QObject" + exports: ["BaseModelLoader 1.0"] + isCreatable: false + exportMetaObjectRevisions: [0] + Property { name: "apiClient"; type: "ApiClient"; isPointer: true } + Property { + name: "status" + type: "Jellyfin::ViewModel::ModelStatusClass::Value" + isReadonly: true + } + Property { name: "limit"; type: "int" } + Property { name: "autoReload"; type: "bool" } + Signal { name: "ready" } + Signal { + name: "apiClientChanged" + Parameter { name: "newApiClient"; type: "ApiClient"; isPointer: true } + } + Signal { + name: "limitChanged" + Parameter { name: "newLimit"; type: "int" } + } + Signal { + name: "autoReloadChanged" + Parameter { name: "newAutoReload"; type: "bool" } + } + Signal { name: "modelShouldClear" } + Signal { name: "itemsLoaded" } + Signal { name: "reloadWanted" } + Method { name: "reload" } + } + Component { + name: "Jellyfin::DTO::GeneralCommandTypeClass" + exports: ["GeneralCommandType 1.0"] + isCreatable: false + exportMetaObjectRevisions: [0] + Enum { + name: "Value" + values: { + "EnumNotSet": 0, + "MoveUp": 1, + "MoveDown": 2, + "MoveLeft": 3, + "MoveRight": 4, + "PageUp": 5, + "PageDown": 6, + "PreviousLetter": 7, + "NextLetter": 8, + "ToggleOsd": 9, + "ToggleContextMenu": 10, + "Select": 11, + "Back": 12, + "TakeScreenshot": 13, + "SendKey": 14, + "SendString": 15, + "GoHome": 16, + "GoToSettings": 17, + "VolumeUp": 18, + "VolumeDown": 19, + "Mute": 20, + "Unmute": 21, + "ToggleMute": 22, + "SetVolume": 23, + "SetAudioStreamIndex": 24, + "SetSubtitleStreamIndex": 25, + "ToggleFullscreen": 26, + "DisplayContent": 27, + "GoToSearch": 28, + "DisplayMessage": 29, + "SetRepeatMode": 30, + "ChannelUp": 31, + "ChannelDown": 32, + "Guide": 33, + "ToggleStats": 34, + "PlayMediaSource": 35, + "PlayTrailers": 36, + "SetShuffleQueue": 37, + "PlayState": 38, + "PlayNext": 39, + "ToggleOsdMenu": 40, + "Play": 41 + } + } + } + Component { + name: "Jellyfin::DTO::ImageTypeClass" + exports: ["ImageType 1.0"] + isCreatable: false + exportMetaObjectRevisions: [0] + Enum { + name: "Value" + values: { + "EnumNotSet": 0, + "Primary": 1, + "Art": 2, + "Backdrop": 3, + "Banner": 4, + "Logo": 5, + "Thumb": 6, + "Disc": 7, + "Box": 8, + "Screenshot": 9, + "Menu": 10, + "Chapter": 11, + "BoxRear": 12, + "Profile": 13 + } + } + } + Component { + name: "Jellyfin::DTO::ItemFieldsClass" + exports: ["ItemFields 1.0"] + isCreatable: false + exportMetaObjectRevisions: [0] + Enum { + name: "Value" + values: { + "EnumNotSet": 0, + "AirTime": 1, + "CanDelete": 2, + "CanDownload": 3, + "ChannelInfo": 4, + "Chapters": 5, + "ChildCount": 6, + "CumulativeRunTimeTicks": 7, + "CustomRating": 8, + "DateCreated": 9, + "DateLastMediaAdded": 10, + "DisplayPreferencesId": 11, + "Etag": 12, + "ExternalUrls": 13, + "Genres": 14, + "HomePageUrl": 15, + "ItemCounts": 16, + "MediaSourceCount": 17, + "MediaSources": 18, + "OriginalTitle": 19, + "Overview": 20, + "ParentId": 21, + "Path": 22, + "People": 23, + "PlayAccess": 24, + "ProductionLocations": 25, + "ProviderIds": 26, + "PrimaryImageAspectRatio": 27, + "RecursiveItemCount": 28, + "Settings": 29, + "ScreenshotImageTags": 30, + "SeriesPrimaryImage": 31, + "SeriesStudio": 32, + "SortName": 33, + "SpecialEpisodeNumbers": 34, + "Studios": 35, + "BasicSyncInfo": 36, + "SyncInfo": 37, + "Taglines": 38, + "Tags": 39, + "RemoteTrailers": 40, + "MediaStreams": 41, + "SeasonUserData": 42, + "ServiceName": 43, + "ThemeSongIds": 44, + "ThemeVideoIds": 45, + "ExternalEtag": 46, + "PresentationUniqueKey": 47, + "InheritedParentalRatingValue": 48, + "ExternalSeriesId": 49, + "SeriesPresentationUniqueKey": 50, + "DateLastRefreshed": 51, + "DateLastSaved": 52, + "RefreshState": 53, + "ChannelImage": 54, + "EnableMediaSourceDisplay": 55, + "Width": 56, + "Height": 57, + "ExtraIds": 58, + "LocalTrailerCount": 59, + "IsHD": 60, + "SpecialFeatureCount": 61 + } + } + } + Component { + name: "Jellyfin::DTO::PlayMethodClass" + exports: ["PlayMethod 1.0"] + isCreatable: false + exportMetaObjectRevisions: [0] + Enum { + name: "Value" + values: { + "EnumNotSet": 0, + "Transcode": 1, + "DirectStream": 2, + "DirectPlay": 3 + } + } + } + Component { + name: "Jellyfin::EventBus" + prototype: "QObject" + exports: ["EventBus 1.0"] + isCreatable: false + exportMetaObjectRevisions: [0] + Signal { + name: "itemUserDataUpdated" + Parameter { name: "itemId"; type: "string" } + Parameter { name: "userData"; type: "DTO::UserItemDataDto" } + } + Signal { + name: "displayMessage" + Parameter { name: "message"; type: "string" } + } + } + Component { + name: "Jellyfin::QObjectSettingsWrapper" + prototype: "QObject" + Property { name: "synchronous"; type: "bool" } + Property { name: "path"; type: "string" } + Property { name: "scope"; type: "Jellyfin::QObjectSettingsWrapper"; isPointer: true } + Method { name: "sync" } + Method { name: "clear" } + Method { + name: "value" + type: "QVariant" + Parameter { name: "key"; type: "string" } + Parameter { name: "defaultValue"; type: "QVariant" } + Parameter { name: "typeHint"; type: "int" } + } + Method { + name: "value" + type: "QVariant" + Parameter { name: "key"; type: "string" } + Parameter { name: "defaultValue"; type: "QVariant" } + } + Method { + name: "value" + type: "QVariant" + Parameter { name: "key"; type: "string" } + } + Method { + name: "setValue" + Parameter { name: "key"; type: "string" } + Parameter { name: "value"; type: "QVariant" } + } + } + Component { + name: "Jellyfin::ServerDiscoveryModel" + prototype: "QAbstractListModel" + exports: ["ServerDiscoveryModel 1.0"] + exportMetaObjectRevisions: [0] + Method { name: "refresh" } + } + Component { + name: "Jellyfin::ViewModel::Item" + prototype: "QObject" + exports: ["Item 1.0"] + isCreatable: false + exportMetaObjectRevisions: [0] + Property { name: "jellyfinId"; type: "string"; isReadonly: true } + Property { name: "name"; type: "string"; isReadonly: true } + Property { name: "originalTitle"; type: "string"; isReadonly: true } + Property { name: "serverId"; type: "string"; isReadonly: true } + Property { name: "etag"; type: "string"; isReadonly: true } + Property { name: "sourceType"; type: "string"; isReadonly: true } + Property { name: "playlistItemId"; type: "string"; isReadonly: true } + Property { name: "dateCreated"; type: "QDateTime"; isReadonly: true } + Property { name: "dateLastMediaAdded"; type: "QDateTime"; isReadonly: true } + Property { name: "extraType"; type: "string"; isReadonly: true } + Property { name: "airsBeforeSeasonNumber"; type: "int"; isReadonly: true } + Property { name: "airsAfterSeasonNumber"; type: "int"; isReadonly: true } + Property { name: "airsBeforeEpisodeNumber"; type: "int"; isReadonly: true } + Property { name: "runTimeTicks"; type: "qlonglong"; isReadonly: true } + Property { name: "overview"; type: "string"; isReadonly: true } + Property { name: "productionYear"; type: "int"; isReadonly: true } + Property { name: "indexNumber"; type: "int"; isReadonly: true } + Property { name: "indexNumberEnd"; type: "int"; isReadonly: true } + Property { name: "isFolder"; type: "bool"; isReadonly: true } + Property { name: "type"; type: "string"; isReadonly: true } + Property { name: "parentBackdropItemId"; type: "string"; isReadonly: true } + Property { name: "parentBackdropImageTags"; type: "QStringList"; isReadonly: true } + Property { + name: "userData" + type: "Jellyfin::ViewModel::UserData" + isReadonly: true + isPointer: true + } + Property { name: "recursiveItemCount"; type: "int"; isReadonly: true } + Property { name: "childCount"; type: "int"; isReadonly: true } + Property { name: "albumArtist"; type: "string"; isReadonly: true } + Property { name: "seriesName"; type: "string"; isReadonly: true } + Property { name: "seriesId"; type: "string"; isReadonly: true } + Property { name: "seasonId"; type: "string"; isReadonly: true } + Property { name: "seasonName"; type: "string"; isReadonly: true } + Property { name: "mediaStreams"; type: "QList"; isReadonly: true } + Property { name: "audioStreams"; type: "QList"; isReadonly: true } + Property { name: "videoStreams"; type: "QList"; isReadonly: true } + Property { name: "subtitleStreams"; type: "QList"; isReadonly: true } + Property { name: "artists"; type: "QStringList"; isReadonly: true } + Property { name: "imageTags"; type: "QJsonObject"; isReadonly: true } + Property { name: "backdropImageTags"; type: "QStringList"; isReadonly: true } + Property { name: "imageBlurHashes"; type: "QJsonObject"; isReadonly: true } + Property { name: "mediaType"; type: "string"; isReadonly: true } + Property { name: "width"; type: "int"; isReadonly: true } + Property { name: "height"; type: "int"; isReadonly: true } + Signal { + name: "jellyfinIdChanged" + Parameter { name: "newId"; type: "string" } + } + Signal { + name: "nameChanged" + Parameter { name: "newName"; type: "string" } + } + Signal { + name: "originalTitleChanged" + Parameter { name: "newOriginalTitle"; type: "string" } + } + Signal { + name: "serverIdChanged" + Parameter { name: "newServerId"; type: "string" } + } + Signal { + name: "etagChanged" + Parameter { name: "newEtag"; type: "string" } + } + Signal { + name: "sourceTypeChanged" + Parameter { name: "sourceType"; type: "string" } + } + Signal { + name: "playlistItemIdChanged" + Parameter { name: "playlistItemIdChanged"; type: "string" } + } + Signal { + name: "dateCreatedChanged" + Parameter { name: "newDateCreatedChanged"; type: "QDateTime" } + } + Signal { + name: "dateLastMediaAddedChanged" + Parameter { name: "newDateLastMediaAdded"; type: "QDateTime" } + } + Signal { + name: "extraTypeChanged" + Parameter { name: "newExtraType"; type: "string" } + } + Signal { + name: "airsBeforeSeasonNumberChanged" + Parameter { name: "newAirsBeforeSeasonNumber"; type: "int" } + } + Signal { + name: "airsAfterSeasonNumberChanged" + Parameter { name: "newAirsAfterSeasonNumber"; type: "int" } + } + Signal { + name: "airsBeforeEpisodeNumberChanged" + Parameter { name: "newAirsAfterEpisodeNumber"; type: "int" } + } + Signal { + name: "canDeleteChanged" + type: "bool" + Parameter { name: "newCanDelete"; type: "bool" } + } + Signal { + name: "canDownloadChanged" + Parameter { name: "newCanDownload"; type: "bool" } + } + Signal { + name: "hasSubtitlesChanged" + Parameter { name: "newHasSubtitles"; type: "bool" } + } + Signal { + name: "preferredMetadataLanguageChanged" + Parameter { name: "newPreferredMetadataLanguage"; type: "string" } + } + Signal { + name: "preferredMetadataCountryCodeChanged" + Parameter { name: "newPreferredMetadataCountryCode"; type: "string" } + } + Signal { + name: "supportsSyncChanged" + Parameter { name: "newSupportsSync"; type: "bool" } + } + Signal { + name: "containerChanged" + Parameter { name: "newContainer"; type: "string" } + } + Signal { + name: "sortNameChanged" + Parameter { name: "newSortName"; type: "string" } + } + Signal { + name: "forcedSortNameChanged" + Parameter { name: "newForcedSortName"; type: "string" } + } + Signal { + name: "premiereDateChanged" + Parameter { name: "newPremiereDate"; type: "QDateTime" } + } + Signal { + name: "criticRatingChanged" + Parameter { name: "newCriticRating"; type: "float" } + } + Signal { + name: "productionLocationsChanged" + Parameter { name: "newProductionLocations"; type: "QStringList" } + } + Signal { + name: "runTimeTicksChanged" + Parameter { name: "newRunTimeTicks"; type: "qlonglong" } + } + Signal { + name: "overviewChanged" + Parameter { name: "newOverview"; type: "string" } + } + Signal { + name: "productionYearChanged" + Parameter { name: "newProductionYear"; type: "int" } + } + Signal { + name: "indexNumberChanged" + Parameter { name: "newIndexNumber"; type: "int" } + } + Signal { + name: "indexNumberEndChanged" + Parameter { name: "newIndexNumberEnd"; type: "int" } + } + Signal { + name: "isFolderChanged" + Parameter { name: "newIsFolder"; type: "bool" } + } + Signal { + name: "typeChanged" + Parameter { name: "newType"; type: "string" } + } + Signal { + name: "userDataChanged" + Parameter { name: "newUserData"; type: "UserData"; isPointer: true } + } + Signal { + name: "recursiveItemCountChanged" + Parameter { name: "newRecursiveItemCount"; type: "int" } + } + Signal { + name: "childCountChanged" + Parameter { name: "newChildCount"; type: "int" } + } + Signal { + name: "albumArtistChanged" + Parameter { name: "newAlbumArtist"; type: "string" } + } + Signal { + name: "seriesNameChanged" + Parameter { name: "newSeriesName"; type: "string" } + } + Signal { + name: "seriesIdChanged" + Parameter { name: "newSeriesId"; type: "string" } + } + Signal { + name: "seasonIdChanged" + Parameter { name: "newSeasonId"; type: "string" } + } + Signal { + name: "seasonNameChanged" + Parameter { name: "newSeasonName"; type: "string" } + } + Signal { + name: "mediaStreamsChanged" + Parameter { name: "newMediaStreams"; type: "QVariantList&" } + } + Signal { + name: "audioStreamsChanged" + Parameter { name: "newAudioStreams"; type: "QVariantList&" } + } + Signal { + name: "videoStreamsChanged" + Parameter { name: "newVideoStreams"; type: "QVariantList&" } + } + Signal { + name: "subtitleStreamsChanged" + Parameter { name: "newSubtitleStreams"; type: "QVariantList&" } + } + Signal { + name: "artistsChanged" + Parameter { name: "newArtists"; type: "QStringList" } + } + Signal { + name: "mediaTypeChanged" + Parameter { name: "newMediaType"; type: "string" } + } + Signal { + name: "widthChanged" + Parameter { name: "newWidth"; type: "int" } + } + Signal { + name: "heightChanged" + Parameter { name: "newHeight"; type: "int" } + } + } + Component { + name: "Jellyfin::ViewModel::ItemLoader" + prototype: "Jellyfin::ViewModel::LoaderBase" + exports: ["ItemLoader 1.0"] + exportMetaObjectRevisions: [0] + Property { name: "itemId"; type: "string" } + Signal { + name: "itemIdChanged" + Parameter { name: "newItemId"; type: "string" } + } + } + Component { + name: "Jellyfin::ViewModel::ItemModel" + prototype: "Jellyfin::BaseApiModel" + exports: ["ItemModel 1.0"] + exportMetaObjectRevisions: [0] + } + Component { + name: "Jellyfin::ViewModel::LatestMediaLoader" + prototype: "Jellyfin::BaseModelLoader" + exports: ["LatestMediaLoader 1.0"] + exportMetaObjectRevisions: [0] + Property { name: "enableImageTypes"; type: "QList" } + Property { name: "enableImages"; type: "bool" } + Property { name: "enableUserData"; type: "bool" } + Property { name: "fields"; type: "QList" } + Property { name: "groupItems"; type: "bool" } + Property { name: "imageTypeLimit"; type: "int" } + Property { name: "includeItemTypes"; type: "QStringList" } + Property { name: "isPlayed"; type: "bool" } + Property { name: "parentId"; type: "string" } + } + Component { + name: "Jellyfin::ViewModel::LoaderBase" + prototype: "QObject" + exports: ["LoaderBase 1.0"] + isCreatable: false + exportMetaObjectRevisions: [0] + Enum { + name: "Status" + values: { + "Uninitialised": 0, + "Loading": 1, + "Ready": 2, + "Error": 3 + } + } + Property { name: "apiClient"; type: "ApiClient"; isPointer: true } + Property { name: "status"; type: "Status"; isReadonly: true } + Property { name: "errorString"; type: "string"; isReadonly: true } + Property { name: "autoReload"; type: "bool" } + Property { name: "data"; type: "QObject"; isReadonly: true; isPointer: true } + Signal { + name: "statusChanged" + Parameter { name: "newStatus"; type: "Status" } + } + Signal { + name: "apiClientChanged" + Parameter { name: "newApiClient"; type: "ApiClient"; isPointer: true } + } + Signal { + name: "errorStringChanged" + Parameter { name: "newErrorString"; type: "string" } + } + Signal { + name: "autoReloadChanged" + Parameter { name: "newAutoReload"; type: "bool" } + } + Signal { name: "ready" } + Method { name: "reload" } + } + Component { + name: "Jellyfin::ViewModel::MediaStream" + prototype: "QObject" + exports: ["MediaStream 1.0"] + isCreatable: false + exportMetaObjectRevisions: [0] + Property { name: "codec"; type: "string"; isReadonly: true } + Property { name: "codecTag"; type: "string"; isReadonly: true } + Property { name: "language"; type: "string"; isReadonly: true } + Property { name: "colorRange"; type: "string"; isReadonly: true } + Property { name: "colorSpace"; type: "string"; isReadonly: true } + Property { name: "colorTransfer"; type: "string"; isReadonly: true } + Property { name: "colorPrimaries"; type: "string"; isReadonly: true } + Property { name: "comment"; type: "string"; isReadonly: true } + Property { name: "timeBase"; type: "string"; isReadonly: true } + Property { name: "title"; type: "string"; isReadonly: true } + Property { name: "videoRange"; type: "string"; isReadonly: true } + Property { name: "localizedUndefined"; type: "string"; isReadonly: true } + Property { name: "localizedDefault"; type: "string"; isReadonly: true } + Property { name: "localizedForced"; type: "string"; isReadonly: true } + Property { name: "displayTitle"; type: "string"; isReadonly: true } + Property { name: "nalLengthSize"; type: "string"; isReadonly: true } + Property { name: "interlaced"; type: "bool"; isReadonly: true } + Property { name: "avc"; type: "bool"; isReadonly: true } + Property { name: "channelLayout"; type: "string"; isReadonly: true } + Property { name: "bitRate"; type: "int"; isReadonly: true } + Property { name: "bitDepth"; type: "int"; isReadonly: true } + Property { name: "refFrames"; type: "int"; isReadonly: true } + Property { name: "packetLength"; type: "int"; isReadonly: true } + Property { name: "channels"; type: "int"; isReadonly: true } + Property { name: "sampleRate"; type: "int"; isReadonly: true } + Property { name: "isDefault"; type: "bool"; isReadonly: true } + Property { name: "forced"; type: "bool"; isReadonly: true } + Property { name: "width"; type: "int"; isReadonly: true } + Property { name: "height"; type: "int"; isReadonly: true } + Property { name: "averageFrameRate"; type: "float"; isReadonly: true } + Property { name: "realFrameRate"; type: "float"; isReadonly: true } + Property { name: "profile"; type: "string"; isReadonly: true } + Property { name: "type"; type: "Jellyfin::DTO::MediaStreamTypeClass::Value"; isReadonly: true } + Property { name: "aspectRatio"; type: "string"; isReadonly: true } + Property { name: "index"; type: "int"; isReadonly: true } + Signal { + name: "codecChanged" + Parameter { name: "newCodec"; type: "string" } + } + Signal { + name: "codecTagChanged" + Parameter { name: "newCodecTag"; type: "string" } + } + Signal { + name: "languageChanged" + Parameter { name: "newLanguage"; type: "string" } + } + Signal { + name: "colorRangeChanged" + Parameter { name: "newColorRange"; type: "string" } + } + Signal { + name: "colorSpaceChanged" + Parameter { name: "newColorSpace"; type: "string" } + } + Signal { + name: "colorTransferChanged" + Parameter { name: "newColorTransfer"; type: "string" } + } + Signal { + name: "colorPrimariesChanged" + Parameter { name: "newColorPrimaries"; type: "string" } + } + Signal { + name: "commentChanged" + Parameter { name: "newComment"; type: "string" } + } + Signal { + name: "timeBaseChanged" + Parameter { name: "newTimeBase"; type: "string" } + } + Signal { + name: "titleChanged" + Parameter { name: "newTitle"; type: "string" } + } + Signal { + name: "videoRangeChanged" + Parameter { name: "newVideoRanged"; type: "string" } + } + Signal { + name: "localizedUndefinedChanged" + Parameter { name: "newLocalizedUndefined"; type: "string" } + } + Signal { + name: "localizedDefaultChanged" + Parameter { name: "newLocalizedDefault"; type: "string" } + } + Signal { + name: "localizedForcedChanged" + Parameter { name: "newLocalizedForced"; type: "string" } + } + Signal { + name: "displayTitleChanged" + Parameter { name: "newDisplayTitle"; type: "string" } + } + Signal { + name: "nalLengthSizeChanged" + Parameter { name: "newNalLengthSize"; type: "string" } + } + Signal { + name: "interlacedChanged" + Parameter { name: "newInterlaced"; type: "bool" } + } + Signal { + name: "avcChanged" + Parameter { name: "newAVC"; type: "bool" } + } + Signal { + name: "channelLayoutChanged" + Parameter { name: "newChannelLayout"; type: "string" } + } + Signal { + name: "bitRateChanged" + Parameter { name: "newBitRate"; type: "int" } + } + Signal { + name: "bitDepthChanged" + Parameter { name: "newBitDepth"; type: "int" } + } + Signal { + name: "refFramesChanged" + Parameter { name: "newRefFrames"; type: "int" } + } + Signal { + name: "packetLengthChanged" + Parameter { name: "newPacketLength"; type: "int" } + } + Signal { + name: "channelsChanged" + Parameter { name: "newChannels"; type: "int" } + } + Signal { + name: "sampleRateChanged" + Parameter { name: "newSampleRate"; type: "int" } + } + Signal { + name: "isDefaultChanged" + Parameter { name: "newIsDefault"; type: "bool" } + } + Signal { + name: "forcedChanged" + Parameter { name: "newForced"; type: "bool" } + } + Signal { + name: "heightChanged" + Parameter { name: "newHeight"; type: "int" } + } + Signal { + name: "widthChanged" + Parameter { name: "newWidth"; type: "int" } + } + Signal { + name: "averageFrameRateChanged" + Parameter { name: "newAverageFrameRate"; type: "float" } + } + Signal { + name: "realFrameRateChanged" + Parameter { name: "newRealFrameRate"; type: "float" } + } + Signal { + name: "profileChanged" + Parameter { name: "newProfile"; type: "string" } + } + Signal { + name: "typeChanged" + Parameter { name: "newType"; type: "Jellyfin::DTO::MediaStreamTypeClass::Value" } + } + Signal { + name: "aspectRatioChanged" + Parameter { name: "newAspectRatio"; type: "string" } + } + Signal { + name: "indexChanged" + Parameter { name: "newIndex"; type: "int" } + } + } + Component { + name: "Jellyfin::ViewModel::ModelStatusClass" + exports: ["ModelStatus 1.0"] + isCreatable: false + exportMetaObjectRevisions: [0] + Enum { + name: "Value" + values: { + "Uninitialised": 0, + "Loading": 1, + "Ready": 2, + "Error": 3, + "LoadingMore": 4 + } + } + } + Component { + name: "Jellyfin::ViewModel::NowPlayingSection" + exports: ["NowPlayingSection 1.0"] + isCreatable: false + exportMetaObjectRevisions: [0] + Enum { + name: "Value" + values: { + "Queue": 0, + "NowPlaying": 1 + } + } + } + Component { + name: "Jellyfin::ViewModel::PlatformMediaControl" + prototype: "QObject" + exports: ["PlatformMediaControl 1.0"] + exportMetaObjectRevisions: [0] + Property { + name: "playbackManager" + type: "Jellyfin::ViewModel::PlaybackManager" + isPointer: true + } + Property { name: "canQuit"; type: "bool" } + Property { name: "canRaise"; type: "bool" } + Property { name: "playerName"; type: "string" } + Property { name: "desktopFile"; type: "string" } + Signal { + name: "playbackManagerChanged" + Parameter { name: "newPlaybackManager"; type: "PlaybackManager"; isPointer: true } + } + Signal { + name: "canQuitChanged" + Parameter { name: "newCanQuit"; type: "bool" } + } + Signal { + name: "canRaiseChanged" + Parameter { name: "newCanRaise"; type: "bool" } + } + Signal { + name: "playerNameChanged" + Parameter { name: "newPlayerName"; type: "string" } + } + Signal { + name: "desktopFileChanged" + Parameter { name: "newDesktopFile"; type: "string" } + } + Signal { name: "quitRequested" } + Signal { name: "raiseRequested" } + } + Component { + name: "Jellyfin::ViewModel::PlaybackManager" + prototype: "QObject" + exports: ["PlaybackManager 1.0"] + exportMetaObjectRevisions: [0] + Property { name: "apiClient"; type: "ApiClient"; isPointer: true } + Property { name: "streamUrl"; type: "string"; isReadonly: true } + Property { name: "autoOpen"; type: "bool" } + Property { name: "audioIndex"; type: "int" } + Property { name: "subtitleIndex"; type: "int" } + Property { name: "resumePlayback"; type: "bool" } + Property { name: "playMethod"; type: "Jellyfin::DTO::PlayMethodClass::Value"; isReadonly: true } + Property { name: "item"; type: "QObject"; isReadonly: true; isPointer: true } + Property { name: "queueIndex"; type: "int"; isReadonly: true } + Property { + name: "queue" + type: "Jellyfin::ViewModel::Playlist" + isReadonly: true + isPointer: true + } + Property { name: "duration"; type: "qlonglong"; isReadonly: true } + Property { name: "error"; type: "QMediaPlayer::Error"; isReadonly: true } + Property { name: "errorString"; type: "string"; isReadonly: true } + Property { name: "hasVideo"; type: "bool"; isReadonly: true } + Property { name: "seekable"; type: "bool"; isReadonly: true } + Property { name: "mediaObject"; type: "QObject"; isReadonly: true; isPointer: true } + Property { name: "mediaStatus"; type: "QMediaPlayer::MediaStatus"; isReadonly: true } + Property { name: "playbackState"; type: "QMediaPlayer::State"; isReadonly: true } + Property { name: "position"; type: "qlonglong"; isReadonly: true } + Property { name: "hasNext"; type: "bool"; isReadonly: true } + Property { name: "hasPrevious"; type: "bool"; isReadonly: true } + Signal { + name: "itemChanged" + Parameter { name: "newItemId"; type: "ViewModel::Item"; isPointer: true } + } + Signal { + name: "streamUrlChanged" + Parameter { name: "newStreamUrl"; type: "string" } + } + Signal { + name: "autoOpenChanged" + Parameter { name: "autoOpen"; type: "bool" } + } + Signal { + name: "audioIndexChanged" + Parameter { name: "audioIndex"; type: "int" } + } + Signal { + name: "subtitleIndexChanged" + Parameter { name: "subtitleIndex"; type: "int" } + } + Signal { + name: "mediaPlayerChanged" + Parameter { name: "newMediaPlayer"; type: "QObject"; isPointer: true } + } + Signal { + name: "resumePlaybackChanged" + Parameter { name: "newResumePlayback"; type: "bool" } + } + Signal { + name: "playMethodChanged" + Parameter { name: "newPlayMethod"; type: "PlayMethod" } + } + Signal { + name: "mediaObjectChanged" + Parameter { name: "newMediaObject"; type: "QObject"; isPointer: true } + } + Signal { + name: "positionChanged" + Parameter { name: "newPosition"; type: "qlonglong" } + } + Signal { + name: "durationChanged" + Parameter { name: "newDuration"; type: "qlonglong" } + } + Signal { + name: "queueChanged" + Parameter { name: "newQueue"; type: "QAbstractItemModel"; isPointer: true } + } + Signal { + name: "queueIndexChanged" + Parameter { name: "newIndex"; type: "int" } + } + Signal { + name: "playbackStateChanged" + Parameter { name: "newState"; type: "QMediaPlayer::State" } + } + Signal { + name: "mediaStatusChanged" + Parameter { name: "newMediaStatus"; type: "QMediaPlayer::MediaStatus" } + } + Signal { + name: "hasVideoChanged" + Parameter { name: "newHasVideo"; type: "bool" } + } + Signal { + name: "seekableChanged" + Parameter { name: "newSeekable"; type: "bool" } + } + Signal { + name: "errorChanged" + Parameter { name: "newError"; type: "QMediaPlayer::Error" } + } + Signal { + name: "errorStringChanged" + Parameter { name: "newErrorString"; type: "string" } + } + Signal { + name: "hasNextChanged" + Parameter { name: "newHasNext"; type: "bool" } + } + Signal { + name: "hasPreviousChanged" + Parameter { name: "newHasPrevious"; type: "bool" } + } + Method { + name: "playItem" + Parameter { name: "item"; type: "Item"; isPointer: true } + } + Method { + name: "playItemInList" + Parameter { name: "itemList"; type: "ItemModel"; isPointer: true } + Parameter { name: "index"; type: "int" } + } + Method { + name: "skipToItemIndex" + Parameter { name: "index"; type: "int" } + } + Method { name: "play" } + Method { name: "pause" } + Method { + name: "seek" + Parameter { name: "pos"; type: "qlonglong" } + } + Method { name: "stop" } + Method { name: "previous" } + Method { name: "next" } + } + Component { + name: "Jellyfin::ViewModel::Playlist" + prototype: "QAbstractListModel" + exports: ["Playlist 1.0"] + isCreatable: false + exportMetaObjectRevisions: [0] + } + Component { + name: "Jellyfin::ViewModel::PublicUsersLoader" + prototype: "Jellyfin::BaseModelLoader" + exports: ["PublicUsersLoader 1.0"] + exportMetaObjectRevisions: [0] + } + Component { + name: "Jellyfin::ViewModel::ResumeItemsLoader" + prototype: "Jellyfin::BaseModelLoader" + exports: ["ResumeItemsLoader 1.0"] + exportMetaObjectRevisions: [0] + Property { name: "enableImageTypes"; type: "QList" } + Property { name: "enableImages"; type: "bool" } + Property { name: "enableTotalRecordCount"; type: "bool" } + Property { name: "enableUserData"; type: "bool" } + Property { name: "excludeItemTypes"; type: "QStringList" } + Property { name: "fields"; type: "QList" } + Property { name: "imageTypeLimit"; type: "int" } + Property { name: "includeItemTypes"; type: "QStringList" } + Property { name: "mediaTypes"; type: "QStringList" } + Property { name: "parentId"; type: "string" } + Property { name: "searchTerm"; type: "string" } + } + Component { + name: "Jellyfin::ViewModel::Settings" + prototype: "Jellyfin::QObjectSettingsWrapper" + exports: ["Settings 1.0"] + isCreatable: false + exportMetaObjectRevisions: [0] + Property { name: "allowTranscoding"; type: "bool" } + Property { name: "maxStreamingBitRate"; type: "int" } + Signal { + name: "allowTranscodingChanged" + Parameter { name: "newAllowTranscoding"; type: "bool" } + } + Signal { + name: "maxStreamingBitRateChanged" + Parameter { name: "newMaxBitRate"; type: "int" } + } + } + Component { + name: "Jellyfin::ViewModel::ShowEpisodesLoader" + prototype: "Jellyfin::BaseModelLoader" + exports: ["ShowEpisodesLoader 1.0"] + exportMetaObjectRevisions: [0] + Property { name: "seriesId"; type: "string" } + Property { name: "adjacentTo"; type: "string" } + Property { name: "enableImages"; type: "bool" } + Property { name: "enableUserData"; type: "bool" } + Property { name: "fields"; type: "QList" } + Property { name: "imageTypeLimit"; type: "int" } + Property { name: "isMissing"; type: "bool" } + Property { name: "season"; type: "int" } + Property { name: "seasonId"; type: "string" } + Property { name: "sortBy"; type: "string" } + Property { name: "startItemId"; type: "string" } + } + Component { + name: "Jellyfin::ViewModel::ShowSeasonsLoader" + prototype: "Jellyfin::BaseModelLoader" + exports: ["ShowSeasonsLoader 1.0"] + exportMetaObjectRevisions: [0] + Property { name: "seriesId"; type: "string" } + Property { name: "adjacentTo"; type: "string" } + Property { name: "enableImageTypes"; type: "QList" } + Property { name: "enableImages"; type: "bool" } + Property { name: "enableUserData"; type: "bool" } + Property { name: "fields"; type: "QList" } + Property { name: "imageTypeLimit"; type: "int" } + Property { name: "isMissing"; type: "bool" } + Property { name: "isSpecialSeason"; type: "bool" } + } + Component { + name: "Jellyfin::ViewModel::User" + prototype: "QObject" + exports: ["User 1.0"] + isCreatable: false + exportMetaObjectRevisions: [0] + Property { name: "name"; type: "string"; isReadonly: true } + Property { name: "serverId"; type: "string"; isReadonly: true } + Property { name: "serverName"; type: "string"; isReadonly: true } + Property { name: "userId"; type: "string"; isReadonly: true } + Property { name: "primaryImageTag"; type: "string"; isReadonly: true } + Property { name: "hasPassword"; type: "bool"; isReadonly: true } + Property { name: "hasConfiguredPassword"; type: "bool"; isReadonly: true } + Property { name: "hasConfiguredEasyPassword"; type: "bool"; isReadonly: true } + Signal { + name: "nameChanged" + Parameter { name: "newName"; type: "string" } + } + Signal { + name: "serverIdChanged" + Parameter { name: "newServerId"; type: "string" } + } + Signal { + name: "serverNameChanged" + Parameter { name: "newServerName"; type: "string" } + } + Signal { + name: "userIdChanged" + Parameter { name: "newUserId"; type: "string" } + } + Signal { + name: "primaryImageTagChanged" + Parameter { name: "newPrimaryImageTag"; type: "string" } + } + Signal { + name: "hasPasswordChanged" + Parameter { name: "newHasPassword"; type: "bool" } + } + Signal { + name: "hasConfiguredPasswordChanged" + Parameter { name: "newHasConfiguredPassword"; type: "bool" } + } + Signal { + name: "hasConfiguredEasyPasswordChanged" + Parameter { name: "newHasConfiguredEasyPasswordChanged"; type: "bool" } + } + } + Component { + name: "Jellyfin::ViewModel::UserData" + prototype: "QObject" + exports: ["UserData 1.0"] + isCreatable: false + exportMetaObjectRevisions: [0] + Property { name: "rating"; type: "double"; isReadonly: true } + Property { name: "playedPercentage"; type: "double"; isReadonly: true } + Property { name: "unplayedItemCount"; type: "int"; isReadonly: true } + Property { name: "playbackPositionTicks"; type: "qlonglong"; isReadonly: true } + Property { name: "playCount"; type: "int"; isReadonly: true } + Property { name: "favorite"; type: "bool"; isReadonly: true } + Property { name: "m_likes"; type: "bool"; isReadonly: true } + Property { name: "lastPlayedDate"; type: "QDateTime"; isReadonly: true } + Property { name: "played"; type: "bool"; isReadonly: true } + Property { name: "key"; type: "string"; isReadonly: true } + Signal { + name: "ratingChanged" + Parameter { name: "newRating"; type: "double" } + } + Signal { + name: "playedPercentageChanged" + Parameter { name: "newPlayedPercentage"; type: "double" } + } + Signal { + name: "unplayedItemCountChanged" + Parameter { name: "newUnplayedItemCount"; type: "int" } + } + Signal { + name: "playbackPositionTicksChanged" + Parameter { name: "newPlaybackPositionTicks"; type: "qlonglong" } + } + Signal { + name: "playCountChanged" + Parameter { name: "newPlayCount"; type: "int" } + } + Signal { + name: "favoriteChanged" + Parameter { name: "newFavorite"; type: "bool" } + } + Signal { + name: "likesChanged" + Parameter { name: "newLikes"; type: "bool" } + } + Signal { + name: "lastPlayedDateChanged" + Parameter { name: "newLastPlayedDate"; type: "QDateTime" } + } + Signal { + name: "playedChanged" + Parameter { name: "newPLayed"; type: "bool" } + } + Signal { + name: "keyChanged" + Parameter { name: "newKey"; type: "string" } + } + } + Component { + name: "Jellyfin::ViewModel::UserItemsLoader" + prototype: "Jellyfin::BaseModelLoader" + exports: ["UserItemsLoader 1.0"] + exportMetaObjectRevisions: [0] + Property { name: "adjacentTo"; type: "string" } + Property { name: "albumArtistIds"; type: "QStringList" } + Property { name: "albumIds"; type: "QStringList" } + Property { name: "albums"; type: "QStringList" } + Property { name: "artistIds"; type: "QStringList" } + Property { name: "artists"; type: "QStringList" } + Property { name: "collapseBoxSetItems"; type: "bool" } + Property { name: "contributingArtistIds"; type: "QStringList" } + Property { name: "enableImageTypes"; type: "QList" } + Property { name: "enableImages"; type: "bool" } + Property { name: "enableTotalRecordCount"; type: "bool" } + Property { name: "enableUserData"; type: "bool" } + Property { name: "excludeArtistIds"; type: "QStringList" } + Property { name: "excludeItemIds"; type: "QStringList" } + Property { name: "excludeItemTypes"; type: "QStringList" } + Property { + name: "excludeLocationTypes" + type: "QList" + } + Property { name: "fields"; type: "QList" } + Property { name: "filters"; type: "QList" } + Property { name: "genreIds"; type: "QStringList" } + Property { name: "genres"; type: "QStringList" } + Property { name: "hasImdbId"; type: "bool" } + Property { name: "hasOfficialRating"; type: "bool" } + Property { name: "hasOverview"; type: "bool" } + Property { name: "hasParentalRating"; type: "bool" } + Property { name: "hasSpecialFeature"; type: "bool" } + Property { name: "hasSubtitles"; type: "bool" } + Property { name: "hasThemeSong"; type: "bool" } + Property { name: "hasThemeVideo"; type: "bool" } + Property { name: "hasTmdbId"; type: "bool" } + Property { name: "hasTrailer"; type: "bool" } + Property { name: "hasTvdbId"; type: "bool" } + Property { name: "ids"; type: "QStringList" } + Property { name: "imageTypeLimit"; type: "int" } + Property { name: "imageTypes"; type: "QList" } + Property { name: "includeItemTypes"; type: "QStringList" } + Property { name: "is3D"; type: "bool" } + Property { name: "is4K"; type: "bool" } + Property { name: "isFavorite"; type: "bool" } + Property { name: "isHd"; type: "bool" } + Property { name: "isLocked"; type: "bool" } + Property { name: "isMissing"; type: "bool" } + Property { name: "isPlaceHolder"; type: "bool" } + Property { name: "isPlayed"; type: "bool" } + Property { name: "isUnaired"; type: "bool" } + Property { name: "limit"; type: "int" } + Property { name: "locationTypes"; type: "QList" } + Property { name: "maxHeight"; type: "int" } + Property { name: "maxOfficialRating"; type: "string" } + Property { name: "maxPremiereDate"; type: "QDateTime" } + Property { name: "maxWidth"; type: "int" } + Property { name: "mediaTypes"; type: "QStringList" } + Property { name: "minHeight"; type: "int" } + Property { name: "minOfficialRating"; type: "string" } + Property { name: "minPremiereDate"; type: "QDateTime" } + Property { name: "minWidth"; type: "int" } + Property { name: "sortBy"; type: "string" } + Property { name: "sortOrder"; type: "string" } + Property { name: "tags"; type: "QStringList" } + Property { name: "years"; type: "QList" } + Property { name: "parentId"; type: "string" } + Property { name: "recursive"; type: "bool" } + Property { name: "searchTerm"; type: "string" } + } + Component { + name: "Jellyfin::ViewModel::UserLoader" + prototype: "Jellyfin::ViewModel::LoaderBase" + exports: ["UserLoader 1.0"] + exportMetaObjectRevisions: [0] + Property { name: "userId"; type: "string" } + Signal { + name: "userIdChanged" + Parameter { name: "newUserId"; type: "string" } + } + } + Component { + name: "Jellyfin::ViewModel::UserModel" + prototype: "Jellyfin::BaseApiModel" + exports: ["UserModel 1.0"] + exportMetaObjectRevisions: [0] + } + Component { + name: "Jellyfin::ViewModel::UserViewsLoader" + prototype: "Jellyfin::BaseModelLoader" + exports: ["UsersViewsLoader 1.0"] + exportMetaObjectRevisions: [0] + Property { name: "includeExternalContent"; type: "bool" } + Property { name: "includeHidden"; type: "bool" } + Property { name: "presetViews"; type: "QStringList" } + } + Component { + name: "Jellyfin::WebSocket" + prototype: "QObject" + exports: ["WebSocket 1.0"] + isCreatable: false + exportMetaObjectRevisions: [0] + Enum { + name: "MessageType" + values: { + "ForceKeepAlive": 0, + "KeepAlive": 1, + "UserDataChanged": 2 + } + } + Property { name: "state"; type: "QAbstractSocket::SocketState"; isReadonly: true } + Signal { + name: "commandReceived" + Parameter { name: "arts"; type: "string" } + Parameter { name: "args"; type: "QVariantMap" } + } + Signal { + name: "stateChanged" + Parameter { name: "newState"; type: "QAbstractSocket::SocketState" } + } + Method { name: "open" } + } + Component { + name: "QAbstractItemModel" + prototype: "QObject" + Enum { + name: "LayoutChangeHint" + values: { + "NoLayoutChangeHint": 0, + "VerticalSortHint": 1, + "HorizontalSortHint": 2 + } + } + Enum { + name: "CheckIndexOption" + values: { + "NoOption": 0, + "IndexIsValid": 1, + "DoNotUseParent": 2, + "ParentIsInvalid": 4 + } + } + Signal { + name: "dataChanged" + Parameter { name: "topLeft"; type: "QModelIndex" } + Parameter { name: "bottomRight"; type: "QModelIndex" } + Parameter { name: "roles"; type: "QVector" } + } + Signal { + name: "dataChanged" + Parameter { name: "topLeft"; type: "QModelIndex" } + Parameter { name: "bottomRight"; type: "QModelIndex" } + } + Signal { + name: "headerDataChanged" + Parameter { name: "orientation"; type: "Qt::Orientation" } + Parameter { name: "first"; type: "int" } + Parameter { name: "last"; type: "int" } + } + Signal { + name: "layoutChanged" + Parameter { name: "parents"; type: "QList" } + Parameter { name: "hint"; type: "QAbstractItemModel::LayoutChangeHint" } + } + Signal { + name: "layoutChanged" + Parameter { name: "parents"; type: "QList" } + } + Signal { name: "layoutChanged" } + Signal { + name: "layoutAboutToBeChanged" + Parameter { name: "parents"; type: "QList" } + Parameter { name: "hint"; type: "QAbstractItemModel::LayoutChangeHint" } + } + Signal { + name: "layoutAboutToBeChanged" + Parameter { name: "parents"; type: "QList" } + } + Signal { name: "layoutAboutToBeChanged" } + Signal { + name: "rowsAboutToBeInserted" + Parameter { name: "parent"; type: "QModelIndex" } + Parameter { name: "first"; type: "int" } + Parameter { name: "last"; type: "int" } + } + Signal { + name: "rowsInserted" + Parameter { name: "parent"; type: "QModelIndex" } + Parameter { name: "first"; type: "int" } + Parameter { name: "last"; type: "int" } + } + Signal { + name: "rowsAboutToBeRemoved" + Parameter { name: "parent"; type: "QModelIndex" } + Parameter { name: "first"; type: "int" } + Parameter { name: "last"; type: "int" } + } + Signal { + name: "rowsRemoved" + Parameter { name: "parent"; type: "QModelIndex" } + Parameter { name: "first"; type: "int" } + Parameter { name: "last"; type: "int" } + } + Signal { + name: "columnsAboutToBeInserted" + Parameter { name: "parent"; type: "QModelIndex" } + Parameter { name: "first"; type: "int" } + Parameter { name: "last"; type: "int" } + } + Signal { + name: "columnsInserted" + Parameter { name: "parent"; type: "QModelIndex" } + Parameter { name: "first"; type: "int" } + Parameter { name: "last"; type: "int" } + } + Signal { + name: "columnsAboutToBeRemoved" + Parameter { name: "parent"; type: "QModelIndex" } + Parameter { name: "first"; type: "int" } + Parameter { name: "last"; type: "int" } + } + Signal { + name: "columnsRemoved" + Parameter { name: "parent"; type: "QModelIndex" } + Parameter { name: "first"; type: "int" } + Parameter { name: "last"; type: "int" } + } + Signal { name: "modelAboutToBeReset" } + Signal { name: "modelReset" } + Signal { + name: "rowsAboutToBeMoved" + Parameter { name: "sourceParent"; type: "QModelIndex" } + Parameter { name: "sourceStart"; type: "int" } + Parameter { name: "sourceEnd"; type: "int" } + Parameter { name: "destinationParent"; type: "QModelIndex" } + Parameter { name: "destinationRow"; type: "int" } + } + Signal { + name: "rowsMoved" + Parameter { name: "parent"; type: "QModelIndex" } + Parameter { name: "start"; type: "int" } + Parameter { name: "end"; type: "int" } + Parameter { name: "destination"; type: "QModelIndex" } + Parameter { name: "row"; type: "int" } + } + Signal { + name: "columnsAboutToBeMoved" + Parameter { name: "sourceParent"; type: "QModelIndex" } + Parameter { name: "sourceStart"; type: "int" } + Parameter { name: "sourceEnd"; type: "int" } + Parameter { name: "destinationParent"; type: "QModelIndex" } + Parameter { name: "destinationColumn"; type: "int" } + } + Signal { + name: "columnsMoved" + Parameter { name: "parent"; type: "QModelIndex" } + Parameter { name: "start"; type: "int" } + Parameter { name: "end"; type: "int" } + Parameter { name: "destination"; type: "QModelIndex" } + Parameter { name: "column"; type: "int" } + } + Method { name: "submit"; type: "bool" } + Method { name: "revert" } + Method { + name: "hasIndex" + type: "bool" + Parameter { name: "row"; type: "int" } + Parameter { name: "column"; type: "int" } + Parameter { name: "parent"; type: "QModelIndex" } + } + Method { + name: "hasIndex" + type: "bool" + Parameter { name: "row"; type: "int" } + Parameter { name: "column"; type: "int" } + } + Method { + name: "index" + type: "QModelIndex" + Parameter { name: "row"; type: "int" } + Parameter { name: "column"; type: "int" } + Parameter { name: "parent"; type: "QModelIndex" } + } + Method { + name: "index" + type: "QModelIndex" + Parameter { name: "row"; type: "int" } + Parameter { name: "column"; type: "int" } + } + Method { + name: "parent" + type: "QModelIndex" + Parameter { name: "child"; type: "QModelIndex" } + } + Method { + name: "sibling" + type: "QModelIndex" + Parameter { name: "row"; type: "int" } + Parameter { name: "column"; type: "int" } + Parameter { name: "idx"; type: "QModelIndex" } + } + Method { + name: "rowCount" + type: "int" + Parameter { name: "parent"; type: "QModelIndex" } + } + Method { name: "rowCount"; type: "int" } + Method { + name: "columnCount" + type: "int" + Parameter { name: "parent"; type: "QModelIndex" } + } + Method { name: "columnCount"; type: "int" } + Method { + name: "hasChildren" + type: "bool" + Parameter { name: "parent"; type: "QModelIndex" } + } + Method { name: "hasChildren"; type: "bool" } + Method { + name: "data" + type: "QVariant" + Parameter { name: "index"; type: "QModelIndex" } + Parameter { name: "role"; type: "int" } + } + Method { + name: "data" + type: "QVariant" + Parameter { name: "index"; type: "QModelIndex" } + } + Method { + name: "setData" + type: "bool" + Parameter { name: "index"; type: "QModelIndex" } + Parameter { name: "value"; type: "QVariant" } + Parameter { name: "role"; type: "int" } + } + Method { + name: "setData" + type: "bool" + Parameter { name: "index"; type: "QModelIndex" } + Parameter { name: "value"; type: "QVariant" } + } + Method { + name: "headerData" + type: "QVariant" + Parameter { name: "section"; type: "int" } + Parameter { name: "orientation"; type: "Qt::Orientation" } + Parameter { name: "role"; type: "int" } + } + Method { + name: "headerData" + type: "QVariant" + Parameter { name: "section"; type: "int" } + Parameter { name: "orientation"; type: "Qt::Orientation" } + } + Method { + name: "fetchMore" + Parameter { name: "parent"; type: "QModelIndex" } + } + Method { + name: "canFetchMore" + type: "bool" + Parameter { name: "parent"; type: "QModelIndex" } + } + Method { + name: "flags" + type: "Qt::ItemFlags" + Parameter { name: "index"; type: "QModelIndex" } + } + Method { + name: "match" + type: "QModelIndexList" + Parameter { name: "start"; type: "QModelIndex" } + Parameter { name: "role"; type: "int" } + Parameter { name: "value"; type: "QVariant" } + Parameter { name: "hits"; type: "int" } + Parameter { name: "flags"; type: "Qt::MatchFlags" } + } + Method { + name: "match" + type: "QModelIndexList" + Parameter { name: "start"; type: "QModelIndex" } + Parameter { name: "role"; type: "int" } + Parameter { name: "value"; type: "QVariant" } + Parameter { name: "hits"; type: "int" } + } + Method { + name: "match" + type: "QModelIndexList" + Parameter { name: "start"; type: "QModelIndex" } + Parameter { name: "role"; type: "int" } + Parameter { name: "value"; type: "QVariant" } + } + } + Component { name: "QAbstractListModel"; prototype: "QAbstractItemModel" } + Component { + name: "QMediaObject" + prototype: "QObject" + Property { name: "notifyInterval"; type: "int" } + Signal { + name: "notifyIntervalChanged" + Parameter { name: "milliSeconds"; type: "int" } + } + Signal { + name: "metaDataAvailableChanged" + Parameter { name: "available"; type: "bool" } + } + Signal { name: "metaDataChanged" } + Signal { + name: "metaDataChanged" + Parameter { name: "key"; type: "string" } + Parameter { name: "value"; type: "QVariant" } + } + Signal { + name: "availabilityChanged" + Parameter { name: "available"; type: "bool" } + } + Signal { + name: "availabilityChanged" + Parameter { name: "availability"; type: "QMultimedia::AvailabilityStatus" } + } + } + Component { + name: "QMediaPlayer" + prototype: "QMediaObject" + Enum { + name: "State" + values: { + "StoppedState": 0, + "PlayingState": 1, + "PausedState": 2 + } + } + Enum { + name: "MediaStatus" + values: { + "UnknownMediaStatus": 0, + "NoMedia": 1, + "LoadingMedia": 2, + "LoadedMedia": 3, + "StalledMedia": 4, + "BufferingMedia": 5, + "BufferedMedia": 6, + "EndOfMedia": 7, + "InvalidMedia": 8 + } + } + Enum { + name: "Error" + values: { + "NoError": 0, + "ResourceError": 1, + "FormatError": 2, + "NetworkError": 3, + "AccessDeniedError": 4, + "ServiceMissingError": 5, + "MediaIsPlaylist": 6 + } + } + Property { name: "media"; type: "QMediaContent" } + Property { name: "currentMedia"; type: "QMediaContent"; isReadonly: true } + Property { name: "playlist"; type: "QMediaPlaylist"; isPointer: true } + Property { name: "duration"; type: "qlonglong"; isReadonly: true } + Property { name: "position"; type: "qlonglong" } + Property { name: "volume"; type: "int" } + Property { name: "muted"; type: "bool" } + Property { name: "bufferStatus"; type: "int"; isReadonly: true } + Property { name: "audioAvailable"; type: "bool"; isReadonly: true } + Property { name: "videoAvailable"; type: "bool"; isReadonly: true } + Property { name: "seekable"; type: "bool"; isReadonly: true } + Property { name: "playbackRate"; type: "double" } + Property { name: "state"; type: "State"; isReadonly: true } + Property { name: "mediaStatus"; type: "MediaStatus"; isReadonly: true } + Property { name: "audioRole"; type: "QAudio::Role" } + Property { name: "customAudioRole"; type: "string" } + Property { name: "error"; type: "string"; isReadonly: true } + Signal { + name: "mediaChanged" + Parameter { name: "media"; type: "QMediaContent" } + } + Signal { + name: "currentMediaChanged" + Parameter { name: "media"; type: "QMediaContent" } + } + Signal { + name: "stateChanged" + Parameter { name: "newState"; type: "QMediaPlayer::State" } + } + Signal { + name: "mediaStatusChanged" + Parameter { name: "status"; type: "QMediaPlayer::MediaStatus" } + } + Signal { + name: "durationChanged" + Parameter { name: "duration"; type: "qlonglong" } + } + Signal { + name: "positionChanged" + Parameter { name: "position"; type: "qlonglong" } + } + Signal { + name: "volumeChanged" + Parameter { name: "volume"; type: "int" } + } + Signal { + name: "mutedChanged" + Parameter { name: "muted"; type: "bool" } + } + Signal { + name: "audioAvailableChanged" + Parameter { name: "available"; type: "bool" } + } + Signal { + name: "videoAvailableChanged" + Parameter { name: "videoAvailable"; type: "bool" } + } + Signal { + name: "bufferStatusChanged" + Parameter { name: "percentFilled"; type: "int" } + } + Signal { + name: "seekableChanged" + Parameter { name: "seekable"; type: "bool" } + } + Signal { + name: "playbackRateChanged" + Parameter { name: "rate"; type: "double" } + } + Signal { + name: "audioRoleChanged" + Parameter { name: "role"; type: "QAudio::Role" } + } + Signal { + name: "customAudioRoleChanged" + Parameter { name: "role"; type: "string" } + } + Signal { + name: "error" + Parameter { name: "error"; type: "QMediaPlayer::Error" } + } + Signal { + name: "networkConfigurationChanged" + Parameter { name: "configuration"; type: "QNetworkConfiguration" } + } + Method { name: "play" } + Method { name: "pause" } + Method { name: "stop" } + Method { + name: "setPosition" + Parameter { name: "position"; type: "qlonglong" } + } + Method { + name: "setVolume" + Parameter { name: "volume"; type: "int" } + } + Method { + name: "setMuted" + Parameter { name: "muted"; type: "bool" } + } + Method { + name: "setPlaybackRate" + Parameter { name: "rate"; type: "double" } + } + Method { + name: "setMedia" + Parameter { name: "media"; type: "QMediaContent" } + Parameter { name: "stream"; type: "QIODevice"; isPointer: true } + } + Method { + name: "setMedia" + Parameter { name: "media"; type: "QMediaContent" } + } + Method { + name: "setPlaylist" + Parameter { name: "playlist"; type: "QMediaPlaylist"; isPointer: true } + } + Method { + name: "setNetworkConfigurations" + Parameter { name: "configurations"; type: "QList" } + } + } +} diff --git a/core/include/JellyfinQt/jellyfin.h b/core/include/JellyfinQt/jellyfin.h index 4c8c7cd..20d7b18 100644 --- a/core/include/JellyfinQt/jellyfin.h +++ b/core/include/JellyfinQt/jellyfin.h @@ -19,35 +19,18 @@ #ifndef JELLYFIN_H #define JELLYFIN_H +#include #include -#include "model/item.h" -#include "dto/itemfields.h" -#include "dto/mediastream.h" -#include "dto/nameguidpair.h" -#include "dto/userdto.h" -#include "dto/useritemdatadto.h" - -#include "apiclient.h" -#include "apimodel.h" -#include "serverdiscoverymodel.h" -#include "websocket.h" -#include "viewmodel/item.h" -#include "viewmodel/itemmodel.h" -#include "viewmodel/loader.h" -#include "viewmodel/mediastream.h" -#include "viewmodel/modelstatus.h" -#include "viewmodel/platformmediacontrol.h" -#include "viewmodel/playbackmanager.h" -#include "viewmodel/playlist.h" -#include "viewmodel/settings.h" -#include "viewmodel/userdata.h" -#include "viewmodel/usermodel.h" -#include "viewmodel/user.h" - namespace Jellyfin { -void registerTypes(const char *uri = "nl.netsoj.chris.Jellyfin"); +class JellyfinPlugin : public QQmlExtensionPlugin { + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) +public: + void registerTypes(const char *uri) override; + +}; } diff --git a/core/qmldir b/core/qmldir index e69de29..ab8d47d 100644 --- a/core/qmldir +++ b/core/qmldir @@ -0,0 +1,2 @@ +module nl.netsoj.chris.Jellyfin +plugin JellyfinQt diff --git a/core/src/jellyfin.cpp b/core/src/jellyfin.cpp index 270cdc2..3b4478a 100644 --- a/core/src/jellyfin.cpp +++ b/core/src/jellyfin.cpp @@ -17,9 +17,34 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "JellyfinQt/jellyfin.h" + +#include "JellyfinQt/model/item.h" +#include "JellyfinQt/dto/itemfields.h" +#include "JellyfinQt/dto/mediastream.h" +#include "JellyfinQt/dto/nameguidpair.h" +#include "JellyfinQt/dto/userdto.h" +#include "JellyfinQt/dto/useritemdatadto.h" + +#include "JellyfinQt/apiclient.h" +#include "JellyfinQt/apimodel.h" +#include "JellyfinQt/serverdiscoverymodel.h" +#include "JellyfinQt/websocket.h" +#include "JellyfinQt/viewmodel/item.h" +#include "JellyfinQt/viewmodel/itemmodel.h" +#include "JellyfinQt/viewmodel/loader.h" +#include "JellyfinQt/viewmodel/mediastream.h" +#include "JellyfinQt/viewmodel/modelstatus.h" +#include "JellyfinQt/viewmodel/platformmediacontrol.h" +#include "JellyfinQt/viewmodel/playbackmanager.h" +#include "JellyfinQt/viewmodel/playlist.h" +#include "JellyfinQt/viewmodel/settings.h" +#include "JellyfinQt/viewmodel/userdata.h" +#include "JellyfinQt/viewmodel/usermodel.h" +#include "JellyfinQt/viewmodel/user.h" + namespace Jellyfin { -void registerTypes(const char *uri) { +void JellyfinPlugin::registerTypes(const char *uri) { qmlRegisterType(uri, 1, 0, "ApiClient"); qmlRegisterType(uri, 1, 0, "ServerDiscoveryModel"); qmlRegisterType(uri, 1, 0, "PlaybackManager"); diff --git a/qtquick/src/main.cpp b/qtquick/src/main.cpp index cdecb08..ca27cc7 100644 --- a/qtquick/src/main.cpp +++ b/qtquick/src/main.cpp @@ -14,6 +14,9 @@ int main(int argc, char** argv) { QGuiApplication app(argc, argv); app.setApplicationDisplayName(QStringLiteral("Sailfin QtQuick")); + app.setApplicationName("Sailfin QtQuick"); + app.setOrganizationDomain("nl.netsoj.chris"); + app.setOrganizationName("Chris Josten"); #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // Disable Qt nagging about "implicitly defined onFoo properties in connections are deprecated", @@ -25,7 +28,8 @@ int main(int argc, char** argv) { QQmlApplicationEngine engine; qDebug() << "Registering types"; - Jellyfin::registerTypes(); + Jellyfin::JellyfinPlugin plugin; + plugin.registerTypes("nl.netsoj.chris.Jellyfin"); qDebug() << "Loading file"; engine.load(QStringLiteral("qrc:/qml/main.qml")); diff --git a/sailfish/CMakeLists.txt b/sailfish/CMakeLists.txt index 839da7e..c130d7e 100644 --- a/sailfish/CMakeLists.txt +++ b/sailfish/CMakeLists.txt @@ -5,11 +5,11 @@ include(ExternalProject) # FIXME: don't hardcode /home/deploy/installroot/ set(DEPLOY_ROOT /home/deploy/installroot/) ExternalProject_Add(BlurhashQt - # PREFIX ${CMAKE_CURRENT_BINARY_DIR}/lib/blurhash-qt - GIT_REPOSITORY https://github.com/HenkKalkwater/BlurhashQt.git - GIT_TAG 61ae7f0feca6ab67da1bfdbcb222bbd12e8f7e07 + # PREFIX ${CMAKE_CURRENT_BINARY_DIR}/lib/blurhash-qt + GIT_REPOSITORY https://github.com/HenkKalkwater/BlurhashQt.git + GIT_TAG 61ae7f0feca6ab67da1bfdbcb222bbd12e8f7e07 - CMAKE_ARGS -DBUILD_SHARED_LIBS:BOOL=ON -DENABLE_EXPORT=OFF -DQML_PLUGIN_PATH=${PROJECT_BINARY_DIR}/plugins/) + CMAKE_ARGS -DBUILD_SHARED_LIBS:BOOL=ON -DENABLE_EXPORT=OFF -DQML_PLUGIN_PATH=${PROJECT_BINARY_DIR}/plugins/) find_package(Qt5 COMPONENTS Gui Qml Quick) find_package(SailfishApp 1.0 REQUIRED) @@ -26,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 @@ -58,9 +58,10 @@ set(sailfin_QML_SOURCES qml/pages/itemdetails/UnsupportedPage.qml qml/pages/itemdetails/VideoPage.qml qml/pages/settings/DebugPage.qml + qml/pages/settings/StreamingPage.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}) @@ -69,35 +70,33 @@ target_link_libraries(harbour-sailfin PRIVATE Qt5::Gui Qt5::Qml Qt5::Quick Sailf # invoker/booster to work JellyfinQt "-Wl,-rpath,${CMAKE_INSTALL_LIBDIR} -rdynamic -pie") target_compile_definitions(harbour-sailfin - PRIVATE $<$,$>:QT_QML_DEBUG>) + PRIVATE $<$,$>:QT_QML_DEBUG>) install(TARGETS harbour-sailfin RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(DIRECTORY ${PROJECT_BINARY_DIR}/plugins/ - DESTINATION share/harbour-sailfin/) + DESTINATION share/harbour-sailfin/) install(DIRECTORY qml - DESTINATION share/harbour-sailfin + DESTINATION share/harbour-sailfin ) install(DIRECTORY translations - DESTINATION share/harbour-sailfin - FILES_MATCHING PATTERN "*.qm" + DESTINATION share/harbour-sailfin + FILES_MATCHING PATTERN "*.qm" ) install(FILES harbour-sailfin.desktop - DESTINATION share/applications + DESTINATION share/applications ) install(FILES icons/86x86/harbour-sailfin.png - DESTINATION share/icons/hicolor/86x86/apps + DESTINATION share/icons/hicolor/86x86/apps ) install(FILES icons/108x108/harbour-sailfin.png - DESTINATION share/icons/hicolor/108x108/apps -) + DESTINATION share/icons/hicolor/108x108/apps ) install(FILES icons/128x128/harbour-sailfin.png - DESTINATION share/icons/hicolor/128x128/apps + DESTINATION share/icons/hicolor/128x128/apps ) install(FILES icons/172x172/harbour-sailfin.png - DESTINATION share/icons/hicolor/172x172/apps -) + DESTINATION share/icons/hicolor/172x172/apps ) # Tell Qt Creator where the application executable(s) would be located on the # device. @@ -111,6 +110,6 @@ install(FILES icons/172x172/harbour-sailfin.png # Search the Qt Creator Manual to learn about the QtCreatorDeployment.txt file # format. file(WRITE "${CMAKE_BINARY_DIR}/QtCreatorDeployment.txt" - "${CMAKE_INSTALL_PREFIX} - ${CMAKE_BINARY_DIR}/sailfish/harbour-sailfin:bin + "${CMAKE_INSTALL_PREFIX} + ${CMAKE_BINARY_DIR}/sailfish/harbour-sailfin:bin ") diff --git a/sailfish/src/harbour-sailfin.cpp b/sailfish/src/harbour-sailfin.cpp index 6b13d8c..2ee1ff2 100644 --- a/sailfish/src/harbour-sailfin.cpp +++ b/sailfish/src/harbour-sailfin.cpp @@ -33,10 +33,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #include -#include - -static const char *SANDBOX_PROGRAM = "/usr/bin/sailjail"; - int main(int argc, char *argv[]) { //QQmlDebuggingEnabler enabler; //enabler.startTcpDebugServer(9999); @@ -60,7 +56,6 @@ int main(int argc, char *argv[]) { cmdParser.addHelpOption(); cmdParser.addVersionOption(); - Jellyfin::registerTypes(); QQuickView *view = SailfishApp::createView(); view->setSource(SailfishApp::pathToMainQml()); view->show();