mirror of
https://github.com/HenkKalkwater/harbour-sailfin.git
synced 2025-09-04 01:42:44 +00:00
WIP: Add playlists/queues and add support for Sailfish back
This commit is contained in:
parent
fbc154fb56
commit
86672be051
89 changed files with 1637 additions and 849 deletions
|
@ -231,6 +231,9 @@ public slots:
|
|||
protected slots:
|
||||
void defaultNetworkErrorHandler(QNetworkReply::NetworkError error);
|
||||
void onUserDataChanged(const QString &itemId, UserData *newData);
|
||||
void credManagerServersListed(QStringList users);
|
||||
void credManagerUsersListed(const QString &server, QStringList users);
|
||||
void credManagerTokenRetrieved(const QString &server, const QString &user, const QString &token);
|
||||
|
||||
protected:
|
||||
/**
|
||||
|
|
|
@ -108,8 +108,7 @@ signals:
|
|||
*/
|
||||
void itemsLoaded();
|
||||
void reloadWanted();
|
||||
|
||||
protected slots:
|
||||
public slots:
|
||||
virtual void futureReady() = 0;
|
||||
|
||||
protected:
|
||||
|
@ -243,6 +242,18 @@ bool setRequestStartIndex(P ¶meters, int startIndex) {
|
|||
return false;
|
||||
}
|
||||
|
||||
#ifndef JELLYFIN_APIMODEL_CPP
|
||||
extern template bool setRequestStartIndex(Loader::GetUserViewsParams ¶ms, int startIndex);
|
||||
extern template void setRequestLimit(Loader::GetUserViewsParams ¶ms, int limit);
|
||||
extern template QList<DTO::BaseItemDto> extractRecords(const DTO::BaseItemDtoQueryResult &result);
|
||||
extern template int extractTotalRecordCount(const DTO::BaseItemDtoQueryResult &result);
|
||||
extern template QList<DTO::BaseItemDto> extractRecords(const QList<DTO::BaseItemDto> &result);
|
||||
extern template int extractTotalRecordCount(const QList<DTO::BaseItemDto> &result);
|
||||
extern template void setRequestLimit(Loader::GetLatestMediaParams ¶ms, int limit);
|
||||
extern template bool setRequestStartIndex(Loader::GetLatestMediaParams ¶ms, int offset);
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Template for implementing a loader for the given type, response and parameters using Jellyfin::Support:Loaders.
|
||||
*
|
||||
|
@ -301,7 +312,7 @@ protected:
|
|||
return;
|
||||
}
|
||||
result = optResult.value();
|
||||
} catch (Support::LoadException e) {
|
||||
} catch (Support::LoadException &e) {
|
||||
qWarning() << "Exception while loading: " << e.what();
|
||||
this->setStatus(ViewModel::ModelStatus::Error);
|
||||
return;
|
||||
|
@ -430,11 +441,11 @@ public:
|
|||
}
|
||||
|
||||
// QList-like API
|
||||
const T& at(int index) { return m_array.at(index); }
|
||||
const T& at(int index) const { return m_array.at(index); }
|
||||
/**
|
||||
* @return the amount of objects in this model.
|
||||
*/
|
||||
int size() {
|
||||
int size() const {
|
||||
return m_array.size();
|
||||
}
|
||||
|
||||
|
|
|
@ -101,7 +101,7 @@ public:
|
|||
signals:
|
||||
void tokenRetrieved(const QString &server, const QString &user, const QString &token) const;
|
||||
void serversListed(const QStringList &servers) const;
|
||||
void usersListed(const QStringList &users) const;
|
||||
void usersListed(const QString& server, const QStringList &users) const;
|
||||
|
||||
protected:
|
||||
explicit CredentialsManager(QObject *parent = nullptr) : QObject (parent) {}
|
||||
|
|
|
@ -68,7 +68,7 @@ public:
|
|||
|
||||
|
||||
protected:
|
||||
QSharedPointer<LibraryOptions> m_libraryOptions = nullptr;
|
||||
QSharedPointer<LibraryOptions> m_libraryOptions = QSharedPointer<LibraryOptions>();
|
||||
};
|
||||
|
||||
} // NS DTO
|
||||
|
|
|
@ -94,7 +94,7 @@ public:
|
|||
|
||||
|
||||
protected:
|
||||
QSharedPointer<AlbumInfo> m_searchInfo = nullptr;
|
||||
QSharedPointer<AlbumInfo> m_searchInfo = QSharedPointer<AlbumInfo>();
|
||||
QString m_itemId;
|
||||
QString m_searchProviderName;
|
||||
bool m_includeDisabledProviders;
|
||||
|
|
|
@ -78,9 +78,9 @@ public:
|
|||
|
||||
|
||||
protected:
|
||||
QSharedPointer<ThemeMediaResult> m_themeVideosResult = nullptr;
|
||||
QSharedPointer<ThemeMediaResult> m_themeSongsResult = nullptr;
|
||||
QSharedPointer<ThemeMediaResult> m_soundtrackSongsResult = nullptr;
|
||||
QSharedPointer<ThemeMediaResult> m_themeVideosResult = QSharedPointer<ThemeMediaResult>();
|
||||
QSharedPointer<ThemeMediaResult> m_themeSongsResult = QSharedPointer<ThemeMediaResult>();
|
||||
QSharedPointer<ThemeMediaResult> m_soundtrackSongsResult = QSharedPointer<ThemeMediaResult>();
|
||||
};
|
||||
|
||||
} // NS DTO
|
||||
|
|
|
@ -94,7 +94,7 @@ public:
|
|||
|
||||
|
||||
protected:
|
||||
QSharedPointer<ArtistInfo> m_searchInfo = nullptr;
|
||||
QSharedPointer<ArtistInfo> m_searchInfo = QSharedPointer<ArtistInfo>();
|
||||
QString m_itemId;
|
||||
QString m_searchProviderName;
|
||||
bool m_includeDisabledProviders;
|
||||
|
|
|
@ -89,8 +89,8 @@ public:
|
|||
|
||||
|
||||
protected:
|
||||
QSharedPointer<UserDto> m_user = nullptr;
|
||||
QSharedPointer<SessionInfo> m_sessionInfo = nullptr;
|
||||
QSharedPointer<UserDto> m_user = QSharedPointer<UserDto>();
|
||||
QSharedPointer<SessionInfo> m_sessionInfo = QSharedPointer<SessionInfo>();
|
||||
QString m_accessToken;
|
||||
QString m_serverId;
|
||||
};
|
||||
|
|
|
@ -1611,7 +1611,7 @@ protected:
|
|||
QString m_parentBackdropItemId;
|
||||
QStringList m_parentBackdropImageTags;
|
||||
std::optional<qint32> m_localTrailerCount = std::nullopt;
|
||||
QSharedPointer<UserItemDataDto> m_userData = nullptr;
|
||||
QSharedPointer<UserItemDataDto> m_userData = QSharedPointer<UserItemDataDto>();
|
||||
std::optional<qint32> m_recursiveItemCount = std::nullopt;
|
||||
std::optional<qint32> m_childCount = std::nullopt;
|
||||
QString m_seriesName;
|
||||
|
@ -1699,7 +1699,7 @@ protected:
|
|||
std::optional<bool> m_isKids = std::nullopt;
|
||||
std::optional<bool> m_isPremiere = std::nullopt;
|
||||
QString m_timerId;
|
||||
QSharedPointer<BaseItemDto> m_currentProgram = nullptr;
|
||||
QSharedPointer<BaseItemDto> m_currentProgram = QSharedPointer<BaseItemDto>();
|
||||
};
|
||||
|
||||
} // NS DTO
|
||||
|
|
|
@ -94,7 +94,7 @@ public:
|
|||
|
||||
|
||||
protected:
|
||||
QSharedPointer<BookInfo> m_searchInfo = nullptr;
|
||||
QSharedPointer<BookInfo> m_searchInfo = QSharedPointer<BookInfo>();
|
||||
QString m_itemId;
|
||||
QString m_searchProviderName;
|
||||
bool m_includeDisabledProviders;
|
||||
|
|
|
@ -94,7 +94,7 @@ public:
|
|||
|
||||
|
||||
protected:
|
||||
QSharedPointer<BoxSetInfo> m_searchInfo = nullptr;
|
||||
QSharedPointer<BoxSetInfo> m_searchInfo = QSharedPointer<BoxSetInfo>();
|
||||
QString m_itemId;
|
||||
QString m_searchProviderName;
|
||||
bool m_includeDisabledProviders;
|
||||
|
|
|
@ -134,7 +134,7 @@ protected:
|
|||
QString m_messageCallbackUrl;
|
||||
bool m_supportsPersistentIdentifier;
|
||||
bool m_supportsSync;
|
||||
QSharedPointer<DeviceProfile> m_deviceProfile = nullptr;
|
||||
QSharedPointer<DeviceProfile> m_deviceProfile = QSharedPointer<DeviceProfile>();
|
||||
QString m_appStoreUrl;
|
||||
QString m_iconUrl;
|
||||
};
|
||||
|
|
|
@ -170,7 +170,7 @@ protected:
|
|||
QString m_messageCallbackUrl;
|
||||
bool m_supportsPersistentIdentifier;
|
||||
bool m_supportsSync;
|
||||
QSharedPointer<DeviceProfile> m_deviceProfile = nullptr;
|
||||
QSharedPointer<DeviceProfile> m_deviceProfile = QSharedPointer<DeviceProfile>();
|
||||
QString m_appStoreUrl;
|
||||
QString m_iconUrl;
|
||||
};
|
||||
|
|
|
@ -153,7 +153,7 @@ protected:
|
|||
QString m_appVersion;
|
||||
QString m_lastUserId;
|
||||
QDateTime m_dateLastActivity;
|
||||
QSharedPointer<ClientCapabilities> m_capabilities = nullptr;
|
||||
QSharedPointer<ClientCapabilities> m_capabilities = QSharedPointer<ClientCapabilities>();
|
||||
QString m_iconUrl;
|
||||
};
|
||||
|
||||
|
|
|
@ -478,7 +478,7 @@ public:
|
|||
protected:
|
||||
QString m_name;
|
||||
QString m_jellyfinId;
|
||||
QSharedPointer<DeviceIdentification> m_identification = nullptr;
|
||||
QSharedPointer<DeviceIdentification> m_identification = QSharedPointer<DeviceIdentification>();
|
||||
QString m_friendlyName;
|
||||
QString m_manufacturer;
|
||||
QString m_manufacturerUrl;
|
||||
|
|
|
@ -124,7 +124,7 @@ public:
|
|||
protected:
|
||||
QString m_guid;
|
||||
QString m_name;
|
||||
QSharedPointer<Version> m_version = nullptr;
|
||||
QSharedPointer<Version> m_version = QSharedPointer<Version>();
|
||||
QString m_changelog;
|
||||
QString m_sourceUrl;
|
||||
QString m_checksum;
|
||||
|
|
|
@ -134,7 +134,7 @@ protected:
|
|||
QString m_name;
|
||||
QString m_description;
|
||||
QString m_jellyfinId;
|
||||
QSharedPointer<Version> m_version = nullptr;
|
||||
QSharedPointer<Version> m_version = QSharedPointer<Version>();
|
||||
QString m_assemblyFilePath;
|
||||
bool m_canUninstall;
|
||||
QString m_dataFolderPath;
|
||||
|
|
|
@ -68,7 +68,7 @@ public:
|
|||
|
||||
|
||||
protected:
|
||||
QSharedPointer<MediaSourceInfo> m_mediaSource = nullptr;
|
||||
QSharedPointer<MediaSourceInfo> m_mediaSource = QSharedPointer<MediaSourceInfo>();
|
||||
};
|
||||
|
||||
} // NS DTO
|
||||
|
|
|
@ -91,7 +91,7 @@ public:
|
|||
protected:
|
||||
QString m_name;
|
||||
QString m_path;
|
||||
QSharedPointer<MediaPathInfo> m_pathInfo = nullptr;
|
||||
QSharedPointer<MediaPathInfo> m_pathInfo = QSharedPointer<MediaPathInfo>();
|
||||
};
|
||||
|
||||
} // NS DTO
|
||||
|
|
|
@ -94,7 +94,7 @@ public:
|
|||
|
||||
|
||||
protected:
|
||||
QSharedPointer<MovieInfo> m_searchInfo = nullptr;
|
||||
QSharedPointer<MovieInfo> m_searchInfo = QSharedPointer<MovieInfo>();
|
||||
QString m_itemId;
|
||||
QString m_searchProviderName;
|
||||
bool m_includeDisabledProviders;
|
||||
|
|
|
@ -94,7 +94,7 @@ public:
|
|||
|
||||
|
||||
protected:
|
||||
QSharedPointer<MusicVideoInfo> m_searchInfo = nullptr;
|
||||
QSharedPointer<MusicVideoInfo> m_searchInfo = QSharedPointer<MusicVideoInfo>();
|
||||
QString m_itemId;
|
||||
QString m_searchProviderName;
|
||||
bool m_includeDisabledProviders;
|
||||
|
|
|
@ -215,7 +215,7 @@ protected:
|
|||
QString m_itemId;
|
||||
std::optional<bool> m_enableDirectPlay = std::nullopt;
|
||||
std::optional<bool> m_enableDirectStream = std::nullopt;
|
||||
QSharedPointer<DeviceProfile> m_deviceProfile = nullptr;
|
||||
QSharedPointer<DeviceProfile> m_deviceProfile = QSharedPointer<DeviceProfile>();
|
||||
QList<MediaProtocol> m_directPlayProtocols;
|
||||
};
|
||||
|
||||
|
|
|
@ -94,7 +94,7 @@ public:
|
|||
|
||||
|
||||
protected:
|
||||
QSharedPointer<PersonLookupInfo> m_searchInfo = nullptr;
|
||||
QSharedPointer<PersonLookupInfo> m_searchInfo = QSharedPointer<PersonLookupInfo>();
|
||||
QString m_itemId;
|
||||
QString m_searchProviderName;
|
||||
bool m_includeDisabledProviders;
|
||||
|
|
|
@ -231,7 +231,7 @@ protected:
|
|||
std::optional<qint32> m_maxAudioChannels = std::nullopt;
|
||||
QString m_mediaSourceId;
|
||||
QString m_liveStreamId;
|
||||
QSharedPointer<DeviceProfile> m_deviceProfile = nullptr;
|
||||
QSharedPointer<DeviceProfile> m_deviceProfile = QSharedPointer<DeviceProfile>();
|
||||
std::optional<bool> m_enableDirectPlay = std::nullopt;
|
||||
std::optional<bool> m_enableDirectStream = std::nullopt;
|
||||
std::optional<bool> m_enableTranscoding = std::nullopt;
|
||||
|
|
|
@ -244,7 +244,7 @@ public:
|
|||
|
||||
protected:
|
||||
bool m_canSeek;
|
||||
QSharedPointer<BaseItemDto> m_item = nullptr;
|
||||
QSharedPointer<BaseItemDto> m_item = QSharedPointer<BaseItemDto>();
|
||||
QString m_itemId;
|
||||
QString m_sessionId;
|
||||
QString m_mediaSourceId;
|
||||
|
|
|
@ -244,7 +244,7 @@ public:
|
|||
|
||||
protected:
|
||||
bool m_canSeek;
|
||||
QSharedPointer<BaseItemDto> m_item = nullptr;
|
||||
QSharedPointer<BaseItemDto> m_item = QSharedPointer<BaseItemDto>();
|
||||
QString m_itemId;
|
||||
QString m_sessionId;
|
||||
QString m_mediaSourceId;
|
||||
|
|
|
@ -166,7 +166,7 @@ public:
|
|||
|
||||
|
||||
protected:
|
||||
QSharedPointer<BaseItemDto> m_item = nullptr;
|
||||
QSharedPointer<BaseItemDto> m_item = QSharedPointer<BaseItemDto>();
|
||||
QString m_itemId;
|
||||
QString m_sessionId;
|
||||
QString m_mediaSourceId;
|
||||
|
|
|
@ -136,7 +136,7 @@ public:
|
|||
|
||||
protected:
|
||||
QString m_name;
|
||||
QSharedPointer<Version> m_version = nullptr;
|
||||
QSharedPointer<Version> m_version = QSharedPointer<Version>();
|
||||
QString m_configurationFileName;
|
||||
QString m_description;
|
||||
QString m_jellyfinId;
|
||||
|
|
|
@ -170,7 +170,7 @@ protected:
|
|||
QString m_imageUrl;
|
||||
QString m_searchProviderName;
|
||||
QString m_overview;
|
||||
QSharedPointer<RemoteSearchResult> m_albumArtist = nullptr;
|
||||
QSharedPointer<RemoteSearchResult> m_albumArtist = QSharedPointer<RemoteSearchResult>();
|
||||
QList<RemoteSearchResult> m_artists;
|
||||
};
|
||||
|
||||
|
|
|
@ -94,7 +94,7 @@ public:
|
|||
|
||||
|
||||
protected:
|
||||
QSharedPointer<SeriesInfo> m_searchInfo = nullptr;
|
||||
QSharedPointer<SeriesInfo> m_searchInfo = QSharedPointer<SeriesInfo>();
|
||||
QString m_itemId;
|
||||
QString m_searchProviderName;
|
||||
bool m_includeDisabledProviders;
|
||||
|
|
|
@ -819,7 +819,7 @@ protected:
|
|||
qint32 m_logFileRetentionDays;
|
||||
bool m_isStartupWizardCompleted;
|
||||
QString m_cachePath;
|
||||
QSharedPointer<Version> m_previousVersion = nullptr;
|
||||
QSharedPointer<Version> m_previousVersion = QSharedPointer<Version>();
|
||||
QString m_previousVersionStr;
|
||||
bool m_enableUPnP;
|
||||
bool m_enableMetrics;
|
||||
|
|
|
@ -300,9 +300,9 @@ public:
|
|||
|
||||
|
||||
protected:
|
||||
QSharedPointer<PlayerStateInfo> m_playState = nullptr;
|
||||
QSharedPointer<PlayerStateInfo> m_playState = QSharedPointer<PlayerStateInfo>();
|
||||
QList<SessionUserInfo> m_additionalUsers;
|
||||
QSharedPointer<ClientCapabilities> m_capabilities = nullptr;
|
||||
QSharedPointer<ClientCapabilities> m_capabilities = QSharedPointer<ClientCapabilities>();
|
||||
QString m_remoteEndPoint;
|
||||
QStringList m_playableMediaTypes;
|
||||
QString m_jellyfinId;
|
||||
|
@ -313,12 +313,12 @@ protected:
|
|||
QDateTime m_lastPlaybackCheckIn;
|
||||
QString m_deviceName;
|
||||
QString m_deviceType;
|
||||
QSharedPointer<BaseItemDto> m_nowPlayingItem = nullptr;
|
||||
QSharedPointer<BaseItem> m_fullNowPlayingItem = nullptr;
|
||||
QSharedPointer<BaseItemDto> m_nowViewingItem = nullptr;
|
||||
QSharedPointer<BaseItemDto> m_nowPlayingItem = QSharedPointer<BaseItemDto>();
|
||||
QSharedPointer<BaseItem> m_fullNowPlayingItem = QSharedPointer<BaseItem>();
|
||||
QSharedPointer<BaseItemDto> m_nowViewingItem = QSharedPointer<BaseItemDto>();
|
||||
QString m_deviceId;
|
||||
QString m_applicationVersion;
|
||||
QSharedPointer<TranscodingInfo> m_transcodingInfo = nullptr;
|
||||
QSharedPointer<TranscodingInfo> m_transcodingInfo = QSharedPointer<TranscodingInfo>();
|
||||
bool m_isActive;
|
||||
bool m_supportsMediaControl;
|
||||
bool m_supportsRemoteControl;
|
||||
|
|
|
@ -168,7 +168,7 @@ protected:
|
|||
TaskState m_state;
|
||||
std::optional<double> m_currentProgressPercentage = std::nullopt;
|
||||
QString m_jellyfinId;
|
||||
QSharedPointer<TaskResult> m_lastExecutionResult = nullptr;
|
||||
QSharedPointer<TaskResult> m_lastExecutionResult = QSharedPointer<TaskResult>();
|
||||
QList<TaskTriggerInfo> m_triggers;
|
||||
QString m_description;
|
||||
QString m_category;
|
||||
|
|
|
@ -362,7 +362,7 @@ protected:
|
|||
QString m_seriesTimerId;
|
||||
QString m_externalSeriesTimerId;
|
||||
std::optional<qint64> m_runTimeTicks = std::nullopt;
|
||||
QSharedPointer<BaseItemDto> m_programInfo = nullptr;
|
||||
QSharedPointer<BaseItemDto> m_programInfo = QSharedPointer<BaseItemDto>();
|
||||
};
|
||||
|
||||
} // NS DTO
|
||||
|
|
|
@ -94,7 +94,7 @@ public:
|
|||
|
||||
|
||||
protected:
|
||||
QSharedPointer<TrailerInfo> m_searchInfo = nullptr;
|
||||
QSharedPointer<TrailerInfo> m_searchInfo = QSharedPointer<TrailerInfo>();
|
||||
QString m_itemId;
|
||||
QString m_searchProviderName;
|
||||
bool m_includeDisabledProviders;
|
||||
|
|
|
@ -79,7 +79,7 @@ public:
|
|||
|
||||
protected:
|
||||
QString m_jellyfinId;
|
||||
QSharedPointer<LibraryOptions> m_libraryOptions = nullptr;
|
||||
QSharedPointer<LibraryOptions> m_libraryOptions = QSharedPointer<LibraryOptions>();
|
||||
};
|
||||
|
||||
} // NS DTO
|
||||
|
|
|
@ -213,8 +213,8 @@ protected:
|
|||
std::optional<bool> m_enableAutoLogin = std::nullopt;
|
||||
QDateTime m_lastLoginDate;
|
||||
QDateTime m_lastActivityDate;
|
||||
QSharedPointer<UserConfiguration> m_configuration = nullptr;
|
||||
QSharedPointer<UserPolicy> m_policy = nullptr;
|
||||
QSharedPointer<UserConfiguration> m_configuration = QSharedPointer<UserConfiguration>();
|
||||
QSharedPointer<UserPolicy> m_policy = QSharedPointer<UserPolicy>();
|
||||
std::optional<double> m_primaryImageAspectRatio = std::nullopt;
|
||||
};
|
||||
|
||||
|
|
|
@ -158,7 +158,7 @@ public:
|
|||
|
||||
protected:
|
||||
QString m_version;
|
||||
QSharedPointer<Version> m_versionNumber = nullptr;
|
||||
QSharedPointer<Version> m_versionNumber = QSharedPointer<Version>();
|
||||
QString m_changelog;
|
||||
QString m_targetAbi;
|
||||
QString m_sourceUrl;
|
||||
|
|
|
@ -143,7 +143,7 @@ protected:
|
|||
QString m_name;
|
||||
QStringList m_locations;
|
||||
QString m_collectionType;
|
||||
QSharedPointer<LibraryOptions> m_libraryOptions = nullptr;
|
||||
QSharedPointer<LibraryOptions> m_libraryOptions = QSharedPointer<LibraryOptions>();
|
||||
QString m_itemId;
|
||||
QString m_primaryImageItemId;
|
||||
std::optional<double> m_refreshProgress = std::nullopt;
|
||||
|
|
|
@ -1,39 +1,110 @@
|
|||
/*
|
||||
* Sailfin: a Jellyfin client written using Qt
|
||||
* Copyright (C) 2021 Chris Josten and the Sailfin Contributors.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef JELLYFIN_MODEL_PLAYLIST_H
|
||||
#define JELLYFIN_MODEL_PLAYLIST_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QSharedPointer>
|
||||
#include <QString>
|
||||
#include <QUrl>
|
||||
#include <QVector>
|
||||
|
||||
#include "../viewmodel/itemmodel.h"
|
||||
#include "item.h"
|
||||
|
||||
namespace Jellyfin {
|
||||
namespace Model {
|
||||
|
||||
// Forward declaration
|
||||
class Item;
|
||||
class Shuffle;
|
||||
|
||||
class Playlist {
|
||||
/**
|
||||
* @brief Model of a playlist, a list of items that can be played.
|
||||
*
|
||||
* This tries to take the managing what items to play away from the PlaybackManager,
|
||||
* which now only will be informed about the current and next item to play.
|
||||
*
|
||||
* The playlist has actually two list, one named list and the other named queue. When
|
||||
* playing, the queue has priority over the list and will not be affected by the
|
||||
* shuffle mode. After all items of the queue are played, the items in the list are played.
|
||||
* Items in the list may be shuffled.
|
||||
*/
|
||||
class Playlist : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit Playlist();
|
||||
explicit Playlist(QObject *parent = nullptr);
|
||||
|
||||
/// Start loading data for the next item.
|
||||
void preloadNext();
|
||||
/// Returns the current item in the queue
|
||||
QSharedPointer<Item> currentItem();
|
||||
QSharedPointer<Item> nextItem();
|
||||
|
||||
/**
|
||||
* @brief Determine the previous item to be played.
|
||||
*/
|
||||
void previous();
|
||||
|
||||
/**
|
||||
* @brief Determine the next item to be played.
|
||||
*/
|
||||
void next();
|
||||
|
||||
// int queueSize() { return m_queue.size(); };
|
||||
int listSize() const { return m_list.size(); };
|
||||
int totalSize() const { return m_queue.size() + m_list.size(); }
|
||||
|
||||
QSharedPointer<const Item> listAt(int index) const;
|
||||
/**
|
||||
* @brief Removes all the items from the playlist
|
||||
*/
|
||||
void clearList();
|
||||
|
||||
/**
|
||||
* @brief Appends all items from the given itemModel to this list
|
||||
*/
|
||||
void appendToList(const ViewModel::ItemModel &model);
|
||||
|
||||
/**
|
||||
* @brief Start playing this playlist
|
||||
* @param index The index to start from.
|
||||
*/
|
||||
void play(int index = 0);
|
||||
signals:
|
||||
void listCleared();
|
||||
void itemsAddedToQueue(int index, int count);
|
||||
void itemsAddedToList(int index, int count);
|
||||
void listReshuffled();
|
||||
private:
|
||||
/// Extra data about each itemId that this playlist manages
|
||||
struct ExtendedItem {
|
||||
QSharedPointer<Item> item;
|
||||
/// The url from which this item can be streamed.
|
||||
QUrl url;
|
||||
/// Playsession that should be reported to Jellyfin's server.
|
||||
QString playSession;
|
||||
/// Text to be shown when an error occurred while fetching playback information.
|
||||
QString errorText;
|
||||
};
|
||||
void reshuffle();
|
||||
|
||||
QVector<ExtendedItem> list;
|
||||
QSharedPointer<Item> m_currentItem;
|
||||
bool m_currentItemFromQueue = false;
|
||||
QSharedPointer<Item> m_nextItem;
|
||||
bool m_nextItemFromQueue = false;
|
||||
|
||||
/// list of the items in the queue
|
||||
QVector<QSharedPointer<Item>> m_queue;
|
||||
/// list of the items in the playlist
|
||||
QVector<QSharedPointer<Item>> m_list;
|
||||
/// The current position in the playlist
|
||||
int m_pos = 0;
|
||||
|
||||
/// Algorithm for shuffling the playlist.
|
||||
Shuffle *m_shuffler;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
166
core/include/JellyfinQt/model/shuffle.h
Normal file
166
core/include/JellyfinQt/model/shuffle.h
Normal file
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* Sailfin: a Jellyfin client written using Qt
|
||||
* Copyright (C) 2021 Chris Josten and the Sailfin Contributors.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef JELLYFIN_MODEL_SHUFFLE_H
|
||||
#define JELLYFIN_MODEL_SHUFFLE_H
|
||||
|
||||
#include "playlist.h"
|
||||
#include "../model/item.h"
|
||||
|
||||
|
||||
namespace Jellyfin {
|
||||
namespace Model {
|
||||
|
||||
/**
|
||||
* @brief Interface for an algorithm shuffling a playlist.
|
||||
*/
|
||||
class Shuffle {
|
||||
public:
|
||||
Shuffle(const Playlist *parent) : m_playlist(parent) {}
|
||||
|
||||
/**
|
||||
* @brief If this Shuffle implementation shuffles the entire list in advance.
|
||||
* @return True if this shuffle class shuffles the entire list in advance
|
||||
*
|
||||
* Some shuffle implementations may only shuffle the next item as they go.
|
||||
*/
|
||||
virtual bool canShuffleInAdvance() { return true; }
|
||||
|
||||
/**
|
||||
* @brief Shuffle the list in advance. Should only be called if canShuffleInAdvance()
|
||||
* is called.
|
||||
*/
|
||||
virtual void shuffleInAdvance() {}
|
||||
|
||||
/**
|
||||
* @brief The shuffle should determine the next item.
|
||||
*/
|
||||
virtual void next() {};
|
||||
|
||||
/**
|
||||
* @brief The shuffle should determine the previous item.
|
||||
*/
|
||||
virtual void previous() {};
|
||||
|
||||
/**
|
||||
* @brief Set the index of the now playing item.
|
||||
* @param i
|
||||
*/
|
||||
virtual void setIndex(int i) {};
|
||||
|
||||
/**
|
||||
* @return the index of the currently playing item
|
||||
*/
|
||||
virtual int currentItem() const { return -1; }
|
||||
|
||||
/**
|
||||
* @brief Determine the item index at at the shuffled index
|
||||
* @param index The shuffled index
|
||||
* @return The actual index
|
||||
*
|
||||
* If canShuffleInAdvance() returns false, a new implemention is not needed
|
||||
* or -1 may be returned. This function should not even be called in that case.
|
||||
*/
|
||||
virtual int itemAt(int index) const { return -1; }
|
||||
|
||||
/**
|
||||
* @return the index of the next item
|
||||
*/
|
||||
virtual int nextItem() const { return -1; }
|
||||
|
||||
/**
|
||||
* @brief Sets whether the shuffler to loop over the list if all items are played.
|
||||
*/
|
||||
void setRepeatAll(bool repeatAll) { m_repeatAll = repeatAll; }
|
||||
protected:
|
||||
/// Playlist that can be used to gather information about the songs if needed for the algorithm
|
||||
const Playlist *m_playlist;
|
||||
bool m_repeatAll = false;
|
||||
static int random(int max, int min = 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A shuffler that does not shuffle.
|
||||
*/
|
||||
class NoShuffle : public Shuffle {
|
||||
public:
|
||||
NoShuffle(const Playlist *parent);
|
||||
|
||||
virtual int currentItem() const override;
|
||||
virtual int nextItem() const override;
|
||||
|
||||
virtual void previous() override;
|
||||
virtual void next() override;
|
||||
virtual void setIndex(int i) override;
|
||||
protected:
|
||||
int nextIndex() const;
|
||||
int previousIndex() const;
|
||||
int m_index = 0;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Base class for shuffles that shuffle the entire list in advance
|
||||
*/
|
||||
class ListShuffleBase : public NoShuffle {
|
||||
public:
|
||||
ListShuffleBase(const Playlist *parent);
|
||||
virtual int currentItem() const override;
|
||||
virtual int nextItem() const override;
|
||||
protected:
|
||||
QVector<int> m_map;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A simple shuffler which shuffles each item in the list in advance
|
||||
*/
|
||||
class SimpleListShuffle : public ListShuffleBase {
|
||||
public:
|
||||
SimpleListShuffle(const Playlist *parent);
|
||||
virtual void shuffleInAdvance() override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A shuffler that is pretty random. Does not care about repeating items in a list.
|
||||
*/
|
||||
class RandomShuffle : public Shuffle {
|
||||
public:
|
||||
RandomShuffle(const Playlist *parent);
|
||||
bool canShuffleInAdvance() override;
|
||||
virtual int currentItem() const override;
|
||||
virtual int nextItem() const override;
|
||||
virtual void previous() override;
|
||||
virtual void next() override;
|
||||
protected:
|
||||
int m_previous, m_current, m_next = -1;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A smart shuffler that shuffles a list with a few constraints to make it appear "more random" to an user.
|
||||
*
|
||||
* This shuffler tries to place to avoid placing tracks of the same album, artist, and genre next to each other.
|
||||
* This way, the user may perceive the list as more random
|
||||
*/
|
||||
class VariedListShuffle {
|
||||
|
||||
};
|
||||
|
||||
} // NS Model
|
||||
} // NS Jellyfin
|
||||
|
||||
#endif // SHUFFLE_H
|
|
@ -19,159 +19,31 @@
|
|||
#ifndef JELLYFIN_SUPPORT_JSONCONV_H
|
||||
#define JELLYFIN_SUPPORT_JSONCONV_H
|
||||
|
||||
#include <QException>
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QDateTime>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonValue>
|
||||
#include <QList>
|
||||
#include <QUuid>
|
||||
#include "jsonconvimpl.h"
|
||||
#include "parseexception.h"
|
||||
|
||||
namespace Jellyfin {
|
||||
namespace Support {
|
||||
|
||||
// Helper functions
|
||||
QString uuidToString(const QUuid &source);
|
||||
QUuid stringToUuid(const QString &source);
|
||||
|
||||
/**
|
||||
* @brief Thrown when JSON cannot be parsed.
|
||||
*/
|
||||
class ParseException : public QException {
|
||||
public:
|
||||
explicit ParseException(const QString &message)
|
||||
: m_message(message.toStdString()) {}
|
||||
|
||||
/*explicit ParseException(const ParseException &other)
|
||||
: m_message(other.m_message) {}*/
|
||||
|
||||
virtual const char *what() const noexcept override;
|
||||
|
||||
virtual QException *clone() const override;
|
||||
virtual void raise() const override;
|
||||
private:
|
||||
std::string m_message;
|
||||
};
|
||||
|
||||
// https://www.fluentcpp.com/2017/08/15/function-templates-partial-specialization-cpp/
|
||||
template <typename T>
|
||||
struct convertType{};
|
||||
|
||||
/**
|
||||
* Template for converting types from JSON into their respective type.
|
||||
*/
|
||||
template <typename T>
|
||||
T fromJsonValue(const QJsonValue &source, convertType<T>) {
|
||||
Q_UNUSED(source)
|
||||
Q_ASSERT_X(false, "fromJsonValue<T>", "fromJsonValue called with unimplemented type");
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
QJsonValue toJsonValue(const T &source, convertType<T>) {
|
||||
Q_UNUSED(source)
|
||||
std::string msg = "toJsonValue called with unimplemented type ";
|
||||
msg += typeid (T).name();
|
||||
Q_ASSERT_X(false, "toJsonValue<T>", msg.c_str());
|
||||
return QJsonValue();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T fromJsonValue(const QJsonValue &source) {
|
||||
return fromJsonValue(source, convertType<T>{});
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
QJsonValue toJsonValue(const T &source) {
|
||||
return toJsonValue(source, convertType<T>{});
|
||||
}
|
||||
|
||||
// QList
|
||||
template <typename T>
|
||||
QList<T> fromJsonValue(const QJsonValue &source, convertType<QList<T>>) {
|
||||
QList<T> result;
|
||||
QJsonArray arr = source.toArray();
|
||||
result.reserve(arr.size());
|
||||
for (auto it = arr.cbegin(); it != arr.cend(); it++) {
|
||||
result.append(fromJsonValue<T>(*it));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
QJsonValue toJsonValue(const QList<T> &source, convertType<QList<T>>) {
|
||||
QJsonArray result;
|
||||
for (auto it = source.cbegin(); it != source.cend(); it++) {
|
||||
result.push_back(toJsonValue<T>(*it));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Optional
|
||||
|
||||
template <typename T>
|
||||
std::optional<T> fromJsonValue(const QJsonValue &source, convertType<std::optional<T>>) {
|
||||
if (source.isNull()) {
|
||||
return std::nullopt;
|
||||
} else {
|
||||
return fromJsonValue<T>(source, convertType<T>{});
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
QJsonValue toJsonValue(const std::optional<T> &source, convertType<std::optional<T>>) {
|
||||
if (source.has_value()) {
|
||||
return toJsonValue<T>(source.value(), convertType<T>{});
|
||||
} else {
|
||||
// Null
|
||||
return QJsonValue();
|
||||
}
|
||||
}
|
||||
|
||||
// QSharedPointer
|
||||
template <typename T>
|
||||
QSharedPointer<T> fromJsonValue(const QJsonValue &source, convertType<QSharedPointer<T>>) {
|
||||
if (source.isNull()) {
|
||||
return QSharedPointer<T>();
|
||||
}
|
||||
return QSharedPointer<T>::create(fromJsonValue<T>(source));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
QJsonValue toJsonValue(const QSharedPointer<T> &source, convertType<QSharedPointer<T>>) {
|
||||
if (source.isNull()) {
|
||||
return QJsonValue();
|
||||
}
|
||||
return toJsonValue<T>(*source);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Templates for string conversion.
|
||||
*/
|
||||
|
||||
template <typename T>
|
||||
QString toString(const T &source, convertType<T>) {
|
||||
return toJsonValue(source).toString();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
QString toString(const std::optional<T> &source, convertType<std::optional<T>>) {
|
||||
if (source.has_value()) {
|
||||
return toString<T>(source.value(), convertType<T>{});
|
||||
} else {
|
||||
return QString();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
QString toString(const T &source) {
|
||||
return toString(source, convertType<T>{});
|
||||
}
|
||||
extern template int fromJsonValue<int>(const QJsonValue &source, convertType<int>);
|
||||
extern template qint64 fromJsonValue<qint64>(const QJsonValue &source, convertType<qint64>);
|
||||
extern template bool fromJsonValue<bool>(const QJsonValue &source, convertType<bool>);
|
||||
extern template QString fromJsonValue<QString>(const QJsonValue &source, convertType<QString>);
|
||||
extern template QStringList fromJsonValue<QStringList>(const QJsonValue &source, convertType<QStringList>);
|
||||
extern template QJsonObject fromJsonValue<QJsonObject>(const QJsonValue &source, convertType<QJsonObject>);
|
||||
extern template double fromJsonValue<double>(const QJsonValue &source, convertType<double>);
|
||||
extern template float fromJsonValue<float>(const QJsonValue &source, convertType<float>);
|
||||
extern template QDateTime fromJsonValue<QDateTime>(const QJsonValue &source, convertType<QDateTime>);
|
||||
extern template QVariant fromJsonValue<QVariant>(const QJsonValue &source, convertType<QVariant>);
|
||||
extern template QUuid fromJsonValue<QUuid>(const QJsonValue &source, convertType<QUuid>);
|
||||
|
||||
extern template QString toString(const QUuid &source, convertType<QUuid>);
|
||||
extern template QString toString(const qint32 &source, convertType<qint32>);
|
||||
extern template QString toString(const qint64 &source, convertType<qint64>);
|
||||
extern template QString toString(const float &source, convertType<float>);
|
||||
extern template QString toString(const double &source, convertType<double>);
|
||||
extern template QString toString(const bool &source, convertType<bool>);
|
||||
extern template QString toString(const QString &source, convertType<QString>);
|
||||
|
||||
} // NS Support
|
||||
} // NS Jellyfin
|
||||
|
|
165
core/include/JellyfinQt/support/jsonconvimpl.h
Normal file
165
core/include/JellyfinQt/support/jsonconvimpl.h
Normal file
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* Sailfin: a Jellyfin client written using Qt
|
||||
* Copyright (C) 2021 Chris Josten and the Sailfin Contributors.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef JELLYFIN_SUPPORT_JSONCONVIMPL_H
|
||||
#define JELLYFIN_SUPPORT_JSONCONVIMPL_H
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QDateTime>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonValue>
|
||||
#include <QList>
|
||||
#include <QSharedPointer>
|
||||
#include <QUuid>
|
||||
#include <QVariant>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace Jellyfin {
|
||||
namespace Support {
|
||||
|
||||
// Helper functions
|
||||
//QString uuidToString(const QUuid &source);
|
||||
//QUuid stringToUuid(const QString &source);
|
||||
|
||||
|
||||
// https://www.fluentcpp.com/2017/08/15/function-templates-partial-specialization-cpp/
|
||||
template <typename T>
|
||||
struct convertType{};
|
||||
|
||||
/**
|
||||
* Template for converting types from JSON into their respective type.
|
||||
*/
|
||||
template <typename T>
|
||||
T fromJsonValue(const QJsonValue &source, convertType<T>) {
|
||||
Q_UNUSED(source)
|
||||
Q_ASSERT_X(false, "fromJsonValue<T>", "fromJsonValue called with unimplemented type");
|
||||
}
|
||||
|
||||
/**
|
||||
* Template for converting types from their type to JSON.
|
||||
*/
|
||||
template <typename T>
|
||||
QJsonValue toJsonValue(const T &source, convertType<T>) {
|
||||
Q_UNUSED(source)
|
||||
std::string msg = "toJsonValue called with unimplemented type ";
|
||||
msg += typeid (T).name();
|
||||
Q_ASSERT_X(false, "toJsonValue<T>", msg.c_str());
|
||||
return QJsonValue();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T fromJsonValue(const QJsonValue &source) {
|
||||
return fromJsonValue(source, convertType<T>{});
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
QJsonValue toJsonValue(const T &source) {
|
||||
return toJsonValue(source, convertType<T>{});
|
||||
}
|
||||
|
||||
// QList
|
||||
template <typename T>
|
||||
QList<T> fromJsonValue(const QJsonValue &source, convertType<QList<T>>) {
|
||||
QList<T> result;
|
||||
QJsonArray arr = source.toArray();
|
||||
result.reserve(arr.size());
|
||||
for (auto it = arr.constBegin(); it != arr.constEnd(); it++) {
|
||||
result.append(fromJsonValue<T>(*it));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
QJsonValue toJsonValue(const QList<T> &source, convertType<QList<T>>) {
|
||||
QJsonArray result;
|
||||
for (auto it = source.cbegin(); it != source.cend(); it++) {
|
||||
result.push_back(toJsonValue<T>(*it));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Optional
|
||||
|
||||
template <typename T>
|
||||
std::optional<T> fromJsonValue(const QJsonValue &source, convertType<std::optional<T>>) {
|
||||
if (source.isNull()) {
|
||||
return std::nullopt;
|
||||
} else {
|
||||
return fromJsonValue<T>(source, convertType<T>{});
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
QJsonValue toJsonValue(const std::optional<T> &source, convertType<std::optional<T>>) {
|
||||
if (source.has_value()) {
|
||||
return toJsonValue<T>(source.value(), convertType<T>{});
|
||||
} else {
|
||||
// Null
|
||||
return QJsonValue();
|
||||
}
|
||||
}
|
||||
|
||||
// QSharedPointer
|
||||
template <typename T>
|
||||
QSharedPointer<T> fromJsonValue(const QJsonValue &source, convertType<QSharedPointer<T>>) {
|
||||
if (source.isNull()) {
|
||||
return QSharedPointer<T>();
|
||||
}
|
||||
return QSharedPointer<T>::create(fromJsonValue<T>(source));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
QJsonValue toJsonValue(const QSharedPointer<T> &source, convertType<QSharedPointer<T>>) {
|
||||
if (source.isNull()) {
|
||||
return QJsonValue();
|
||||
}
|
||||
return toJsonValue<T>(*source);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Templates for string conversion.
|
||||
*/
|
||||
|
||||
template <typename T>
|
||||
QString toString(const T &source, convertType<T>) {
|
||||
return toJsonValue(source).toString();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
QString toString(const std::optional<T> &source, convertType<std::optional<T>>) {
|
||||
if (source.has_value()) {
|
||||
return toString<T>(source.value(), convertType<T>{});
|
||||
} else {
|
||||
return QString();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
QString toString(const T &source) {
|
||||
return toString(source, convertType<T>{});
|
||||
}
|
||||
|
||||
|
||||
} // NS Support
|
||||
} // NS Jellyfin
|
||||
|
||||
#endif // JELLYFIN_SUPPORT_JSONCONVIMPL_H
|
50
core/include/JellyfinQt/support/parseexception.h
Normal file
50
core/include/JellyfinQt/support/parseexception.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Sailfin: a Jellyfin client written using Qt
|
||||
* Copyright (C) 2021 Chris Josten and the Sailfin Contributors.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef JELLYFIN_SUPPORT_PARSEEXCEPTION_H
|
||||
#define JELLYFIN_SUPPORT_PARSEEXCEPTION_H
|
||||
|
||||
#include <QException>
|
||||
#include <QString>
|
||||
|
||||
namespace Jellyfin {
|
||||
namespace Support {
|
||||
|
||||
/**
|
||||
* @brief Thrown when JSON cannot be parsed.
|
||||
*/
|
||||
class ParseException : public QException {
|
||||
public:
|
||||
explicit ParseException(const QString &message)
|
||||
: m_message(message.toStdString()) {}
|
||||
|
||||
/*explicit ParseException(const ParseException &other)
|
||||
: m_message(other.m_message) {}*/
|
||||
|
||||
virtual const char *what() const noexcept override;
|
||||
|
||||
virtual QException *clone() const override;
|
||||
virtual void raise() const override;
|
||||
private:
|
||||
std::string m_message;
|
||||
};
|
||||
|
||||
} // NS Support
|
||||
} // NS Jellyfin
|
||||
|
||||
#endif // JELLYFIN_SUPPORT_PARSEEXCEPTION_H
|
|
@ -84,14 +84,14 @@ public:
|
|||
Q_PROPERTY(QStringList productionLocations READ productionLocations NOTIFY productionLocationsChanged)
|
||||
|
||||
// Handpicked, important ones
|
||||
Q_PROPERTY(qint64 runTimeTicks READ runTimeTicks NOTIFY runTimeTicksChanged)
|
||||
Q_PROPERTY(qint64 runTimeTicks READ runTimeTicks NOTIFY runTimeTicksChanged)*/
|
||||
Q_PROPERTY(QString overview READ overview NOTIFY overviewChanged)
|
||||
Q_PROPERTY(int productionYear READ productionYear NOTIFY productionYearChanged)
|
||||
Q_PROPERTY(int indexNumber READ indexNumber NOTIFY indexNumberChanged)
|
||||
Q_PROPERTY(int indexNumberEnd READ indexNumberEnd NOTIFY indexNumberEndChanged)
|
||||
Q_PROPERTY(bool isFolder READ isFolder NOTIFY isFolderChanged)
|
||||
Q_PROPERTY(QString type READ type NOTIFY typeChanged)
|
||||
Q_PROPERTY(QString parentBackdropItemId READ parentBackdropItemId NOTIFY parentBackdropItemIdChanged)
|
||||
/*Q_PROPERTY(QString parentBackdropItemId READ parentBackdropItemId NOTIFY parentBackdropItemIdChanged)
|
||||
Q_PROPERTY(QStringList parentBackdropImageTags READ parentBackdropImageTags NOTIFY parentBackdropImageTagsChanged)
|
||||
Q_PROPERTY(UserData *userData READ userData NOTIFY userDataChanged)
|
||||
Q_PROPERTY(int recursiveItemCount READ recursiveItemCount NOTIFY recursiveItemCountChanged)
|
||||
|
@ -125,6 +125,12 @@ public:
|
|||
int airsBeforeSeasonNumber() const { return m_data->airsBeforeSeasonNumber().value_or(0); }
|
||||
int airsAfterSeasonNumber() const { return m_data->airsAfterSeasonNumber().value_or(999); }
|
||||
int airsBeforeEpisodeNumber() const { return m_data->airsBeforeEpisodeNumber().value_or(0); }
|
||||
QString overview() const { return m_data->overview(); }
|
||||
int productionYear() const { return m_data->productionYear().value_or(0); }
|
||||
int indexNumber() const { return m_data->indexNumber().value_or(-1); }
|
||||
int indexNumberEnd() const { return m_data->indexNumberEnd().value_or(-1); }
|
||||
bool isFolder() const { return m_data->isFolder().value_or(false); }
|
||||
QString type() const { return m_data->type(); }
|
||||
|
||||
QSharedPointer<Model::Item> data() const { return m_data; }
|
||||
void setData(QSharedPointer<Model::Item> newData);
|
||||
|
@ -192,7 +198,7 @@ public:
|
|||
Q_PROPERTY(QString itemId READ itemId WRITE setItemId NOTIFY itemIdChanged)
|
||||
|
||||
QString itemId() const { return m_parameters.itemId(); }
|
||||
void setItemId(QString newItemId) { m_parameters.setItemId(newItemId); emit itemIdChanged(newItemId); }
|
||||
void setItemId(QString newItemId);
|
||||
virtual bool canReload() const override;
|
||||
|
||||
signals:
|
||||
|
|
|
@ -163,6 +163,10 @@ public:
|
|||
|
||||
// Hand-picked, important ones
|
||||
imageTags,
|
||||
imageBlurHashes,
|
||||
mediaType,
|
||||
type,
|
||||
collectionType,
|
||||
|
||||
jellyfinExtendModelAfterHere = Qt::UserRole + 300 // Should be enough for now
|
||||
};
|
||||
|
@ -183,9 +187,14 @@ public:
|
|||
JFRN(extraType),
|
||||
// Handpicked, important ones
|
||||
JFRN(imageTags),
|
||||
JFRN(imageBlurHashes),
|
||||
JFRN(mediaType),
|
||||
JFRN(type),
|
||||
JFRN(collectionType),
|
||||
};
|
||||
}
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
QSharedPointer<Model::Item> itemAt(int index);
|
||||
};
|
||||
|
||||
/*class UserItemModel : public ItemModel {
|
||||
|
|
|
@ -110,15 +110,19 @@ protected:
|
|||
void setError(QNetworkReply::NetworkError error);
|
||||
void setErrorString(const QString &newErrorString);
|
||||
|
||||
|
||||
void reloadIfNeeded() {
|
||||
if (canReload()) {
|
||||
reload();
|
||||
}
|
||||
}
|
||||
void classBegin() override {
|
||||
m_isParsing = true;
|
||||
}
|
||||
|
||||
void componentComplete() override {
|
||||
m_isParsing = false;
|
||||
if (canReload()) {
|
||||
reload();
|
||||
}
|
||||
reloadIfNeeded();
|
||||
}
|
||||
ApiClient *m_apiClient = nullptr;
|
||||
protected:
|
||||
|
@ -157,7 +161,7 @@ public:
|
|||
}
|
||||
|
||||
T *dataViewModel() const { return m_dataViewModel; }
|
||||
QObject *data() const { return m_dataViewModel; }
|
||||
QObject *data() const override { return m_dataViewModel; }
|
||||
|
||||
void reload() override {
|
||||
if (m_futureWatcher->isRunning()) return;
|
||||
|
|
|
@ -34,9 +34,13 @@
|
|||
#include <functional>
|
||||
|
||||
#include "../dto/baseitemdto.h"
|
||||
#include "../dto/playbackinfodto.h"
|
||||
#include "../dto/playmethod.h"
|
||||
#include "../loader/requesttypes.h"
|
||||
#include "../loader/http/getpostedplaybackinfo.h"
|
||||
#include "../model/playlist.h"
|
||||
#include "../support/jsonconv.h"
|
||||
#include "../viewmodel/item.h"
|
||||
|
||||
#include "../apiclient.h"
|
||||
#include "itemmodel.h"
|
||||
|
||||
|
@ -50,6 +54,9 @@ class RemoteItem;
|
|||
|
||||
namespace ViewModel {
|
||||
|
||||
// Later defined in this file
|
||||
class ItemUrlFetcherThread;
|
||||
|
||||
/**
|
||||
* @brief The PlaybackManager class manages the playback of Jellyfin items. It fetches streams based on Jellyfin items, posts
|
||||
* the current playback state to the Jellyfin Server, contains the actual media player and so on.
|
||||
|
@ -58,15 +65,10 @@ namespace ViewModel {
|
|||
* preloading the next item in the queue. The current media player is pointed to by m_mediaPlayer.
|
||||
*/
|
||||
class PlaybackManager : public QObject, public QQmlParserStatus {
|
||||
friend class ItemUrlFetcherThread;
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(QQmlParserStatus)
|
||||
public:
|
||||
enum PlayMethod {
|
||||
Transcode,
|
||||
Stream,
|
||||
DirectPlay
|
||||
};
|
||||
Q_ENUM(PlayMethod)
|
||||
using FetchCallback = std::function<void(QUrl &&, PlayMethod)>;
|
||||
|
||||
explicit PlaybackManager(QObject *parent = nullptr);
|
||||
|
@ -77,11 +79,11 @@ public:
|
|||
Q_PROPERTY(int audioIndex MEMBER m_audioIndex NOTIFY audioIndexChanged)
|
||||
Q_PROPERTY(int subtitleIndex MEMBER m_subtitleIndex NOTIFY subtitleIndexChanged)
|
||||
Q_PROPERTY(bool resumePlayback MEMBER m_resumePlayback NOTIFY resumePlaybackChanged)
|
||||
Q_PROPERTY(PlayMethod playMethod READ playMethod NOTIFY playMethodChanged)
|
||||
Q_PROPERTY(Jellyfin::DTO::PlayMethodClass::Value playMethod READ playMethod NOTIFY playMethodChanged)
|
||||
|
||||
// Current Item and queue informatoion
|
||||
Q_PROPERTY(ViewModel::Item *item READ item NOTIFY itemChanged)
|
||||
Q_PROPERTY(QAbstractItemModel *queue READ queue NOTIFY queueChanged)
|
||||
Q_PROPERTY(QObject *item READ item NOTIFY itemChanged)
|
||||
// Q_PROPERTY(QAbstractItemModel *queue READ queue NOTIFY queueChanged)
|
||||
Q_PROPERTY(int queueIndex READ queueIndex NOTIFY queueIndexChanged)
|
||||
|
||||
// Current media player related property getters
|
||||
|
@ -102,7 +104,7 @@ public:
|
|||
QObject *mediaObject() const { return m_mediaPlayer; }
|
||||
qint64 position() const { return m_mediaPlayer->position(); }
|
||||
qint64 duration() const { return m_mediaPlayer->duration(); }
|
||||
ItemModel *queue() const { return m_queue; }
|
||||
//ItemModel *queue() const { return m_queue; }
|
||||
int queueIndex() const { return m_queueIndex; }
|
||||
|
||||
// Current media player related property getters
|
||||
|
@ -112,7 +114,7 @@ public:
|
|||
QMediaPlayer::Error error () const { return m_mediaPlayer->error(); }
|
||||
QString errorString() const { return m_mediaPlayer->errorString(); }
|
||||
signals:
|
||||
void itemChanged(BaseItemDto *newItemId);
|
||||
void itemChanged(ViewModel::Item *newItemId);
|
||||
void streamUrlChanged(const QString &newStreamUrl);
|
||||
void autoOpenChanged(bool autoOpen);
|
||||
void audioIndexChanged(int audioIndex);
|
||||
|
@ -125,7 +127,7 @@ signals:
|
|||
void mediaObjectChanged(QObject *newMediaObject);
|
||||
void positionChanged(qint64 newPosition);
|
||||
void durationChanged(qint64 newDuration);
|
||||
void queueChanged(ItemModel *newQue);
|
||||
//void queueChanged(ItemModel *newQue);
|
||||
void queueIndexChanged(int newIndex);
|
||||
void playbackStateChanged(QMediaPlayer::State newState);
|
||||
void mediaStatusChanged(QMediaPlayer::MediaStatus newMediaStatus);
|
||||
|
@ -138,9 +140,9 @@ public slots:
|
|||
*
|
||||
* This will construct the Jellyfin::Item internally
|
||||
* and delete it later.
|
||||
* @param itemId The id of the item to play.
|
||||
* @param item The item to play.
|
||||
*/
|
||||
void playItem(const QString &itemId);
|
||||
void playItem(Item *item);
|
||||
void playItemInList(ItemModel *itemList, int index);
|
||||
void play() { m_mediaPlayer->play(); }
|
||||
void pause() { m_mediaPlayer->pause(); }
|
||||
|
@ -162,24 +164,43 @@ private slots:
|
|||
void mediaPlayerPositionChanged(qint64 position);
|
||||
void mediaPlayerMediaStatusChanged(QMediaPlayer::MediaStatus newStatus);
|
||||
void mediaPlayerError(QMediaPlayer::Error error);
|
||||
void mediaPlayerDurationChanged(qint64 newDuration);
|
||||
/**
|
||||
* @brief updatePlaybackInfo Updates the Jellyfin server with the current playback progress etc.
|
||||
*/
|
||||
void updatePlaybackInfo();
|
||||
|
||||
/// Called when the fetcherThread has fetched the playback URL and playSession
|
||||
void onItemExtraDataReceived(const QString &itemId, const QUrl &url, const QString &playSession,
|
||||
// Fully specify class to please MOC
|
||||
Jellyfin::DTO::PlayMethodClass::Value playMethod);
|
||||
/// Called when the fetcherThread encountered an error
|
||||
void onItemErrorReceived(const QString &itemId, const QString &errorString);
|
||||
void onDestroyed();
|
||||
|
||||
private:
|
||||
/// Factor to multiply with when converting from milliseconds to ticks.
|
||||
const static int MS_TICK_FACTOR = 10000;
|
||||
enum PlaybackInfoType { Started, Stopped, Progress };
|
||||
|
||||
/// Timer used to update the play progress on the Jellyfin server
|
||||
QTimer m_updateTimer;
|
||||
/// Timer used to notify ourselves when we need to preload the next item
|
||||
QTimer m_preloadTimer;
|
||||
|
||||
ApiClient *m_apiClient = nullptr;
|
||||
/// The currently playing item
|
||||
QSharedPointer<Model::Item> m_item;
|
||||
/// The item that will be played next
|
||||
QSharedPointer<Model::Item> m_nextItem;
|
||||
/// The currently played item that will be shown in the GUI
|
||||
ViewModel::Item *m_displayItem = new ViewModel::Item(this);
|
||||
|
||||
// Properties for making the streaming request.
|
||||
QString m_streamUrl;
|
||||
QString m_nextStreamUrl;
|
||||
QString m_playSessionId;
|
||||
QString m_nextPlaySessionId;
|
||||
/// The index of the mediastreams of the to-be-played item containing the audio
|
||||
int m_audioIndex = 0;
|
||||
/// The index of the mediastreams of the to-be-played item containing subtitles
|
||||
|
@ -196,10 +217,11 @@ private:
|
|||
*/
|
||||
bool m_autoOpen = false;
|
||||
|
||||
|
||||
// Playback-related members
|
||||
ItemUrlFetcherThread *m_urlFetcherThread;
|
||||
|
||||
QMediaPlayer::State m_oldState = QMediaPlayer::StoppedState;
|
||||
PlayMethod m_playMethod = Transcode;
|
||||
PlayMethod m_playMethod = PlayMethod::Transcode;
|
||||
QMediaPlayer::State m_playbackState = QMediaPlayer::StoppedState;
|
||||
/// Pointer to the current media player.
|
||||
QMediaPlayer *m_mediaPlayer = nullptr;
|
||||
|
@ -211,28 +233,18 @@ private:
|
|||
QMediaPlayer *m_mediaPlayer1;
|
||||
/// Media player 2
|
||||
QMediaPlayer *m_mediaPlayer2;
|
||||
ItemModel *m_queue = nullptr;
|
||||
|
||||
Model::Playlist *m_queue = nullptr;
|
||||
int m_queueIndex = 0;
|
||||
bool m_resumePlayback = true;
|
||||
|
||||
// Helper methods
|
||||
void setItem(ViewModel::Item *newItem);
|
||||
void setItem(QSharedPointer<Model::Item> newItem);
|
||||
void swapMediaPlayer();
|
||||
|
||||
|
||||
/**
|
||||
* @brief Retrieves the URL of the stream to open.
|
||||
*/
|
||||
void fetchStreamUrl(const Model::Item *item, bool autoOpen, const FetchCallback &callback);
|
||||
void fetchAndSetStreamUrl(const Model::Item *item);
|
||||
void setStreamUrl(const QString &streamUrl);
|
||||
void setStreamUrl(const QUrl &streamUrl);
|
||||
void setPlaybackState(QMediaPlayer::State newState);
|
||||
|
||||
Model::Item *nextItem();
|
||||
void setQueue(ItemModel *itemModel);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Posts the playback information
|
||||
*/
|
||||
|
@ -243,6 +255,60 @@ private:
|
|||
void classBegin() override { m_qmlIsParsingComponent = true; }
|
||||
void componentComplete() override;
|
||||
bool m_qmlIsParsingComponent = false;
|
||||
|
||||
/// Time in ms at what moment this playbackmanager should start loading the next item.
|
||||
const qint64 PRELOAD_DURATION = 15 * 1000;
|
||||
};
|
||||
|
||||
/// Thread that fetches the Item's stream URL always in the given order they were requested
|
||||
class ItemUrlFetcherThread : public QThread {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ItemUrlFetcherThread(PlaybackManager *manager);
|
||||
|
||||
/**
|
||||
* @brief Adds an item to the queue of items that should be requested
|
||||
* @param item The item to fetch the URL of
|
||||
*/
|
||||
void addItemToQueue(QSharedPointer<Model::Item> item);
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief Emitted when the url of the item with the itemId has been retrieved.
|
||||
* @param itemId The id of the item of which the URL has been retrieved
|
||||
* @param itemUrl The retrieved url
|
||||
* @param playSession The playsession set by the Jellyfin Server
|
||||
*/
|
||||
void itemUrlFetched(QString itemId, QUrl itemUrl, QString playSession, Jellyfin::DTO::PlayMethodClass::Value playMethod);
|
||||
void itemUrlFetchError(QString itemId, QString errorString);
|
||||
|
||||
void prepareLoaderRequested(QPrivateSignal);
|
||||
public slots:
|
||||
/**
|
||||
* @brief Ask the thread nicely to stop running.
|
||||
*/
|
||||
void cleanlyStop();
|
||||
private slots:
|
||||
void onPrepareLoader();
|
||||
protected:
|
||||
void run() override;
|
||||
private:
|
||||
PlaybackManager *m_parent;
|
||||
Support::Loader<DTO::PlaybackInfoResponse, Jellyfin::Loader::GetPostedPlaybackInfoParams> *m_loader;
|
||||
|
||||
QMutex m_queueModifyMutex;
|
||||
QQueue<QSharedPointer<Model::Item>> m_queue;
|
||||
|
||||
QMutex m_urlWaitConditionMutex;
|
||||
/// WaitCondition on which this threads waits until an Item is put into the queue
|
||||
QWaitCondition m_urlWaitCondition;
|
||||
|
||||
QMutex m_waitLoaderPreparedMutex;
|
||||
/// WaitCondition on which this threads waits until the loader has been prepared.
|
||||
QWaitCondition m_waitLoaderPrepared;
|
||||
|
||||
bool m_keepRunning = true;
|
||||
bool m_loaderPrepared = false;
|
||||
};
|
||||
|
||||
} // NS ViewModel
|
||||
|
|
|
@ -29,109 +29,30 @@
|
|||
#include <QWaitCondition>
|
||||
#include <QtMultimedia/QMediaPlaylist>
|
||||
|
||||
#include "../dto/playbackinfodto.h"
|
||||
#include "../loader/requesttypes.h"
|
||||
#include "../loader/http/getpostedplaybackinfo.h"
|
||||
#include "../apiclient.h"
|
||||
#include "itemmodel.h"
|
||||
|
||||
namespace Jellyfin {
|
||||
namespace ViewModel {
|
||||
|
||||
class ItemUrlFetcherThread;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Playlist/queue that can be exposed to the UI. It also containts the playlist-related logic,
|
||||
* which is mostly relevant
|
||||
*/
|
||||
class Playlist : public ItemModel {
|
||||
/*class Playlist : public ItemModel {
|
||||
Q_OBJECT
|
||||
friend class ItemUrlFetcherThread;
|
||||
public:
|
||||
explicit Playlist(ApiClient *apiClient, QObject *parent = nullptr);
|
||||
enum ExtraRoles {
|
||||
Url = ItemModel::RoleNames::jellyfinExtendModelAfterHere + 1,
|
||||
PlaySession,
|
||||
ErrorText
|
||||
};
|
||||
|
||||
QHash<int, QByteArray> roleNames() const override {
|
||||
QHash<int, QByteArray> result(ItemModel::roleNames());
|
||||
result.insert(Url, "url");
|
||||
result.insert(PlaySession, "playSession");
|
||||
result.insert(ErrorText, "errorText");
|
||||
return result;
|
||||
}
|
||||
|
||||
private slots:
|
||||
void onItemsAdded(const QModelIndex &parent, int startIndex, int endIndex);
|
||||
void onItemsMoved(const QModelIndex &parent, int startIndex, int endIndex, const QModelIndex &destination, int destinationRow);
|
||||
void onItemsRemoved(const QModelIndex &parent, int startIndex, int endIndex);
|
||||
void onItemsReset();
|
||||
/// Called when the fetcherThread has fetched the playback URL and playSession
|
||||
void onItemExtraDataReceived(const QString &itemId, const QUrl &url, const QString &playSession);
|
||||
/// Called when the fetcherThread encountered an error
|
||||
void onItemErrorReceived(const QString &itemId, const QString &errorString);
|
||||
private:
|
||||
/// Map from ItemId to ExtraData
|
||||
QHash<QString, ExtraData> m_cache;
|
||||
};*/
|
||||
|
||||
ApiClient *m_apiClient;
|
||||
|
||||
/// Thread that fetches the URLS asynchronously
|
||||
ItemUrlFetcherThread *m_fetcherThread;
|
||||
};
|
||||
|
||||
/// Thread that fetches the Item's stream URL always in the given order they were requested
|
||||
class ItemUrlFetcherThread : public QThread {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ItemUrlFetcherThread(Playlist *playlist);
|
||||
|
||||
/**
|
||||
* @brief Adds an item to the queue of items that should be requested
|
||||
* @param item The item to fetch the URL of
|
||||
*/
|
||||
void addItemToQueue(const Model::Item item);
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief Emitted when the url of the item with the itemId has been retrieved.
|
||||
* @param itemId The id of the item of which the URL has been retrieved
|
||||
* @param itemUrl The retrieved url
|
||||
* @param playSession The playsession set by the Jellyfin Server
|
||||
*/
|
||||
void itemUrlFetched(QString itemId, QUrl itemUrl, QString playSession);
|
||||
void itemUrlFetchError(QString itemId, QString errorString);
|
||||
|
||||
void prepareLoaderRequested(QPrivateSignal);
|
||||
public slots:
|
||||
/**
|
||||
* @brief Ask the thread nicely to stop running.
|
||||
*/
|
||||
void cleanlyStop();
|
||||
private slots:
|
||||
void onPrepareLoader();
|
||||
protected:
|
||||
void run() override;
|
||||
private:
|
||||
Playlist *m_parent;
|
||||
Support::Loader<DTO::PlaybackInfoResponse, Jellyfin::Loader::GetPostedPlaybackInfoParams> *m_loader;
|
||||
|
||||
QMutex m_queueModifyMutex;
|
||||
QQueue<const Model::Item&> m_queue;
|
||||
|
||||
QMutex m_urlWaitConditionMutex;
|
||||
/// WaitCondition on which this threads waits until an Item is put into the queue
|
||||
QWaitCondition m_urlWaitCondition;
|
||||
|
||||
QMutex m_waitLoaderPreparedMutex;
|
||||
/// WaitCondition on which this threads waits until the loader has been prepared.
|
||||
QWaitCondition m_waitLoaderPrepared;
|
||||
|
||||
bool m_keepRunning = true;
|
||||
bool m_loaderPrepared = false;
|
||||
};
|
||||
|
||||
} // NS ViewModel
|
||||
} // NS Jellyfin
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue