mirror of
https://github.com/HenkKalkwater/harbour-sailfin.git
synced 2024-11-22 09:15:18 +00:00
WIP: Slowly bringing back viewmodels
This commit is contained in:
parent
9abee12658
commit
228f81984b
|
@ -168,7 +168,7 @@ public:
|
||||||
m_startIndex = 0;
|
m_startIndex = 0;
|
||||||
m_totalRecordCount = -1;
|
m_totalRecordCount = -1;
|
||||||
emitModelShouldClear();
|
emitModelShouldClear();
|
||||||
loadMore(0, -1, ViewModel::ModelStatus::Loading);
|
loadMore(0, m_limit, ViewModel::ModelStatus::Loading);
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadMore() {
|
void loadMore() {
|
||||||
|
@ -272,7 +272,9 @@ protected:
|
||||||
// meaning loadMore is not supported.
|
// meaning loadMore is not supported.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setRequestLimit<P>(this->m_parameters, limit);
|
if (limit > 0) {
|
||||||
|
setRequestLimit<P>(this->m_parameters, limit);
|
||||||
|
}
|
||||||
this->setStatus(suggestedModelStatus);
|
this->setStatus(suggestedModelStatus);
|
||||||
|
|
||||||
// We never want to set this while the loader is running, hence the Mutex and setting it here
|
// We never want to set this while the loader is running, hence the Mutex and setting it here
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include "apiclient.h"
|
#include "apiclient.h"
|
||||||
#include "apimodel.h"
|
#include "apimodel.h"
|
||||||
#include "serverdiscoverymodel.h"
|
#include "serverdiscoverymodel.h"
|
||||||
|
#include "websocket.h"
|
||||||
#include "viewmodel/item.h"
|
#include "viewmodel/item.h"
|
||||||
#include "viewmodel/itemmodel.h"
|
#include "viewmodel/itemmodel.h"
|
||||||
#include "viewmodel/loader.h"
|
#include "viewmodel/loader.h"
|
||||||
|
|
|
@ -33,6 +33,16 @@ namespace Model {
|
||||||
|
|
||||||
class Item : public DTO::BaseItemDto {
|
class Item : public DTO::BaseItemDto {
|
||||||
public:
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Constructor that creates an empty item.
|
||||||
|
*/
|
||||||
|
Item();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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);
|
Item(const DTO::BaseItemDto &data, ApiClient *apiClient = nullptr);
|
||||||
virtual ~Item();
|
virtual ~Item();
|
||||||
|
|
||||||
|
|
|
@ -152,9 +152,24 @@ QJsonValue toJsonValue(const QSharedPointer<T> &source, convertType<QSharedPoint
|
||||||
/**
|
/**
|
||||||
* Templates for string conversion.
|
* 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>
|
template <typename T>
|
||||||
QString toString(const T &source) {
|
QString toString(const T &source) {
|
||||||
return toJsonValue(source).toString();
|
return toString(source, convertType<T>{});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -51,9 +51,7 @@ namespace ViewModel {
|
||||||
class Item : public QObject {
|
class Item : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit Item(QObject *parent = nullptr);
|
explicit Item(QObject *parent = nullptr, QSharedPointer<Model::Item> data = QSharedPointer<Model::Item>::create());
|
||||||
explicit Item(QSharedPointer<Model::Item> data = QSharedPointer<Model::Item>(),
|
|
||||||
QObject *parent = nullptr);
|
|
||||||
|
|
||||||
// Please keep the order of the properties the same as in the file linked above.
|
// 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(QUuid jellyfinId READ jellyfinId NOTIFY jellyfinIdChanged)
|
||||||
|
@ -194,7 +192,7 @@ public:
|
||||||
Q_PROPERTY(QString itemId READ itemId WRITE setItemId NOTIFY itemIdChanged)
|
Q_PROPERTY(QString itemId READ itemId WRITE setItemId NOTIFY itemIdChanged)
|
||||||
|
|
||||||
QString itemId() const { return m_parameters.itemId(); }
|
QString itemId() const { return m_parameters.itemId(); }
|
||||||
void setItemId(QString newItemId) { m_parameters.setItemId(newItemId); }
|
void setItemId(QString newItemId) { m_parameters.setItemId(newItemId); emit itemIdChanged(newItemId); }
|
||||||
virtual bool canReload() const override;
|
virtual bool canReload() const override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
|
@ -44,7 +44,44 @@ namespace ViewModel {
|
||||||
|
|
||||||
// This file contains all models that expose a Model::Item
|
// This file contains all models that expose a Model::Item
|
||||||
|
|
||||||
using UserViewsLoaderBase = LoaderModelLoader<Model::Item, DTO::BaseItemDto, DTO::BaseItemDtoQueryResult, Jellyfin::Loader::GetUserViewsParams>;
|
/**
|
||||||
|
* @brief Class intended for models which have a mandatory userId property, which can be extracted from the
|
||||||
|
* ApiClient.
|
||||||
|
*/
|
||||||
|
template <class T, class D, class R, class P>
|
||||||
|
class AbstractUserParameterLoader : public LoaderModelLoader<T, D, R, P> {
|
||||||
|
public:
|
||||||
|
explicit AbstractUserParameterLoader(Support::Loader<R, P> *loader, QObject *parent = nullptr)
|
||||||
|
: LoaderModelLoader<T, D, R, P>(loader, parent) {
|
||||||
|
this->connect(this, &BaseModelLoader::apiClientChanged, this, &AbstractUserParameterLoader<T, D, R, P>::apiClientChanged);
|
||||||
|
}
|
||||||
|
protected:
|
||||||
|
virtual bool canReload() const override {
|
||||||
|
return BaseModelLoader::canReload() && !this->m_parameters.userId().isNull();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
void apiClientChanged(ApiClient *newApiClient) {
|
||||||
|
if (this->m_apiClient != nullptr) {
|
||||||
|
this->disconnect(this->m_apiClient, &ApiClient::userIdChanged, this, &AbstractUserParameterLoader<T, D, R, P>::userIdChanged);
|
||||||
|
}
|
||||||
|
if (newApiClient != nullptr) {
|
||||||
|
this->connect(newApiClient, &ApiClient::userIdChanged, this, &AbstractUserParameterLoader<T, D, R, P>::userIdChanged);
|
||||||
|
if (!newApiClient->userId().isNull()) {
|
||||||
|
this->m_parameters.setUserId(newApiClient->userId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void userIdChanged(const QString &newUserId) {
|
||||||
|
this->m_parameters.setUserId(newUserId);
|
||||||
|
this->autoReloadIfNeeded();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the views of an user, such as "Videos", "Music" and so on.
|
||||||
|
*/
|
||||||
|
using UserViewsLoaderBase = AbstractUserParameterLoader<Model::Item, DTO::BaseItemDto, DTO::BaseItemDtoQueryResult, Jellyfin::Loader::GetUserViewsParams>;
|
||||||
class UserViewsLoader : public UserViewsLoaderBase {
|
class UserViewsLoader : public UserViewsLoaderBase {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
@ -53,12 +90,27 @@ public:
|
||||||
FWDPROP(bool, includeExternalContent, IncludeExternalContent)
|
FWDPROP(bool, includeExternalContent, IncludeExternalContent)
|
||||||
FWDPROP(bool, includeHidden, IncludeHidden)
|
FWDPROP(bool, includeHidden, IncludeHidden)
|
||||||
FWDPROP(QStringList, presetViews, PresetViews)
|
FWDPROP(QStringList, presetViews, PresetViews)
|
||||||
private slots:
|
|
||||||
void apiClientChanged(ApiClient *newApiClient);
|
|
||||||
void userIdChanged(const QString &newUserId);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
using UserItemsLoaderBase = LoaderModelLoader<Model::Item, DTO::BaseItemDto, DTO::BaseItemDtoQueryResult, Jellyfin::Loader::GetItemsByUserIdParams>;
|
using LatestMediaBase = AbstractUserParameterLoader<Model::Item, DTO::BaseItemDto, QList<DTO::BaseItemDto>, Jellyfin::Loader::GetLatestMediaParams>;
|
||||||
|
class LatestMediaLoader : public LatestMediaBase {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit LatestMediaLoader(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
// Optional
|
||||||
|
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(bool, groupItems, GroupItems)
|
||||||
|
FWDPROP(qint32, imageTypeLimit, ImageTypeLimit)
|
||||||
|
FWDPROP(QStringList, includeItemTypes, IncludeItemTypes)
|
||||||
|
FWDPROP(bool, isPlayed, IsPlayed)
|
||||||
|
FWDPROP(QString, parentId, ParentId)
|
||||||
|
};
|
||||||
|
|
||||||
|
using UserItemsLoaderBase = AbstractUserParameterLoader<Model::Item, DTO::BaseItemDto, DTO::BaseItemDtoQueryResult, Jellyfin::Loader::GetItemsByUserIdParams>;
|
||||||
class UserItemsLoader : public UserItemsLoaderBase {
|
class UserItemsLoader : public UserItemsLoaderBase {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
@ -70,14 +122,26 @@ public:
|
||||||
FWDPROP(QStringList, albums, Albums)
|
FWDPROP(QStringList, albums, Albums)
|
||||||
FWDPROP(QStringList, artistIds, ArtistIds)
|
FWDPROP(QStringList, artistIds, ArtistIds)
|
||||||
FWDPROP(QStringList, artists, Artists)
|
FWDPROP(QStringList, artists, Artists)
|
||||||
|
FWDPROP(bool, collapseBoxSetItems, CollapseBoxSetItems)
|
||||||
|
FWDPROP(QStringList, contributingArtistIds, ContributingArtistIds)
|
||||||
|
FWDPROP(QList<Jellyfin::DTO::ImageTypeClass::Value>, enableImageTypes, EnableImageTypes);
|
||||||
|
FWDPROP(bool, enableImages, EnableImages)
|
||||||
|
FWDPROP(bool, enableTotalRecordCount, EnableTotalRecordCount)
|
||||||
|
FWDPROP(bool, enableUserData, EnableUserData)
|
||||||
|
FWDPROP(QStringList, excludeArtistIds, ExcludeArtistIds)
|
||||||
|
FWDPROP(QStringList, excludeItemIds, ExcludeItemIds)
|
||||||
|
FWDPROP(QStringList, excludeItemTypes, ExcludeItemTypes)
|
||||||
|
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(QString, parentId, ParentId)
|
||||||
FWDPROP(bool, recursive, Recursive)
|
FWDPROP(bool, recursive, Recursive)
|
||||||
//FWDPROP(bool, collapseBoxSetItems)
|
//FWDPROP(bool, collapseBoxSetItems)
|
||||||
protected:
|
|
||||||
virtual bool canReload() const override;
|
|
||||||
private slots:
|
|
||||||
void apiClientChanged(ApiClient *newApiClient);
|
|
||||||
void userIdChanged(const QString &newUserId);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Base class for each model that works with items.
|
* @brief Base class for each model that works with items.
|
||||||
*/
|
*/
|
||||||
|
@ -95,7 +159,10 @@ public:
|
||||||
playlistItemId,
|
playlistItemId,
|
||||||
dateCreated,
|
dateCreated,
|
||||||
dateLastMediaAdded,
|
dateLastMediaAdded,
|
||||||
extraType
|
extraType,
|
||||||
|
|
||||||
|
// Hand-picked, important ones
|
||||||
|
imageTags
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit ItemModel (QObject *parent = nullptr);
|
explicit ItemModel (QObject *parent = nullptr);
|
||||||
|
@ -111,7 +178,9 @@ public:
|
||||||
JFRN(playlistItemId),
|
JFRN(playlistItemId),
|
||||||
JFRN(dateCreated),
|
JFRN(dateCreated),
|
||||||
JFRN(dateLastMediaAdded),
|
JFRN(dateLastMediaAdded),
|
||||||
JFRN(extraType)
|
JFRN(extraType),
|
||||||
|
// Handpicked, important ones
|
||||||
|
JFRN(imageTags),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
QVariant data(const QModelIndex &index, int role) const override;
|
QVariant data(const QModelIndex &index, int role) const override;
|
||||||
|
|
|
@ -144,13 +144,14 @@ template <class T, class R, class P>
|
||||||
class Loader : public LoaderBase {
|
class Loader : public LoaderBase {
|
||||||
using RFutureWatcher = QFutureWatcher<std::optional<R>>;
|
using RFutureWatcher = QFutureWatcher<std::optional<R>>;
|
||||||
public:
|
public:
|
||||||
Loader(Support::Loader<R, P> loaderImpl, QObject *parent = nullptr)
|
Loader(Support::Loader<R, P> *loaderImpl, QObject *parent = nullptr)
|
||||||
: Loader(nullptr, loaderImpl, parent) {}
|
: Loader(nullptr, loaderImpl, parent) {}
|
||||||
|
|
||||||
Loader(ApiClient *apiClient, Support::Loader<R, P> loaderImpl, QObject *parent = nullptr)
|
Loader(ApiClient *apiClient, Support::Loader<R, P> *loaderImpl, QObject *parent = nullptr)
|
||||||
: LoaderBase(apiClient, parent),
|
: LoaderBase(apiClient, parent),
|
||||||
m_loader(loaderImpl),
|
m_loader(loaderImpl),
|
||||||
m_futureWatcher(new QFutureWatcher<std::optional<R>>) {
|
m_futureWatcher(new QFutureWatcher<std::optional<R>>(this)) {
|
||||||
|
|
||||||
m_dataViewModel = new T(this);
|
m_dataViewModel = new T(this);
|
||||||
connect(m_futureWatcher, &RFutureWatcher::finished, this, &Loader<T, R, P>::updateData);
|
connect(m_futureWatcher, &RFutureWatcher::finished, this, &Loader<T, R, P>::updateData);
|
||||||
}
|
}
|
||||||
|
@ -161,8 +162,9 @@ public:
|
||||||
void reload() override {
|
void reload() override {
|
||||||
if (m_futureWatcher->isRunning()) return;
|
if (m_futureWatcher->isRunning()) return;
|
||||||
setStatus(Loading);
|
setStatus(Loading);
|
||||||
m_loader.setParameters(m_parameters);
|
this->m_loader->setApiClient(m_apiClient);
|
||||||
m_loader.prepareLoad();
|
m_loader->setParameters(m_parameters);
|
||||||
|
m_loader->prepareLoad();
|
||||||
QFuture<std::optional<R>> future = QtConcurrent::run(this, &Loader<T, R, P>::invokeLoader);
|
QFuture<std::optional<R>> future = QtConcurrent::run(this, &Loader<T, R, P>::invokeLoader);
|
||||||
m_futureWatcher->setFuture(future);
|
m_futureWatcher->setFuture(future);
|
||||||
}
|
}
|
||||||
|
@ -172,7 +174,7 @@ protected:
|
||||||
/**
|
/**
|
||||||
* @brief Subclasses should initialize this to a loader that actually loads stuff.
|
* @brief Subclasses should initialize this to a loader that actually loads stuff.
|
||||||
*/
|
*/
|
||||||
Support::Loader<R, P> m_loader;
|
QScopedPointer<Support::Loader<R, P>> m_loader = nullptr;
|
||||||
private:
|
private:
|
||||||
QFutureWatcher<std::optional<R>> *m_futureWatcher;
|
QFutureWatcher<std::optional<R>> *m_futureWatcher;
|
||||||
|
|
||||||
|
@ -184,9 +186,8 @@ private:
|
||||||
*/
|
*/
|
||||||
std::optional<R> invokeLoader() {
|
std::optional<R> invokeLoader() {
|
||||||
QMutexLocker(&this->m_mutex);
|
QMutexLocker(&this->m_mutex);
|
||||||
this->m_loader.setApiClient(m_apiClient);
|
|
||||||
try {
|
try {
|
||||||
return this->m_loader.load();
|
return this->m_loader->load();
|
||||||
} catch (Support::LoadException &e) {
|
} catch (Support::LoadException &e) {
|
||||||
qWarning() << "Exception while loading an item: " << e.what();
|
qWarning() << "Exception while loading an item: " << e.what();
|
||||||
this->setErrorString(QString(e.what()));
|
this->setErrorString(QString(e.what()));
|
||||||
|
@ -206,7 +207,7 @@ private:
|
||||||
} else {
|
} else {
|
||||||
// Replace the model
|
// Replace the model
|
||||||
using PointerType = typename decltype(m_dataViewModel->data())::Type;
|
using PointerType = typename decltype(m_dataViewModel->data())::Type;
|
||||||
m_dataViewModel = new T(QSharedPointer<PointerType>::create(newData), this);
|
m_dataViewModel = new T(this, QSharedPointer<PointerType>::create(newData, m_apiClient));
|
||||||
}
|
}
|
||||||
setStatus(Ready);
|
setStatus(Ready);
|
||||||
emitDataChanged();
|
emitDataChanged();
|
||||||
|
|
|
@ -80,7 +80,7 @@ public:
|
||||||
Q_PROPERTY(PlayMethod playMethod READ playMethod NOTIFY playMethodChanged)
|
Q_PROPERTY(PlayMethod playMethod READ playMethod NOTIFY playMethodChanged)
|
||||||
|
|
||||||
// Current Item and queue informatoion
|
// Current Item and queue informatoion
|
||||||
Q_PROPERTY(Model::Item *item READ item NOTIFY itemChanged)
|
Q_PROPERTY(ViewModel::Item *item READ item NOTIFY itemChanged)
|
||||||
Q_PROPERTY(QAbstractItemModel *queue READ queue NOTIFY queueChanged)
|
Q_PROPERTY(QAbstractItemModel *queue READ queue NOTIFY queueChanged)
|
||||||
Q_PROPERTY(int queueIndex READ queueIndex NOTIFY queueIndexChanged)
|
Q_PROPERTY(int queueIndex READ queueIndex NOTIFY queueIndexChanged)
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ public:
|
||||||
Q_PROPERTY(QMediaPlayer::State playbackState READ playbackState NOTIFY playbackStateChanged)
|
Q_PROPERTY(QMediaPlayer::State playbackState READ playbackState NOTIFY playbackStateChanged)
|
||||||
Q_PROPERTY(qint64 position READ position NOTIFY positionChanged)
|
Q_PROPERTY(qint64 position READ position NOTIFY positionChanged)
|
||||||
|
|
||||||
Model::Item *item() const { return m_item.data(); }
|
ViewModel::Item *item() const { return m_displayItem.get(); }
|
||||||
void setApiClient(ApiClient *apiClient);
|
void setApiClient(ApiClient *apiClient);
|
||||||
|
|
||||||
QString streamUrl() const { return m_streamUrl; }
|
QString streamUrl() const { return m_streamUrl; }
|
||||||
|
@ -169,6 +169,8 @@ private:
|
||||||
QTimer m_updateTimer;
|
QTimer m_updateTimer;
|
||||||
ApiClient *m_apiClient = nullptr;
|
ApiClient *m_apiClient = nullptr;
|
||||||
QSharedPointer<Model::Item> m_item;
|
QSharedPointer<Model::Item> m_item;
|
||||||
|
QScopedPointer<ViewModel::Item> m_displayItem = QScopedPointer<ViewModel::Item>(new ViewModel::Item());
|
||||||
|
|
||||||
QString m_streamUrl;
|
QString m_streamUrl;
|
||||||
QString m_playSessionId;
|
QString m_playSessionId;
|
||||||
int m_audioIndex = 0;
|
int m_audioIndex = 0;
|
||||||
|
|
|
@ -62,7 +62,7 @@ void BaseModelLoader::setApiClient(ApiClient *newApiClient) {
|
||||||
void BaseModelLoader::setLimit(int newLimit) {
|
void BaseModelLoader::setLimit(int newLimit) {
|
||||||
int oldLimit = this->m_limit;
|
int oldLimit = this->m_limit;
|
||||||
m_limit = newLimit;
|
m_limit = newLimit;
|
||||||
if (oldLimit != this->m_limit) {
|
if (oldLimit != newLimit) {
|
||||||
emit limitChanged(this->m_limit);
|
emit limitChanged(this->m_limit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,6 +76,7 @@ void BaseModelLoader::setAutoReload(bool newAutoReload) {
|
||||||
|
|
||||||
bool BaseModelLoader::canReload() const {
|
bool BaseModelLoader::canReload() const {
|
||||||
return m_apiClient != nullptr
|
return m_apiClient != nullptr
|
||||||
|
&& !m_isBeingParsed
|
||||||
// If the loader for this model needs authentication (almost every one does)
|
// If the loader for this model needs authentication (almost every one does)
|
||||||
// block if the ApiClient is not authenticated yet.
|
// block if the ApiClient is not authenticated yet.
|
||||||
&& (!m_needsAuthentication || m_apiClient->authenticated())
|
&& (!m_needsAuthentication || m_apiClient->authenticated())
|
||||||
|
@ -88,6 +89,8 @@ void BaseApiModel::reload() {
|
||||||
qWarning() << " BaseApiModel slot called instead of overloaded method";
|
qWarning() << " BaseApiModel slot called instead of overloaded method";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parameters injectors and result extractors
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
bool setRequestStartIndex(Loader::GetUserViewsParams ¶ms, int startIndex) {
|
bool setRequestStartIndex(Loader::GetUserViewsParams ¶ms, int startIndex) {
|
||||||
// Not supported
|
// Not supported
|
||||||
|
@ -112,6 +115,28 @@ int extractTotalRecordCount(const DTO::BaseItemDtoQueryResult &result) {
|
||||||
return result.totalRecordCount();
|
return result.totalRecordCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
QList<DTO::BaseItemDto> extractRecords(const QList<DTO::BaseItemDto> &result) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
int extractTotalRecordCount(const QList<DTO::BaseItemDto> &result) {
|
||||||
|
return result.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
void setRequestLimit(Loader::GetLatestMediaParams ¶ms, int limit) {
|
||||||
|
params.setLimit(limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
bool setRequestStartIndex(Loader::GetLatestMediaParams ¶ms, int offset) {
|
||||||
|
Q_UNUSED(params)
|
||||||
|
Q_UNUSED(offset)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void registerModels(const char *URI) {
|
void registerModels(const char *URI) {
|
||||||
Q_UNUSED(URI)
|
Q_UNUSED(URI)
|
||||||
|
|
|
@ -22,20 +22,25 @@ namespace Jellyfin {
|
||||||
void registerTypes(const char *uri) {
|
void registerTypes(const char *uri) {
|
||||||
qmlRegisterType<ApiClient>(uri, 1, 0, "ApiClient");
|
qmlRegisterType<ApiClient>(uri, 1, 0, "ApiClient");
|
||||||
qmlRegisterType<ServerDiscoveryModel>(uri, 1, 0, "ServerDiscoveryModel");
|
qmlRegisterType<ServerDiscoveryModel>(uri, 1, 0, "ServerDiscoveryModel");
|
||||||
|
qmlRegisterType<ViewModel::PlaybackManager>(uri, 1, 0, "PlaybackManager");
|
||||||
|
qmlRegisterUncreatableType<ViewModel::Item>(uri, 1, 0, "Item", "Acquire one via ItemLoader or exposed properties");
|
||||||
|
qmlRegisterUncreatableType<WebSocket>(uri, 1, 0, "WebSocket", "Obtain one via your ApiClient");
|
||||||
|
|
||||||
|
// AbstractItemModels
|
||||||
qmlRegisterUncreatableType<BaseApiModel>(uri, 1, 0, "BaseApiModel", "Please use one of its subclasses");
|
qmlRegisterUncreatableType<BaseApiModel>(uri, 1, 0, "BaseApiModel", "Please use one of its subclasses");
|
||||||
qmlRegisterUncreatableType<BaseModelLoader>(uri, 1, 0, "BaseModelLoader", "Please use one of its subclasses");
|
qmlRegisterUncreatableType<BaseModelLoader>(uri, 1, 0, "BaseModelLoader", "Please use one of its subclasses");
|
||||||
qmlRegisterUncreatableType<ViewModel::LoaderBase>(uri, 1, 0, "LoaderBase", "Use on eof its subclasses");
|
|
||||||
|
|
||||||
qmlRegisterUncreatableType<ViewModel::Item>(uri, 1, 0, "Item", "Acquire one via ItemLoader or exposed properties");
|
|
||||||
qmlRegisterType<ViewModel::ItemLoader>(uri, 1, 0, "ItemLoader");
|
|
||||||
qmlRegisterType<ViewModel::ItemModel>(uri, 1, 0, "ItemModel");
|
qmlRegisterType<ViewModel::ItemModel>(uri, 1, 0, "ItemModel");
|
||||||
qmlRegisterType<ViewModel::UserViewsLoader>(uri, 1, 0, "UsersViewLoader");
|
|
||||||
|
|
||||||
qmlRegisterType<ViewModel::PlaybackManager>(uri, 1, 0, "PlaybackManager");
|
// Loaders
|
||||||
|
qmlRegisterUncreatableType<ViewModel::LoaderBase>(uri, 1, 0, "LoaderBase", "Use one of its subclasses");
|
||||||
|
qmlRegisterType<ViewModel::ItemLoader>(uri, 1, 0, "ItemLoader");
|
||||||
|
qmlRegisterType<ViewModel::LatestMediaLoader>(uri, 1, 0, "LatestMediaLoader");
|
||||||
|
qmlRegisterType<ViewModel::UserItemsLoader>(uri, 1, 0, "UserItemsLoader");
|
||||||
|
qmlRegisterType<ViewModel::UserViewsLoader>(uri, 1, 0, "UsersViewsLoader");
|
||||||
|
|
||||||
|
// Enumerations
|
||||||
qmlRegisterUncreatableType<DTO::GeneralCommandTypeClass>(uri, 1, 0, "GeneralCommandType", "Is an enum");
|
qmlRegisterUncreatableType<DTO::GeneralCommandTypeClass>(uri, 1, 0, "GeneralCommandType", "Is an enum");
|
||||||
qmlRegisterUncreatableType<ViewModel::ModelStatusClass>(uri, 1, 0, "ModelStatus", "Is an enum");
|
qmlRegisterUncreatableType<ViewModel::ModelStatusClass>(uri, 1, 0, "ModelStatus", "Is an enum");
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,10 @@
|
||||||
namespace Jellyfin {
|
namespace Jellyfin {
|
||||||
namespace Model {
|
namespace Model {
|
||||||
|
|
||||||
|
|
||||||
|
Item::Item()
|
||||||
|
: Item(DTO::BaseItemDto(), nullptr) { }
|
||||||
|
|
||||||
Item::Item(const DTO::BaseItemDto &data, ApiClient *apiClient)
|
Item::Item(const DTO::BaseItemDto &data, ApiClient *apiClient)
|
||||||
: DTO::BaseItemDto(data), m_apiClient(apiClient) {
|
: DTO::BaseItemDto(data), m_apiClient(apiClient) {
|
||||||
if (m_apiClient != nullptr) {
|
if (m_apiClient != nullptr) {
|
||||||
|
|
|
@ -149,8 +149,14 @@ QJsonValue toJsonValue<QStringList>(const QStringList &source, convertType<QStri
|
||||||
// QJsonObject
|
// QJsonObject
|
||||||
template <>
|
template <>
|
||||||
QJsonObject fromJsonValue<QJsonObject>(const QJsonValue &source, convertType<QJsonObject>) {
|
QJsonObject fromJsonValue<QJsonObject>(const QJsonValue &source, convertType<QJsonObject>) {
|
||||||
if (!source.isObject()) throw ParseException("Error parsing JSON value as object: not a double");
|
switch(source.type()) {
|
||||||
return source.toObject();
|
case QJsonValue::Null:
|
||||||
|
return QJsonObject();
|
||||||
|
case QJsonValue::Object:
|
||||||
|
return source.toObject();
|
||||||
|
default:
|
||||||
|
throw ParseException("Error parsing JSON value as object: not an object");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
|
@ -161,7 +167,7 @@ QJsonValue toJsonValue<QJsonObject>(const QJsonObject &source, convertType<QJson
|
||||||
// Double
|
// Double
|
||||||
template <>
|
template <>
|
||||||
double fromJsonValue<double>(const QJsonValue &source, convertType<double>) {
|
double fromJsonValue<double>(const QJsonValue &source, convertType<double>) {
|
||||||
if (!source.isDouble()) throw ParseException("Error parsing JSON value as integer: not a double");
|
if (!source.isDouble()) throw ParseException("Error parsing JSON value as double: not a double");
|
||||||
return source.toDouble();
|
return source.toDouble();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,6 +176,18 @@ QJsonValue toJsonValue<double>(const double &source, convertType<double>) {
|
||||||
return QJsonValue(source);
|
return QJsonValue(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Float
|
||||||
|
template <>
|
||||||
|
float fromJsonValue<float>(const QJsonValue &source, convertType<float>) {
|
||||||
|
if (!source.isDouble()) throw ParseException("Error parsing JSON value as float: not a double");
|
||||||
|
return static_cast<float>(source.toDouble());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
QJsonValue toJsonValue<float>(const float &source, convertType<float>) {
|
||||||
|
return QJsonValue(static_cast<double>(source));
|
||||||
|
}
|
||||||
|
|
||||||
// QDateTime
|
// QDateTime
|
||||||
template <>
|
template <>
|
||||||
QDateTime fromJsonValue<QDateTime>(const QJsonValue &source, convertType<QDateTime>) {
|
QDateTime fromJsonValue<QDateTime>(const QJsonValue &source, convertType<QDateTime>) {
|
||||||
|
@ -205,27 +223,37 @@ QJsonValue toJsonValue<QUuid>(const QUuid &source, convertType<QUuid>) {
|
||||||
|
|
||||||
// String types
|
// String types
|
||||||
template <>
|
template <>
|
||||||
QString toString(const QUuid &source) {
|
QString toString(const QUuid &source, convertType<QUuid>) {
|
||||||
return uuidToString(source);
|
return uuidToString(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
QString toString(const qint32 &source) {
|
QString toString(const qint32 &source, convertType<qint32>) {
|
||||||
return QString::number(source);
|
return QString::number(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
QString toString(const qint64 &source) {
|
QString toString(const qint64 &source, convertType<qint64>) {
|
||||||
return QString::number(source);
|
return QString::number(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
QString toString(const bool &source) {
|
QString toString(const float &source, convertType<float>) {
|
||||||
|
return QString::number(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
QString toString(const double &source, convertType<double>) {
|
||||||
|
return QString::number(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
QString toString(const bool &source, convertType<bool>) {
|
||||||
return source ? QStringLiteral("true") : QStringLiteral("false");
|
return source ? QStringLiteral("true") : QStringLiteral("false");
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
QString toString(const QString &source) {
|
QString toString(const QString &source, convertType<QString>) {
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,11 +21,10 @@
|
||||||
namespace Jellyfin {
|
namespace Jellyfin {
|
||||||
namespace ViewModel {
|
namespace ViewModel {
|
||||||
|
|
||||||
Item::Item(QObject *parent)
|
Item::Item(QObject *parent, QSharedPointer<Model::Item> data)
|
||||||
: Item(nullptr, parent){}
|
: QObject(parent), m_data(data){
|
||||||
|
|
||||||
Item::Item(QSharedPointer<Model::Item> data, QObject *parent)
|
}
|
||||||
: QObject(parent), m_data(data){}
|
|
||||||
|
|
||||||
void Item::setData(QSharedPointer<Model::Item> newData) {
|
void Item::setData(QSharedPointer<Model::Item> newData) {
|
||||||
Model::Item oldData = *m_data.data();
|
Model::Item oldData = *m_data.data();
|
||||||
|
@ -36,7 +35,8 @@ void Item::setData(QSharedPointer<Model::Item> newData) {
|
||||||
// ItemLoader
|
// ItemLoader
|
||||||
|
|
||||||
ItemLoader::ItemLoader(QObject *parent)
|
ItemLoader::ItemLoader(QObject *parent)
|
||||||
: BaseClass(Jellyfin::Loader::HTTP::GetItemLoader(), parent) {
|
: BaseClass(new Jellyfin::Loader::HTTP::GetItemLoader(), parent) {
|
||||||
|
connect(this, &LoaderBase::apiClientChanged, this, &ItemLoader::onApiClientChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ItemLoader::onApiClientChanged(ApiClient *newApiClient) {
|
void ItemLoader::onApiClientChanged(ApiClient *newApiClient) {
|
||||||
|
@ -54,7 +54,9 @@ void ItemLoader::setUserId(const QString &newUserId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ItemLoader::canReload() const {
|
bool ItemLoader::canReload() const {
|
||||||
return BaseClass::canReload() && !m_parameters.itemId().isEmpty();
|
return BaseClass::canReload()
|
||||||
|
&& !m_parameters.itemId().isEmpty()
|
||||||
|
&& !m_parameters.userId().isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,9 @@
|
||||||
*/
|
*/
|
||||||
#include "JellyfinQt/viewmodel/itemmodel.h"
|
#include "JellyfinQt/viewmodel/itemmodel.h"
|
||||||
|
|
||||||
|
#include "JellyfinQt/loader/http/getlatestmedia.h"
|
||||||
|
#include "JellyfinQt/loader/http/getitemsbyuserid.h"
|
||||||
|
|
||||||
#define JF_CASE(roleName) case roleName: \
|
#define JF_CASE(roleName) case roleName: \
|
||||||
try { \
|
try { \
|
||||||
return QVariant(item.roleName()); \
|
return QVariant(item.roleName()); \
|
||||||
|
@ -30,37 +33,13 @@ namespace Jellyfin {
|
||||||
namespace ViewModel {
|
namespace ViewModel {
|
||||||
|
|
||||||
UserViewsLoader::UserViewsLoader(QObject *parent)
|
UserViewsLoader::UserViewsLoader(QObject *parent)
|
||||||
: UserViewsLoaderBase(new Jellyfin::Loader::HTTP::GetUserViewsLoader(), parent) {
|
: UserViewsLoaderBase(new Jellyfin::Loader::HTTP::GetUserViewsLoader(), parent) { }
|
||||||
connect(this, &BaseModelLoader::apiClientChanged, this, &UserViewsLoader::apiClientChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UserViewsLoader::apiClientChanged(ApiClient *newApiClient) {
|
LatestMediaLoader::LatestMediaLoader(QObject *parent)
|
||||||
if (m_apiClient != nullptr) disconnect(m_apiClient, &ApiClient::userIdChanged, this, &UserViewsLoader::userIdChanged);
|
: LatestMediaBase(new Jellyfin::Loader::HTTP::GetLatestMediaLoader(), parent){ }
|
||||||
if (newApiClient != nullptr) {
|
|
||||||
connect(newApiClient, &ApiClient::userIdChanged, this, &UserViewsLoader::userIdChanged);
|
|
||||||
if (!newApiClient->userId().isNull()) {
|
|
||||||
m_parameters.setUserId(newApiClient->userId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void UserViewsLoader::userIdChanged(const QString &newUserId) {
|
UserItemsLoader::UserItemsLoader(QObject *parent)
|
||||||
m_parameters.setUserId(newUserId);
|
: UserItemsLoaderBase(new Jellyfin::Loader::HTTP::GetItemsByUserIdLoader(), parent) {}
|
||||||
autoReloadIfNeeded();
|
|
||||||
}
|
|
||||||
void UserItemsLoader::apiClientChanged(ApiClient *newApiClient) {
|
|
||||||
if (m_apiClient != nullptr) disconnect(m_apiClient, &ApiClient::userIdChanged, this, &UserItemsLoader::userIdChanged);
|
|
||||||
if (newApiClient != nullptr) connect(newApiClient, &ApiClient::userIdChanged, this, &UserItemsLoader::userIdChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UserItemsLoader::userIdChanged(const QString &newUserId) {
|
|
||||||
m_parameters.setUserId(newUserId);
|
|
||||||
autoReloadIfNeeded();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool UserItemsLoader::canReload() const {
|
|
||||||
return BaseModelLoader::canReload() && !m_parameters.userId().isNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
ItemModel::ItemModel(QObject *parent)
|
ItemModel::ItemModel(QObject *parent)
|
||||||
: ApiModel<Model::Item>(parent) { }
|
: ApiModel<Model::Item>(parent) { }
|
||||||
|
@ -81,6 +60,8 @@ QVariant ItemModel::data(const QModelIndex &index, int role) const {
|
||||||
JF_CASE(dateCreated)
|
JF_CASE(dateCreated)
|
||||||
JF_CASE(dateLastMediaAdded)
|
JF_CASE(dateLastMediaAdded)
|
||||||
JF_CASE(extraType)
|
JF_CASE(extraType)
|
||||||
|
// Handpicked, important ones
|
||||||
|
JF_CASE(imageTags)
|
||||||
default:
|
default:
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,11 @@ ApplicationWindow {
|
||||||
height: 600
|
height: 600
|
||||||
visible: true
|
visible: true
|
||||||
property int _oldDepth: 0
|
property int _oldDepth: 0
|
||||||
|
property alias playbackManager: playbackManager
|
||||||
|
|
||||||
|
J.PlaybackManager {
|
||||||
|
id: playbackManager
|
||||||
|
}
|
||||||
|
|
||||||
background: Background {
|
background: Background {
|
||||||
id: background
|
id: background
|
||||||
|
@ -43,4 +48,20 @@ ApplicationWindow {
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
ApiClient.restoreSavedSession()
|
ApiClient.restoreSavedSession()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
footer: Column {
|
||||||
|
id: footer
|
||||||
|
Text {
|
||||||
|
text: qsTr("Now playing")
|
||||||
|
color: "white"
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
text: playbackManager.item.name ? playbackManager.item.name : "Nothing"
|
||||||
|
color: "white"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
color: "darkblue"
|
||||||
|
anchors.fill: footer
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,12 +6,14 @@ import nl.netsoj.chris.Jellyfin 1.0 as J
|
||||||
|
|
||||||
import "../components"
|
import "../components"
|
||||||
import "../.."
|
import "../.."
|
||||||
|
import ".."
|
||||||
|
|
||||||
Page {
|
Page {
|
||||||
|
id: detailPage
|
||||||
property bool _modelsLoaded: false
|
property bool _modelsLoaded: false
|
||||||
property StackView stackView: StackView.view
|
property StackView stackView: StackView.view
|
||||||
property string itemId
|
property string itemId
|
||||||
property alias jellyfinItem: jellyfinItem.data
|
property alias jellyfinItem: jellyfinItemLoader.data
|
||||||
header: ToolBar {
|
header: ToolBar {
|
||||||
Label {
|
Label {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
@ -25,13 +27,33 @@ Page {
|
||||||
onClicked: stackView.pop()
|
onClicked: stackView.pop()
|
||||||
}
|
}
|
||||||
J.ItemLoader {
|
J.ItemLoader {
|
||||||
id: jellyfinItem
|
id: jellyfinItemLoader
|
||||||
jellyfinId: itemId
|
itemId: detailPage.itemId
|
||||||
apiClient: ApiClient
|
apiClient: ApiClient
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
anchors.centerIn: parent
|
anchors.top: parent.top
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height / 3
|
||||||
source: ApiClient.baseUrl + "/Items/" + itemId + "/Images/Primary?tag=" + jellyfinItem.tag
|
source: ApiClient.baseUrl + "/Items/" + itemId + "/Images/Primary?tag=" + jellyfinItem.tag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height / 3 * 2
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
model: J.ItemModel {
|
||||||
|
loader: J.UserItemsLoader {
|
||||||
|
apiClient: ApiClient
|
||||||
|
parentId: detailPage.itemId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delegate: ItemDelegate{
|
||||||
|
icon.source: ApiClient.baseUrl + "/Items/" + model.jellyfinId + "/Images/Primary?tag=" + model.tag
|
||||||
|
text: model.name
|
||||||
|
width: parent.width
|
||||||
|
onClicked: playbackManager.play(model.jellyfinId)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ Page {
|
||||||
|
|
||||||
J.ItemModel {
|
J.ItemModel {
|
||||||
id: mediaLibraryModel
|
id: mediaLibraryModel
|
||||||
loader: J.UsersViewLoader {
|
loader: J.UsersViewsLoader {
|
||||||
id: mediaLibraryModelLoader
|
id: mediaLibraryModelLoader
|
||||||
apiClient: ApiClient
|
apiClient: ApiClient
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ Page {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
contentHeight: content.height
|
contentHeight: content.height
|
||||||
|
contentWidth: availableWidth
|
||||||
Column {
|
Column {
|
||||||
id: content
|
id: content
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
@ -37,12 +38,15 @@ Page {
|
||||||
model: mediaLibraryModel
|
model: mediaLibraryModel
|
||||||
Column {
|
Column {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
/*J.UserItemLatestModel {
|
J.ItemModel{
|
||||||
id: userItemModel
|
id: userItemModel
|
||||||
apiClient: ApiClient
|
loader: J.LatestMediaLoader {
|
||||||
parentId: model.id
|
id: latestMediaLoader
|
||||||
limit: 16
|
apiClient: ApiClient
|
||||||
}*/
|
parentId: model.jellyfinId
|
||||||
|
//limit: 16
|
||||||
|
}
|
||||||
|
}
|
||||||
Label {
|
Label {
|
||||||
text: model.name ? model.name : "<Model without name>"
|
text: model.name ? model.name : "<Model without name>"
|
||||||
}
|
}
|
||||||
|
@ -51,13 +55,14 @@ Page {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: SailfinStyle.unit * 20
|
height: SailfinStyle.unit * 20
|
||||||
orientation: ListView.Horizontal
|
orientation: ListView.Horizontal
|
||||||
model: 10 // userItemModel
|
model: userItemModel
|
||||||
delegate: ItemDelegate {
|
delegate: ItemDelegate {
|
||||||
width: 12 * SailfinStyle.unit
|
width: 12 * SailfinStyle.unit
|
||||||
height: 20 * SailfinStyle.unit
|
height: 20 * SailfinStyle.unit
|
||||||
Image {
|
Image {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
source: ApiClient.baseUrl + "/Items/" + model.id + "/Images/Primary?tag=" + model.tag
|
source: ApiClient.baseUrl + "/Items/" + model.jellyfinId
|
||||||
|
+ "/Images/Primary?tag=" + model.imageTags["Primary"] //model.tag
|
||||||
}
|
}
|
||||||
Label {
|
Label {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
|
@ -65,14 +70,18 @@ Page {
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
text: model.name
|
text: model.name
|
||||||
}
|
}
|
||||||
onClicked: stackView.push(Qt.resolvedUrl("DetailPage.qml"), {"itemId": model.id})
|
onClicked: stackView.push(Qt.resolvedUrl(
|
||||||
|
"DetailPage.qml"), {
|
||||||
|
"itemId": model.jellyfinId
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Connections {
|
Connections {
|
||||||
target: mediaLibraryModelLoader
|
target: mediaLibraryModelLoader
|
||||||
onReady: {
|
onReady: {
|
||||||
if (mediaLibraryModelLoader.status === ModelStatus.Ready) {
|
if (mediaLibraryModelLoader.status === ModelStatus.Ready) {
|
||||||
//userItemModel.reload()
|
|
||||||
|
latestMediaLoader.reload()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,13 +90,14 @@ Page {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads models if not laoded. Set force to true to reload models
|
* Loads models if not laoded. Set force to true to reload models
|
||||||
* even if loaded.
|
* even if loaded.
|
||||||
*/
|
*/
|
||||||
function loadModels(force) {
|
function loadModels(force) {
|
||||||
if (force || (ApiClient.authenticated && !_modelsLoaded)) {
|
if (force || (ApiClient.authenticated && !_modelsLoaded)) {
|
||||||
_modelsLoaded = true;
|
_modelsLoaded = true
|
||||||
mediaLibraryModel.reload()
|
mediaLibraryModel.reload()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue