mirror of
https://github.com/HenkKalkwater/harbour-sailfin.git
synced 2025-09-04 01:42:44 +00:00
Rewire more of Sailfish frontend into new backend
This should encompass most simple things, besides some larger, trickier things, like the video streams and the now-broken userdata
This commit is contained in:
parent
df1e134821
commit
7b6c272aa9
47 changed files with 620 additions and 291 deletions
|
@ -19,6 +19,7 @@ set(JellyfinQt_SOURCES
|
|||
src/viewmodel/modelstatus.cpp
|
||||
src/viewmodel/playbackmanager.cpp
|
||||
src/viewmodel/playlist.cpp
|
||||
src/viewmodel/userdata.cpp
|
||||
src/apiclient.cpp
|
||||
src/apimodel.cpp
|
||||
src/credentialmanager.cpp
|
||||
|
@ -46,6 +47,7 @@ set(JellyfinQt_HEADERS
|
|||
include/JellyfinQt/viewmodel/propertyhelper.h
|
||||
include/JellyfinQt/viewmodel/playbackmanager.h
|
||||
include/JellyfinQt/viewmodel/playlist.h
|
||||
include/JellyfinQt/viewmodel/userdata.h
|
||||
include/JellyfinQt/apiclient.h
|
||||
include/JellyfinQt/apimodel.h
|
||||
include/JellyfinQt/credentialmanager.h
|
||||
|
|
|
@ -124,7 +124,7 @@ public:
|
|||
Q_ENUM(ApiError)
|
||||
|
||||
const QString &baseUrl() const { return this->m_baseUrl; }
|
||||
const QString &userId() const { return m_userId; }
|
||||
const QString userId() const { return m_userId; }
|
||||
const QString &deviceId() const { return m_deviceId; }
|
||||
/**
|
||||
* @brief QML applications can set this type to indicate which commands they support.
|
||||
|
@ -275,7 +275,7 @@ private:
|
|||
QString m_token;
|
||||
QString m_deviceName;
|
||||
QString m_deviceId;
|
||||
QString m_userId = "";
|
||||
QString m_userId;
|
||||
QJsonObject m_deviceProfile;
|
||||
QJsonObject m_playbackDeviceProfile;
|
||||
bool m_online = true;
|
||||
|
|
|
@ -188,7 +188,7 @@ public:
|
|||
* @return pair containing the items loaded and the integer containing the starting offset. A starting
|
||||
* offset of -1 means an error has occurred.
|
||||
*/
|
||||
std::pair<QList<T>, int> &&result() { return std::move(m_result); }
|
||||
std::pair<QList<T*>, int> &&result() { return std::move(m_result); }
|
||||
protected:
|
||||
/**
|
||||
* @brief Loads data from the given offset with a maximum count of limit.
|
||||
|
@ -205,7 +205,7 @@ protected:
|
|||
m_startIndex = startIndex;
|
||||
m_totalRecordCount = totalRecordCount;
|
||||
}
|
||||
std::pair<QList<T>, int> m_result;
|
||||
std::pair<QList<T*>, int> m_result;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -275,7 +275,7 @@ protected:
|
|||
// If futureWatcher's future is running, this method should not be called again.
|
||||
if (m_futureWatcher.isRunning()) return;
|
||||
// Set an invalid result.
|
||||
this->m_result = { QList<T>(), -1 };
|
||||
this->m_result = { QList<T*>(), -1 };
|
||||
|
||||
if (!setRequestStartIndex<P>(this->m_parameters, offset)
|
||||
&& suggestedModelStatus == ViewModel::ModelStatus::LoadingMore) {
|
||||
|
@ -326,12 +326,12 @@ protected:
|
|||
if (totalRecordCount < 0) {
|
||||
totalRecordCount = records.size();
|
||||
}
|
||||
QList<T> models;
|
||||
QList<T*> models;
|
||||
models.reserve(records.size());
|
||||
|
||||
// Convert the DTOs into models
|
||||
for (int i = 0; i < records.size(); i++) {
|
||||
models.append(T(records[i], m_loader->apiClient()));
|
||||
models.append(new T(records[i], m_loader->apiClient()));
|
||||
}
|
||||
this->setStatus(ViewModel::ModelStatus::Ready);
|
||||
this->m_result = { models, this->m_startIndex};
|
||||
|
@ -441,7 +441,7 @@ public:
|
|||
}
|
||||
|
||||
// QList-like API
|
||||
const T& at(int index) const { return m_array.at(index); }
|
||||
QSharedPointer<T> at(int index) const { return m_array.at(index); }
|
||||
/**
|
||||
* @return the amount of objects in this model.
|
||||
*/
|
||||
|
@ -449,22 +449,23 @@ public:
|
|||
return m_array.size();
|
||||
}
|
||||
|
||||
void insert(int index, T &object) {
|
||||
void insert(int index, QSharedPointer<T> object) {
|
||||
Q_ASSERT(index >= 0 && index <= size());
|
||||
this->beginInsertRows(QModelIndex(), index, index);
|
||||
m_array.insert(index, object);
|
||||
this->endInsertRows();
|
||||
}
|
||||
|
||||
void append(T &object) { insert(size(), object); }
|
||||
void append(QList<T> &objects) {
|
||||
void append(QSharedPointer<T> object) { insert(size(), object); }
|
||||
//void append(T &object) { insert(size(), object); }
|
||||
void append(QList<QSharedPointer<T>> &objects) {
|
||||
int index = size();
|
||||
this->beginInsertRows(QModelIndex(), index, index + objects.size() - 1);
|
||||
m_array.append(objects);
|
||||
this->endInsertRows();
|
||||
};
|
||||
|
||||
QList<T> mid(int pos, int length = -1) {
|
||||
QList<T*> mid(int pos, int length = -1) {
|
||||
return m_array.mid(pos, length);
|
||||
}
|
||||
|
||||
|
@ -483,7 +484,7 @@ public:
|
|||
this->endRemoveRows();
|
||||
}
|
||||
|
||||
void removeOne(T &object) {
|
||||
void removeOne(QSharedPointer<T> object) {
|
||||
int idx = m_array.indexOf(object);
|
||||
if (idx >= 0) {
|
||||
removeAt(idx);
|
||||
|
@ -535,20 +536,26 @@ public:
|
|||
|
||||
protected:
|
||||
// Model-specific properties.
|
||||
QList<T> m_array;
|
||||
QList<QSharedPointer<T>> m_array;
|
||||
ModelLoader<T> *m_loader = nullptr;
|
||||
|
||||
void loadingFinished() override {
|
||||
Q_ASSERT(m_loader != nullptr);
|
||||
std::pair<QList<T>, int> result = m_loader->result();
|
||||
std::pair<QList<T*>, int> result = m_loader->result();
|
||||
qDebug() << "Results loaded: index: " << result.second << ", count: " << result.first.size();
|
||||
if (result.second == -1) {
|
||||
clear();
|
||||
} else if (result.second == m_array.size()) {
|
||||
append(result.first);
|
||||
m_array.reserve(m_array.size() + result.second);
|
||||
for (auto it = result.first.begin(); it != result.first.end(); it++) {
|
||||
append(QSharedPointer<T>(*it));
|
||||
}
|
||||
} else if (result.second < m_array.size()){
|
||||
removeUntilEnd(result.second);
|
||||
append(result.first);
|
||||
m_array.reserve(m_array.size() + result.second);
|
||||
for (auto it = result.first.begin(); it != result.first.end(); it++) {
|
||||
append(QSharedPointer<T>(*it));
|
||||
}
|
||||
} else {
|
||||
// result.second > m_array.size()
|
||||
qWarning() << "Retrieved data from loader at position bigger than size()";
|
||||
|
|
|
@ -123,7 +123,6 @@ public:
|
|||
|
||||
private:
|
||||
QString urlToGroupName(const QString &url) const;
|
||||
QString groupNameToUrl(const QString &group) const;
|
||||
QSettings m_settings;
|
||||
};
|
||||
|
||||
|
|
|
@ -20,9 +20,11 @@
|
|||
#ifndef JELLYFIN_MODEL_ITEM
|
||||
#define JELLYFIN_MODEL_ITEM
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
|
||||
#include <QObject>
|
||||
#include <QList>
|
||||
#include <QThreadPool>
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
|
||||
#include "../dto/baseitemdto.h"
|
||||
#include "../support/loader.h"
|
||||
|
@ -31,20 +33,22 @@
|
|||
namespace Jellyfin {
|
||||
namespace Model {
|
||||
|
||||
class Item : public DTO::BaseItemDto {
|
||||
class Item : public QObject, public DTO::BaseItemDto {
|
||||
Q_OBJECT
|
||||
public:
|
||||
using UserDataChangedCallback = std::function<void(DTO::UserItemDataDto)>;
|
||||
/**
|
||||
* @brief Constructor that creates an empty item.
|
||||
*/
|
||||
Item();
|
||||
Item(QObject *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Copies the data from the DTO into this model and attaches an ApiClient
|
||||
* @param data The DTO to copy information from
|
||||
* @param apiClient The ApiClient to attach to, to listen for updates and so on.
|
||||
*/
|
||||
Item(const DTO::BaseItemDto &data, ApiClient *apiClient = nullptr);
|
||||
virtual ~Item();
|
||||
Item(const DTO::BaseItemDto &data, ApiClient *apiClient = nullptr, QObject *parent = nullptr);
|
||||
//virtual ~Item();
|
||||
|
||||
/**
|
||||
* @brief sameAs Returns true if this item represents the same item as `other`
|
||||
|
@ -58,11 +62,13 @@ public:
|
|||
bool sameAs(const DTO::BaseItemDto &other);
|
||||
|
||||
void setApiClient(ApiClient *apiClient);
|
||||
|
||||
signals:
|
||||
void userDataChanged(const DTO::UserItemDataDto &newUserData);
|
||||
private:
|
||||
ApiClient *m_apiClient = nullptr;
|
||||
QList<QMetaObject::Connection> m_apiClientConnections;
|
||||
void updateUserData(const QString &itemId, const DTO::UserItemDataDto &userData);
|
||||
|
||||
void onUserDataUpdated(const QString &itemId, const DTO::UserItemDataDto &userData);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -76,7 +76,7 @@ public:
|
|||
/**
|
||||
* @brief Appends all items from the given itemModel to this list
|
||||
*/
|
||||
void appendToList(const ViewModel::ItemModel &model);
|
||||
void appendToList(ViewModel::ItemModel &model);
|
||||
|
||||
/**
|
||||
* @brief Start playing this playlist
|
||||
|
|
|
@ -46,15 +46,22 @@
|
|||
#include "loader.h"
|
||||
|
||||
namespace Jellyfin {
|
||||
|
||||
namespace DTO {
|
||||
class UserItemDataDto;
|
||||
} // NS DTO
|
||||
|
||||
namespace ViewModel {
|
||||
|
||||
class UserData;
|
||||
|
||||
class Item : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit Item(QObject *parent = nullptr, QSharedPointer<Model::Item> data = QSharedPointer<Model::Item>::create());
|
||||
|
||||
// Please keep the order of the properties the same as in the file linked above.
|
||||
Q_PROPERTY(QUuid jellyfinId READ jellyfinId NOTIFY jellyfinIdChanged)
|
||||
Q_PROPERTY(QString jellyfinId READ jellyfinId NOTIFY jellyfinIdChanged)
|
||||
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
|
||||
Q_PROPERTY(QString originalTitle READ originalTitle NOTIFY originalTitleChanged)
|
||||
Q_PROPERTY(QString serverId READ serverId NOTIFY serverIdChanged)
|
||||
|
@ -81,27 +88,29 @@ public:
|
|||
//SKIP: ExternalUrls
|
||||
//SKIP: MediaSources
|
||||
Q_PROPERTY(float criticRating READ criticRating NOTIFY criticRatingChanged)
|
||||
Q_PROPERTY(QStringList productionLocations READ productionLocations NOTIFY productionLocationsChanged)
|
||||
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)
|
||||
Q_PROPERTY(int childCount READ childCount NOTIFY childCountChanged)
|
||||
Q_PROPERTY(QString albumArtist READ albumArtist NOTIFY albumArtistChanged)
|
||||
Q_PROPERTY(QVariantList albumArtists READ albumArtists NOTIFY albumArtistsChanged)
|
||||
/*Q_PROPERTY(QVariantList albumArtists READ albumArtists NOTIFY albumArtistsChanged)*/
|
||||
Q_PROPERTY(QString seriesName READ seriesName NOTIFY seriesNameChanged)
|
||||
Q_PROPERTY(QString seriesId READ seriesId NOTIFY seriesIdChanged)
|
||||
Q_PROPERTY(QString seasonId READ seasonId NOTIFY seasonIdChanged)
|
||||
Q_PROPERTY(QString seasonName READ seasonName NOTIFY seasonNameChanged)
|
||||
Q_PROPERTY(QList<MediaStream *> __list__mediaStreams MEMBER __list__m_mediaStreams NOTIFY mediaStreamsChanged)
|
||||
Q_PROPERTY(QVariantList mediaStreams READ mediaStreams NOTIFY mediaStreamsChanged STORED false)
|
||||
/*Q_PROPERTY(QList<MediaStream *> __list__mediaStreams MEMBER __list__m_mediaStreams NOTIFY mediaStreamsChanged)
|
||||
Q_PROPERTY(QVariantList mediaStreams READ mediaStreams NOTIFY mediaStreamsChanged STORED false)*/
|
||||
Q_PROPERTY(QStringList artists READ artists NOTIFY artistsChanged)
|
||||
// Why is this a QJsonObject? Well, because I couldn't be bothered to implement the deserialisations of
|
||||
// a QHash at the moment.
|
||||
|
@ -110,9 +119,9 @@ public:
|
|||
Q_PROPERTY(QJsonObject imageBlurHashes READ imageBlurHashes NOTIFY imageBlurHashesChanged)
|
||||
Q_PROPERTY(QString mediaType READ mediaType READ mediaType NOTIFY mediaTypeChanged)
|
||||
Q_PROPERTY(int width READ width NOTIFY widthChanged)
|
||||
Q_PROPERTY(int height READ height NOTIFY heightChanged)*/
|
||||
Q_PROPERTY(int height READ height NOTIFY heightChanged)
|
||||
|
||||
QUuid jellyfinId() const { return m_data->jellyfinId(); }
|
||||
QString jellyfinId() const { return m_data->jellyfinId(); }
|
||||
QString name() const { return m_data->name(); }
|
||||
QString originalTitle() const { return m_data->originalTitle(); }
|
||||
QString serverId() const { return m_data->serverId(); }
|
||||
|
@ -125,17 +134,36 @@ 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); }
|
||||
qint64 runTimeTicks() const { return m_data->runTimeTicks().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(); }
|
||||
QString parentBackdropItemId() const { return m_data->parentBackdropItemId(); }
|
||||
QStringList parentBackdropImageTags() const { return m_data->parentBackdropImageTags(); }
|
||||
UserData *userData() const { return m_userData; }
|
||||
int recursiveItemCount() const { return m_data->recursiveItemCount().value_or(0); }
|
||||
int childCount() const { return m_data->childCount().value_or(0); }
|
||||
QString albumArtist() const { return m_data->albumArtist(); }
|
||||
QString seriesName() const { return m_data->seriesName(); }
|
||||
QString seriesId() const { return m_data->seriesId(); }
|
||||
QString seasonId() const { return m_data->seasonId(); }
|
||||
QString seasonName() const { return m_data->seasonName(); }
|
||||
|
||||
QStringList artists() const { return m_data->artists(); }
|
||||
QJsonObject imageTags() const { return m_data->imageTags(); }
|
||||
QStringList backdropImageTags() const { return m_data->backdropImageTags(); }
|
||||
QJsonObject imageBlurHashes() const { return m_data->imageBlurHashes(); }
|
||||
QString mediaType() const { return m_data->mediaType(); }
|
||||
int width() const { return m_data->width().value_or(0); }
|
||||
int height() const { return m_data->height().value_or(0); }
|
||||
|
||||
QSharedPointer<Model::Item> data() const { return m_data; }
|
||||
void setData(QSharedPointer<Model::Item> newData);
|
||||
signals:
|
||||
void jellyfinIdChanged(const QUuid &newId);
|
||||
void jellyfinIdChanged(const QString &newId);
|
||||
void nameChanged(const QString &newName);
|
||||
void originalTitleChanged(const QString &newOriginalTitle);
|
||||
void serverIdChanged(const QString &newServerId);
|
||||
|
@ -171,12 +199,14 @@ signals:
|
|||
void typeChanged(const QString &newType);
|
||||
void parentBackdropItemIdChanged();
|
||||
void parentBackdropImageTagsChanged();
|
||||
//void userDataChanged(UserData *newUserData);
|
||||
void userDataChanged(UserData *newUserData);
|
||||
void recursiveItemCountChanged(int newRecursiveItemCount);
|
||||
void childCountChanged(int newChildCount);
|
||||
void albumArtistChanged(const QString &newAlbumArtist);
|
||||
//void albumArtistsChanged(NameGuidPair *newAlbumArtists);
|
||||
void seriesNameChanged(const QString &newSeriesName);
|
||||
void seriesIdChanged(const QString &newSeriesId);
|
||||
void seasonIdChanged(const QString &newSeasonId);
|
||||
void seasonNameChanged(const QString &newSeasonName);
|
||||
void mediaStreamsChanged(/*const QList<MediaStream *> &newMediaStreams*/);
|
||||
void artistsChanged(const QStringList &newArtists);
|
||||
|
@ -187,7 +217,13 @@ signals:
|
|||
void widthChanged(int newWidth);
|
||||
void heightChanged(int newHeight);
|
||||
protected:
|
||||
void setUserData(DTO::UserItemDataDto &newData);
|
||||
void setUserData(QSharedPointer<DTO::UserItemDataDto> newData);
|
||||
|
||||
QSharedPointer<Model::Item> m_data;
|
||||
UserData *m_userData = nullptr;
|
||||
private slots:
|
||||
void onUserDataChanged(const DTO::UserItemDataDto &userData);
|
||||
};
|
||||
|
||||
class ItemLoader : public Loader<ViewModel::Item, DTO::BaseItemDto, Jellyfin::Loader::GetItemParams> {
|
||||
|
|
|
@ -134,12 +134,108 @@ public:
|
|||
FWDPROP(QList<Jellyfin::DTO::LocationTypeClass::Value>, excludeLocationTypes, ExcludeLocationTypes)
|
||||
FWDPROP(QList<Jellyfin::DTO::ItemFieldsClass::Value>, fields, Fields)
|
||||
FWDPROP(QList<Jellyfin::DTO::ItemFilterClass::Value>, filters, Filters)
|
||||
FWDPROP(QStringList, genreIds, GenreIds)
|
||||
FWDPROP(QStringList, genres, Genres)
|
||||
FWDPROP(bool, hasImdbId, HasImdbId)
|
||||
FWDPROP(bool, hasOfficialRating, HasOfficialRating)
|
||||
FWDPROP(bool, hasOverview, HasOverview)
|
||||
FWDPROP(bool, hasParentalRating, HasParentalRating)
|
||||
FWDPROP(bool, hasSpecialFeature, HasSpecialFeature)
|
||||
FWDPROP(bool, hasSubtitles, HasSubtitles)
|
||||
FWDPROP(bool, hasThemeSong, HasThemeSong)
|
||||
FWDPROP(bool, hasThemeVideo, HasThemeVideo)
|
||||
FWDPROP(bool, hasTmdbId, HasTmdbId)
|
||||
FWDPROP(bool, hasTrailer, HasTrailer)
|
||||
FWDPROP(bool, hasTvdbId, HasTvdbId)
|
||||
FWDPROP(QStringList, ids, Ids)
|
||||
FWDPROP(qint32, imageTypeLimit, ImageTypeLimit)
|
||||
FWDPROP(QList<Jellyfin::DTO::ImageTypeClass::Value>, imageTypes, ImageTypes)
|
||||
FWDPROP(QStringList, includeItemTypes, IncludeItemTypes)
|
||||
FWDPROP(bool, is3D, Is3D)
|
||||
FWDPROP(bool, is4K, Is4K)
|
||||
FWDPROP(bool, isFavorite, IsFavorite)
|
||||
FWDPROP(bool, isHd, IsHd)
|
||||
FWDPROP(bool, isLocked, IsLocked)
|
||||
FWDPROP(bool, isMissing, IsMissing)
|
||||
FWDPROP(bool, isPlaceHolder, IsPlaceHolder)
|
||||
FWDPROP(bool, isPlayed, IsPlayed)
|
||||
FWDPROP(bool, isUnaired, IsUnaired)
|
||||
FWDPROP(QList<Jellyfin::DTO::LocationTypeClass::Value>, locationTypes, LocationTypes)
|
||||
FWDPROP(qint32, maxHeight, MaxHeight)
|
||||
FWDPROP(QString, maxOfficialRating, MaxOfficialRating)
|
||||
FWDPROP(QDateTime, maxPremiereDate, MaxPremiereDate)
|
||||
FWDPROP(qint32, maxWidth, MaxWidth)
|
||||
FWDPROP(QStringList, mediaTypes, MediaTypes)
|
||||
FWDPROP(qint32, minHeight, MinHeight)
|
||||
FWDPROP(QString, minOfficialRating, MinOfficialRating)
|
||||
FWDPROP(QDateTime, minPremiereDate, MinPremiereDate)
|
||||
FWDPROP(qint32, minWidth, MinWidth)
|
||||
FWDPROP(QString, sortBy, SortBy)
|
||||
FWDPROP(QString, sortOrder, SortOrder)
|
||||
FWDPROP(QStringList, tags, Tags)
|
||||
FWDPROP(QList<qint32>, years, Years)
|
||||
|
||||
FWDPROP(QString, parentId, ParentId)
|
||||
FWDPROP(bool, recursive, Recursive)
|
||||
FWDPROP(QString, searchTerm, SearchTerm)
|
||||
//FWDPROP(bool, collapseBoxSetItems)
|
||||
};
|
||||
|
||||
using ResumeItemsLoaderBase = AbstractUserParameterLoader<Model::Item, DTO::BaseItemDto, DTO::BaseItemDtoQueryResult, Jellyfin::Loader::GetResumeItemsParams>;
|
||||
class ResumeItemsLoader : public ResumeItemsLoaderBase {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ResumeItemsLoader(QObject *parent = nullptr);
|
||||
|
||||
FWDPROP(QList<Jellyfin::DTO::ImageTypeClass::Value>, enableImageTypes, EnableImageTypes);
|
||||
FWDPROP(bool, enableImages, EnableImages)
|
||||
FWDPROP(bool, enableTotalRecordCount, EnableTotalRecordCount)
|
||||
FWDPROP(bool, enableUserData, EnableUserData)
|
||||
FWDPROP(QStringList, excludeItemTypes, ExcludeItemTypes)
|
||||
FWDPROP(QList<Jellyfin::DTO::ItemFieldsClass::Value>, fields, Fields)
|
||||
FWDPROP(qint32, imageTypeLimit, ImageTypeLimit)
|
||||
FWDPROP(QStringList, includeItemTypes, IncludeItemTypes)
|
||||
FWDPROP(QStringList, mediaTypes, MediaTypes)
|
||||
FWDPROP(QString, parentId, ParentId)
|
||||
FWDPROP(QString, searchTerm, SearchTerm)
|
||||
};
|
||||
|
||||
using ShowSeasonsLoaderBase = AbstractUserParameterLoader<Model::Item, DTO::BaseItemDto, DTO::BaseItemDtoQueryResult, Jellyfin::Loader::GetSeasonsParams>;
|
||||
class ShowSeasonsLoader : public ShowSeasonsLoaderBase {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ShowSeasonsLoader(QObject *parent = nullptr);
|
||||
|
||||
FWDPROP(QString, seriesId, SeriesId)
|
||||
FWDPROP(QString, adjacentTo, AdjacentTo)
|
||||
FWDPROP(QList<Jellyfin::DTO::ImageTypeClass::Value>, enableImageTypes, EnableImageTypes)
|
||||
FWDPROP(bool, enableImages, EnableImages)
|
||||
FWDPROP(bool, enableUserData, EnableUserData)
|
||||
FWDPROP(QList<Jellyfin::DTO::ItemFieldsClass::Value>, fields, Fields)
|
||||
FWDPROP(qint32, imageTypeLimit, ImageTypeLimit)
|
||||
FWDPROP(bool, isMissing, IsMissing)
|
||||
FWDPROP(bool, isSpecialSeason, IsSpecialSeason)
|
||||
|
||||
};
|
||||
|
||||
using ShowEpisodesLoaderBase = AbstractUserParameterLoader<Model::Item, DTO::BaseItemDto, DTO::BaseItemDtoQueryResult, Jellyfin::Loader::GetEpisodesParams>;
|
||||
class ShowEpisodesLoader : public ShowEpisodesLoaderBase {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ShowEpisodesLoader(QObject *parent = nullptr);
|
||||
|
||||
FWDPROP(QString, seriesId, SeriesId)
|
||||
FWDPROP(QString, adjacentTo, AdjacentTo)
|
||||
FWDPROP(bool, enableImages, EnableImages)
|
||||
FWDPROP(bool, enableUserData, EnableUserData)
|
||||
FWDPROP(QList<Jellyfin::DTO::ItemFieldsClass::Value>, fields, Fields)
|
||||
FWDPROP(qint32, imageTypeLimit, ImageTypeLimit)
|
||||
FWDPROP(bool, isMissing, IsMissing)
|
||||
FWDPROP(qint32, season, Season)
|
||||
FWDPROP(QString, seasonId, SeasonId)
|
||||
FWDPROP(QString, sortBy, SortBy)
|
||||
FWDPROP(QString, startItemId, StartItemId)
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
|
@ -167,6 +263,11 @@ public:
|
|||
mediaType,
|
||||
type,
|
||||
collectionType,
|
||||
indexNumber,
|
||||
runTimeTicks,
|
||||
artists,
|
||||
isFolder,
|
||||
parentIndexNumber,
|
||||
|
||||
jellyfinExtendModelAfterHere = Qt::UserRole + 300 // Should be enough for now
|
||||
};
|
||||
|
@ -191,6 +292,11 @@ public:
|
|||
JFRN(mediaType),
|
||||
JFRN(type),
|
||||
JFRN(collectionType),
|
||||
JFRN(indexNumber),
|
||||
JFRN(runTimeTicks),
|
||||
JFRN(artists),
|
||||
JFRN(isFolder),
|
||||
JFRN(parentIndexNumber),
|
||||
};
|
||||
}
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
|
|
|
@ -91,6 +91,7 @@ public:
|
|||
Q_PROPERTY(QMediaPlayer::Error error READ error NOTIFY errorChanged)
|
||||
Q_PROPERTY(QString errorString READ errorString NOTIFY errorStringChanged)
|
||||
Q_PROPERTY(bool hasVideo READ hasVideo NOTIFY hasVideoChanged)
|
||||
Q_PROPERTY(bool seekable READ seekable NOTIFY seekableChanged)
|
||||
Q_PROPERTY(QObject* mediaObject READ mediaObject NOTIFY mediaObjectChanged)
|
||||
Q_PROPERTY(QMediaPlayer::MediaStatus mediaStatus READ mediaStatus NOTIFY mediaStatusChanged)
|
||||
Q_PROPERTY(QMediaPlayer::State playbackState READ playbackState NOTIFY playbackStateChanged)
|
||||
|
@ -108,9 +109,10 @@ public:
|
|||
int queueIndex() const { return m_queueIndex; }
|
||||
|
||||
// Current media player related property getters
|
||||
QMediaPlayer::State playbackState() const { return m_playbackState; }
|
||||
QMediaPlayer::State playbackState() const { return m_mediaPlayer->state()/*m_playbackState*/; }
|
||||
QMediaPlayer::MediaStatus mediaStatus() const { return m_mediaPlayer->mediaStatus(); }
|
||||
bool hasVideo() const { return m_mediaPlayer->isVideoAvailable(); }
|
||||
bool seekable() const { return m_mediaPlayer->isSeekable(); }
|
||||
QMediaPlayer::Error error () const { return m_mediaPlayer->error(); }
|
||||
QString errorString() const { return m_mediaPlayer->errorString(); }
|
||||
signals:
|
||||
|
@ -132,6 +134,7 @@ signals:
|
|||
void playbackStateChanged(QMediaPlayer::State newState);
|
||||
void mediaStatusChanged(QMediaPlayer::MediaStatus newMediaStatus);
|
||||
void hasVideoChanged(bool newHasVideo);
|
||||
void seekableChanged(bool newSeekable);
|
||||
void errorChanged(QMediaPlayer::Error newError);
|
||||
void errorStringChanged(const QString &newErrorString);
|
||||
public slots:
|
||||
|
|
81
core/include/JellyfinQt/viewmodel/userdata.h
Normal file
81
core/include/JellyfinQt/viewmodel/userdata.h
Normal file
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* 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_VIEWMODEL_USERDATA_H
|
||||
#define JELLYFIN_VIEWMODEL_USERDATA_H
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QObject>
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include "../dto/useritemdatadto.h"
|
||||
|
||||
namespace Jellyfin {
|
||||
namespace ViewModel{
|
||||
|
||||
class UserData : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit UserData(QObject* parent = nullptr);
|
||||
explicit UserData(QSharedPointer<DTO::UserItemDataDto> data, QObject* parent = nullptr);
|
||||
|
||||
void setData(QSharedPointer<DTO::UserItemDataDto> data);
|
||||
|
||||
Q_PROPERTY(double rating READ rating NOTIFY ratingChanged)
|
||||
Q_PROPERTY(double playedPercentage READ playedPercentage NOTIFY playedPercentageChanged)
|
||||
Q_PROPERTY(int unplayedItemCount READ unplayedItemCount NOTIFY unplayedItemCountChanged)
|
||||
Q_PROPERTY(qint64 playbackPositionTicks READ playbackPositionTicks NOTIFY playbackPositionTicksChanged);
|
||||
Q_PROPERTY(int playCount READ playCount NOTIFY playCountChanged)
|
||||
Q_PROPERTY(bool favorite READ favorite NOTIFY favoriteChanged)
|
||||
Q_PROPERTY(bool m_likes READ likes NOTIFY likesChanged)
|
||||
Q_PROPERTY(QDateTime lastPlayedDate READ lastPlayedDate NOTIFY lastPlayedDateChanged)
|
||||
Q_PROPERTY(bool played READ played NOTIFY playedChanged)
|
||||
Q_PROPERTY(QString key READ key NOTIFY keyChanged)
|
||||
|
||||
double rating() const { return m_data->rating().value_or(0); }
|
||||
double playedPercentage() const { return m_data->playedPercentage().value_or(0); }
|
||||
int unplayedItemCount() const { return m_data->unplayedItemCount().value_or(0); }
|
||||
qint64 playbackPositionTicks() const { return m_data->playbackPositionTicks(); }
|
||||
int playCount() const { return m_data->playCount(); }
|
||||
bool favorite() const { return m_data->isFavorite(); }
|
||||
bool likes() const { return m_data->likes().value_or(false); }
|
||||
QDateTime lastPlayedDate() const { return m_data->lastPlayedDate(); }
|
||||
bool played() const { return m_data->played(); }
|
||||
QString key() const { return m_data->key(); }
|
||||
|
||||
signals:
|
||||
void ratingChanged(double newRating);
|
||||
void playedPercentageChanged(double newPlayedPercentage);
|
||||
void unplayedItemCountChanged(int newUnplayedItemCount);
|
||||
void playbackPositionTicksChanged(qint64 newPlaybackPositionTicks);
|
||||
void playCountChanged(int newPlayCount);
|
||||
void favoriteChanged(bool newFavorite);
|
||||
void likesChanged(bool newLikes);
|
||||
void lastPlayedDateChanged(QDateTime newLastPlayedDate);
|
||||
void playedChanged(bool newPLayed);
|
||||
void keyChanged(QString newKey);
|
||||
|
||||
private:
|
||||
QSharedPointer<DTO::UserItemDataDto> m_data;
|
||||
};
|
||||
|
||||
|
||||
} // NS ViewModel
|
||||
} // NS Jellyfin
|
||||
|
||||
#endif // JELLYFIN_VIEWMODEL_USERDATA_H
|
|
@ -35,6 +35,7 @@ ApiClient::ApiClient(QObject *parent)
|
|||
m_credManager = CredentialsManager::newInstance(this);
|
||||
connect(m_credManager, &CredentialsManager::serversListed, this, &ApiClient::credManagerServersListed);
|
||||
connect(m_credManager, &CredentialsManager::usersListed, this, &ApiClient::credManagerUsersListed);
|
||||
connect(m_credManager, &CredentialsManager::tokenRetrieved, this, &ApiClient::credManagerTokenRetrieved);
|
||||
generateDeviceProfile();
|
||||
}
|
||||
|
||||
|
@ -118,20 +119,14 @@ void ApiClient::credManagerUsersListed(const QString &server, QStringList users)
|
|||
QString user = users[0];
|
||||
qDebug() << "Chosen user: " << user;
|
||||
|
||||
QObject *ctx3 = new QObject(this);
|
||||
connect(m_credManager, &CredentialsManager::tokenRetrieved, ctx3, [this, ctx3]
|
||||
(const QString &server, const QString &user, const QString &token) {
|
||||
Q_UNUSED(server)
|
||||
this->m_token = token;
|
||||
this->setUserId(user);
|
||||
this->setAuthenticated(true);
|
||||
this->postCapabilities();
|
||||
disconnect(ctx3);
|
||||
}, Qt::UniqueConnection);
|
||||
m_credManager->get(server, user);
|
||||
}
|
||||
void ApiClient::credManagerTokenRetrieved(const QString &server, const QString &user, const QString &token) {
|
||||
|
||||
this->m_token = token;
|
||||
qDebug() << "Token retreived, logged in as user " << user;
|
||||
this->setUserId(user);
|
||||
this->setAuthenticated(true);
|
||||
this->postCapabilities();
|
||||
}
|
||||
|
||||
void ApiClient::setupConnection() {
|
||||
|
|
|
@ -71,6 +71,10 @@ void BaseModelLoader::setAutoReload(bool newAutoReload) {
|
|||
if (m_autoReload != newAutoReload) {
|
||||
m_autoReload = newAutoReload;
|
||||
emit autoReloadChanged(newAutoReload);
|
||||
|
||||
if (canReload()) {
|
||||
reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,11 +42,6 @@ QString FallbackCredentialsManager::urlToGroupName(const QString &url) const {
|
|||
return QString::number(qHash(url), 16);
|
||||
}
|
||||
|
||||
QString FallbackCredentialsManager::groupNameToUrl(const QString &group) const {
|
||||
QString tmp = QString(group);
|
||||
return tmp.replace('|', "/");
|
||||
}
|
||||
|
||||
void FallbackCredentialsManager::store(const QString &server, const QString &user, const QString &token) {
|
||||
m_settings.setValue(urlToGroupName(server) + "/users/" + user + "/accessToken", token);
|
||||
m_settings.setValue(urlToGroupName(server) + "/address", server);
|
||||
|
|
|
@ -38,11 +38,15 @@ void registerTypes(const char *uri) {
|
|||
qmlRegisterType<ViewModel::LatestMediaLoader>(uri, 1, 0, "LatestMediaLoader");
|
||||
qmlRegisterType<ViewModel::UserItemsLoader>(uri, 1, 0, "UserItemsLoader");
|
||||
qmlRegisterType<ViewModel::UserViewsLoader>(uri, 1, 0, "UsersViewsLoader");
|
||||
qmlRegisterType<ViewModel::ResumeItemsLoader>(uri, 1, 0, "ResumeItemsLoader");
|
||||
qmlRegisterType<ViewModel::ShowSeasonsLoader>(uri, 1, 0, "ShowSeasonsLoader");
|
||||
qmlRegisterType<ViewModel::ShowEpisodesLoader>(uri, 1, 0, "ShowEpisodesLoader");
|
||||
|
||||
// Enumerations
|
||||
qmlRegisterUncreatableType<Jellyfin::DTO::GeneralCommandTypeClass>(uri, 1, 0, "GeneralCommandType", "Is an enum");
|
||||
qmlRegisterUncreatableType<Jellyfin::ViewModel::ModelStatusClass>(uri, 1, 0, "ModelStatus", "Is an enum");
|
||||
qmlRegisterUncreatableType<Jellyfin::DTO::PlayMethodClass>(uri, 1, 0, "PlayMethod", "Is an enum");
|
||||
qmlRegisterUncreatableType<Jellyfin::DTO::ItemFieldsClass>(uri, 1, 0, "ItemFields", "Is an enum");
|
||||
|
||||
qRegisterMetaType<Jellyfin::DTO::PlayMethodClass::Value>();
|
||||
}
|
||||
|
|
|
@ -23,25 +23,14 @@ namespace Jellyfin {
|
|||
namespace Model {
|
||||
|
||||
|
||||
Item::Item()
|
||||
: Item(DTO::BaseItemDto(), nullptr) { }
|
||||
Item::Item(QObject *parent)
|
||||
: Item(DTO::BaseItemDto(), nullptr, parent) { }
|
||||
|
||||
Item::Item(const DTO::BaseItemDto &data, ApiClient *apiClient)
|
||||
: DTO::BaseItemDto(data), m_apiClient(apiClient) {
|
||||
if (m_apiClient != nullptr) {
|
||||
m_apiClientConnections.append(QObject::connect(
|
||||
m_apiClient->eventbus(),
|
||||
&EventBus::itemUserDataUpdated,
|
||||
[&](auto itemId, auto userData) {
|
||||
onUserDataUpdated(itemId, userData);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
Item::~Item() {
|
||||
for(auto it = m_apiClientConnections.begin(); it != m_apiClientConnections.end(); it++) {
|
||||
QObject::disconnect(*it);
|
||||
}
|
||||
Item::Item(const DTO::BaseItemDto &data, ApiClient *apiClient, QObject *parent)
|
||||
: DTO::BaseItemDto(data),
|
||||
QObject(parent),
|
||||
m_apiClient(nullptr) {
|
||||
setApiClient(apiClient);
|
||||
}
|
||||
|
||||
bool Item::sameAs(const DTO::BaseItemDto &other) {
|
||||
|
@ -50,23 +39,20 @@ bool Item::sameAs(const DTO::BaseItemDto &other) {
|
|||
|
||||
void Item::setApiClient(ApiClient *apiClient) {
|
||||
if (m_apiClient != nullptr) {
|
||||
for(auto it = m_apiClientConnections.begin(); it != m_apiClientConnections.end(); it++) {
|
||||
QObject::disconnect(*it);
|
||||
}
|
||||
m_apiClientConnections.clear();
|
||||
disconnect(m_apiClient->eventbus(), &EventBus::itemUserDataUpdated,
|
||||
this, &Item::updateUserData);
|
||||
}
|
||||
this->m_apiClient = apiClient;
|
||||
if (apiClient != nullptr) {
|
||||
m_apiClientConnections.append(QObject::connect(
|
||||
m_apiClient->eventbus(),
|
||||
&EventBus::itemUserDataUpdated,
|
||||
[&](auto itemId, auto userData) {
|
||||
onUserDataUpdated(itemId, userData);
|
||||
}));
|
||||
QObject::connect(m_apiClient->eventbus(), &EventBus::itemUserDataUpdated,
|
||||
this, &Item::updateUserData);
|
||||
}
|
||||
}
|
||||
void Item::onUserDataUpdated(const QString &itemId, const DTO::UserItemDataDto &userData) {
|
||||
|
||||
void Item::updateUserData(const QString &itemId, const DTO::UserItemDataDto &userData) {
|
||||
if (itemId == this->jellyfinId()) {
|
||||
emit userDataChanged(userData);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -103,12 +103,12 @@ QSharedPointer<Item> Playlist::nextItem() {
|
|||
return m_nextItem;
|
||||
}
|
||||
|
||||
void Playlist::appendToList(const ViewModel::ItemModel &model) {
|
||||
void Playlist::appendToList(ViewModel::ItemModel &model) {
|
||||
int start = m_list.size();
|
||||
int count = model.size();
|
||||
m_list.reserve(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
m_list.append(QSharedPointer<Model::Item>::create(model.at(i)));
|
||||
m_list.append(QSharedPointer<Model::Item>(model.at(i)));
|
||||
}
|
||||
emit itemsAddedToList(start, count);
|
||||
reshuffle();
|
||||
|
|
|
@ -17,17 +17,39 @@
|
|||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include "JellyfinQt/viewmodel/item.h"
|
||||
#include "JellyfinQt/viewmodel/userdata.h"
|
||||
|
||||
namespace Jellyfin {
|
||||
namespace ViewModel {
|
||||
|
||||
Item::Item(QObject *parent, QSharedPointer<Model::Item> data)
|
||||
: QObject(parent), m_data(data){
|
||||
|
||||
: QObject(parent),
|
||||
m_data(data),
|
||||
m_userData(new UserData(this)){
|
||||
connect(m_data.data(), &Model::Item::userDataChanged, this, &Item::onUserDataChanged);
|
||||
}
|
||||
|
||||
void Item::setData(QSharedPointer<Model::Item> newData) {
|
||||
if (!m_data.isNull()) {
|
||||
disconnect(m_data.data(), &Model::Item::userDataChanged, this, &Item::onUserDataChanged);
|
||||
}
|
||||
m_data = newData;
|
||||
if (!m_data.isNull()) {
|
||||
connect(m_data.data(), &Model::Item::userDataChanged, this, &Item::onUserDataChanged);
|
||||
}
|
||||
}
|
||||
|
||||
void Item::setUserData(DTO::UserItemDataDto &newData) {
|
||||
setUserData(QSharedPointer<DTO::UserItemDataDto>::create(newData));
|
||||
}
|
||||
|
||||
void Item::setUserData(QSharedPointer<DTO::UserItemDataDto> newData) {
|
||||
m_userData->setData(newData);
|
||||
emit userDataChanged(m_userData);
|
||||
}
|
||||
|
||||
void Item::onUserDataChanged(const DTO::UserItemDataDto &newData) {
|
||||
setUserData(QSharedPointer<DTO::UserItemDataDto>::create(newData));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -18,12 +18,15 @@
|
|||
*/
|
||||
#include "JellyfinQt/viewmodel/itemmodel.h"
|
||||
|
||||
#include "JellyfinQt/loader/http/getepisodes.h"
|
||||
#include "JellyfinQt/loader/http/getlatestmedia.h"
|
||||
#include "JellyfinQt/loader/http/getitemsbyuserid.h"
|
||||
#include "JellyfinQt/loader/http/getresumeitems.h"
|
||||
#include "JellyfinQt/loader/http/getseasons.h"
|
||||
|
||||
#define JF_CASE(roleName) case roleName: \
|
||||
try { \
|
||||
return QVariant(item.roleName()); \
|
||||
return QVariant(item->roleName()); \
|
||||
} catch(std::bad_optional_access &e) { \
|
||||
return QVariant(); \
|
||||
}
|
||||
|
@ -41,6 +44,15 @@ LatestMediaLoader::LatestMediaLoader(QObject *parent)
|
|||
UserItemsLoader::UserItemsLoader(QObject *parent)
|
||||
: UserItemsLoaderBase(new Jellyfin::Loader::HTTP::GetItemsByUserIdLoader(), parent) {}
|
||||
|
||||
ResumeItemsLoader::ResumeItemsLoader(QObject *parent)
|
||||
: ResumeItemsLoaderBase(new Jellyfin::Loader::HTTP::GetResumeItemsLoader(), parent) {}
|
||||
|
||||
ShowSeasonsLoader::ShowSeasonsLoader(QObject *parent)
|
||||
: ShowSeasonsLoaderBase(new Jellyfin::Loader::HTTP::GetSeasonsLoader(), parent) {}
|
||||
|
||||
ShowEpisodesLoader::ShowEpisodesLoader(QObject *parent)
|
||||
: ShowEpisodesLoaderBase(new Jellyfin::Loader::HTTP::GetEpisodesLoader(), parent) {}
|
||||
|
||||
ItemModel::ItemModel(QObject *parent)
|
||||
: ApiModel<Model::Item>(parent) { }
|
||||
|
||||
|
@ -48,7 +60,7 @@ QVariant ItemModel::data(const QModelIndex &index, int role) const {
|
|||
if (role <= Qt::UserRole || !index.isValid()) return QVariant();
|
||||
int row = index.row();
|
||||
if (row < 0 || row >= m_array.size()) return QVariant();
|
||||
Model::Item item = m_array[row];
|
||||
QSharedPointer<Model::Item> item = m_array[row];
|
||||
switch(role) {
|
||||
JF_CASE(jellyfinId)
|
||||
JF_CASE(name)
|
||||
|
@ -66,6 +78,15 @@ QVariant ItemModel::data(const QModelIndex &index, int role) const {
|
|||
JF_CASE(mediaType)
|
||||
JF_CASE(type)
|
||||
JF_CASE(collectionType)
|
||||
case RoleNames::indexNumber:
|
||||
return QVariant(item->indexNumber().value_or(0));
|
||||
case RoleNames::runTimeTicks:
|
||||
return QVariant(item->runTimeTicks().value_or(0));
|
||||
JF_CASE(artists)
|
||||
case RoleNames::isFolder:
|
||||
return QVariant(item->isFolder().value_or(false));
|
||||
case RoleNames::parentIndexNumber:
|
||||
return QVariant(item->parentIndexNumber().value_or(1));
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
|
@ -73,7 +94,7 @@ QVariant ItemModel::data(const QModelIndex &index, int role) const {
|
|||
}
|
||||
|
||||
QSharedPointer<Model::Item> ItemModel::itemAt(int index) {
|
||||
return QSharedPointer<Model::Item>::create(m_array[index]);
|
||||
return m_array[index];
|
||||
}
|
||||
|
||||
} // NS ViewModel
|
||||
|
|
|
@ -56,6 +56,7 @@ PlaybackManager::PlaybackManager(QObject *parent)
|
|||
connect(m_mediaPlayer, &QMediaPlayer::durationChanged, this, &PlaybackManager::mediaPlayerDurationChanged);
|
||||
connect(m_mediaPlayer, &QMediaPlayer::mediaStatusChanged, this, &PlaybackManager::mediaPlayerMediaStatusChanged);
|
||||
connect(m_mediaPlayer, &QMediaPlayer::videoAvailableChanged, this, &PlaybackManager::hasVideoChanged);
|
||||
connect(m_mediaPlayer, &QMediaPlayer::seekableChanged, this, &PlaybackManager::seekableChanged);
|
||||
// I do not like the complicated overload cast
|
||||
connect(m_mediaPlayer, SIGNAL(error(QMediaPlayer::Error)), this, SLOT(mediaPlayerError(QMediaPlayer::Error)));
|
||||
|
||||
|
@ -142,6 +143,7 @@ void PlaybackManager::mediaPlayerStateChanged(QMediaPlayer::State newState) {
|
|||
postPlaybackInfo(Progress);
|
||||
}
|
||||
m_oldState = newState;
|
||||
emit playbackStateChanged(newState);
|
||||
}
|
||||
|
||||
void PlaybackManager::mediaPlayerMediaStatusChanged(QMediaPlayer::MediaStatus newStatus) {
|
||||
|
@ -352,6 +354,9 @@ void ItemUrlFetcherThread::run() {
|
|||
playMethod = PlayMethod::DirectPlay;
|
||||
} else if (source.supportsDirectStream()) {
|
||||
QString mediaType = item->mediaType();
|
||||
if (mediaType == "Video") {
|
||||
mediaType.append('s');
|
||||
}
|
||||
QUrlQuery query;
|
||||
query.addQueryItem("mediaSourceId", source.jellyfinId());
|
||||
query.addQueryItem("deviceId", m_parent->m_apiClient->deviceId());
|
||||
|
|
39
core/src/viewmodel/userdata.cpp
Normal file
39
core/src/viewmodel/userdata.cpp
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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
|
||||
*/
|
||||
#include <JellyfinQt/viewmodel/userdata.h>
|
||||
|
||||
namespace Jellyfin {
|
||||
namespace ViewModel {
|
||||
|
||||
UserData::UserData(QObject *parent)
|
||||
: QObject(parent),
|
||||
m_data(QSharedPointer<DTO::UserItemDataDto>::create()) {
|
||||
}
|
||||
|
||||
UserData::UserData(QSharedPointer<DTO::UserItemDataDto> data, QObject *parent)
|
||||
: QObject(parent),
|
||||
m_data(data) {
|
||||
}
|
||||
|
||||
void UserData::setData(QSharedPointer<DTO::UserItemDataDto> data) {
|
||||
m_data = data;
|
||||
}
|
||||
|
||||
} // NS ViewModel
|
||||
} // NS Jellyfin
|
Loading…
Add table
Add a link
Reference in a new issue