mirror of
https://github.com/HenkKalkwater/harbour-sailfin.git
synced 2025-09-01 08:52:45 +00:00
WIP: HttpLoader seems to work, Model still borked
This commit is contained in:
parent
e421adf733
commit
729e343661
1412 changed files with 13967 additions and 33794 deletions
|
@ -40,6 +40,10 @@ class LoadException : public QException {
|
|||
public:
|
||||
explicit LoadException(const QString &message)
|
||||
: m_message(message.toStdString()) {}
|
||||
|
||||
/*explicit LoadException(const LoadException &other)
|
||||
: m_message(other.m_message) {}*/
|
||||
|
||||
virtual const char *what() const noexcept override;
|
||||
|
||||
virtual QException *clone() const override;
|
||||
|
@ -54,6 +58,10 @@ static const int HTTP_TIMEOUT = 30000; // 30 seconds;
|
|||
* Interface describing a way to load items. Used to abstract away
|
||||
* the difference between loading from a cache or loading over the network.
|
||||
*
|
||||
* To implement this class, implement prepareLoad() and load(). These are always called
|
||||
* in the same order, but prepareLoad() must always be called on the same thread as the
|
||||
* m_apiClient, while load() may be called on another thread.
|
||||
*
|
||||
* @note: Loaders should NEVER call load() again while load() is running on another
|
||||
* thread or change the apiClient while running. This will result in undefined behaviour.
|
||||
* Please use a Mutex to enforce this.
|
||||
|
@ -67,13 +75,18 @@ class Loader {
|
|||
public:
|
||||
explicit Loader(ApiClient *apiClient)
|
||||
: m_apiClient(apiClient) {}
|
||||
|
||||
/**
|
||||
* @brief load Loads the given resource.
|
||||
* @param parameters Parameters to determine which resource should be loaded.
|
||||
* @brief Called just before load() is called. In constrast to load,
|
||||
* this runs on the same thread as the ApiClient object.
|
||||
*/
|
||||
virtual void prepareLoad() {};
|
||||
|
||||
/**
|
||||
* @brief load Loads the given resource. This usually run on a different thread.
|
||||
* @return The resource if successfull.
|
||||
*/
|
||||
virtual std::optional<R> load(const P ¶meters) const {
|
||||
Q_UNUSED(parameters)
|
||||
virtual std::optional<R> load() {
|
||||
throw LoadException(QStringLiteral("Loader not set"));
|
||||
}
|
||||
/**
|
||||
|
@ -86,38 +99,10 @@ public:
|
|||
virtual bool isAvailable() const { return false; };
|
||||
void setApiClient(ApiClient *newApiClient) { m_apiClient = newApiClient; }
|
||||
ApiClient *apiClient() const { return m_apiClient; }
|
||||
void setParameters(const P ¶meters) { m_parameters = parameters; }
|
||||
protected:
|
||||
Jellyfin::ApiClient *m_apiClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of a Loader that tries multiple loaders.
|
||||
*/
|
||||
template <typename R, typename P>
|
||||
class MultipleChoiceLoader : public Loader<R, P> {
|
||||
using L = Loader<R, P>;
|
||||
public:
|
||||
explicit MultipleChoiceLoader(ApiClient *apiClient,
|
||||
std::initializer_list<L> loaders)
|
||||
: L(apiClient), m_loaders(loaders) {}
|
||||
|
||||
virtual std::optional<R> load(const P ¶meters) const override {
|
||||
for(auto it = m_loaders.begin(); it != m_loaders.end(); it++) {
|
||||
if (it->isAvailable()) {
|
||||
try {
|
||||
std::optional<R> res = it->load(parameters);
|
||||
if (res.has_value()) return res;
|
||||
} catch (LoadException &e) {
|
||||
qDebug() << "Error while loading: " << e.what();
|
||||
}
|
||||
}
|
||||
}
|
||||
qDebug() << "No loaders were able to fulfill the request";
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
private:
|
||||
QList<L> m_loaders;
|
||||
P m_parameters;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -129,29 +114,40 @@ public:
|
|||
explicit HttpLoader(Jellyfin::ApiClient *apiClient)
|
||||
: Loader<R, P> (apiClient) {}
|
||||
|
||||
virtual std::optional<R> load(const P ¶meters) const override {
|
||||
QNetworkReply *reply = this->m_apiClient->get(path(parameters), query(parameters));
|
||||
QByteArray array;
|
||||
while (!reply->atEnd()) {
|
||||
if (!reply->waitForReadyRead(HTTP_TIMEOUT)) {
|
||||
if (reply->error() == QNetworkReply::NoError) {
|
||||
reply->deleteLater();
|
||||
//: Loading from a resource failed due to the server taking too long to respond
|
||||
throw LoadException(QObject::tr("Network timeout"));
|
||||
}
|
||||
reply->deleteLater();
|
||||
//: An HTTP has occurred. First argument is replaced by QNetworkReply->errorString()
|
||||
throw LoadException(QObject::tr("HTTP error: %1").arg(reply->errorString()));
|
||||
}
|
||||
array.append(reply->readAll());
|
||||
virtual void prepareLoad() override {
|
||||
m_reply = this->m_apiClient->get(path(this->m_parameters), query(this->m_parameters));
|
||||
m_requestFinishedConnection = QObject::connect(m_reply, &QNetworkReply::finished, [&]() { this->requestFinished(); });
|
||||
}
|
||||
|
||||
virtual std::optional<R> load() override {
|
||||
Q_ASSERT_X(m_reply != nullptr, "HttpLoader::load", "prepareLoad() must be called before load()");
|
||||
QMutexLocker locker(&m_mutex);
|
||||
while (!m_reply->isFinished()) {
|
||||
m_waitCondition.wait(&m_mutex);
|
||||
}
|
||||
reply->deleteLater();
|
||||
QByteArray array = m_reply->readAll();
|
||||
if (m_reply->error() != QNetworkReply::NoError) {
|
||||
m_reply->deleteLater();
|
||||
//: An HTTP has occurred. First argument is replaced by QNetworkReply->errorString()
|
||||
throw LoadException(QObject::tr("HTTP error: %1").arg(m_reply->errorString()));
|
||||
}
|
||||
m_reply->deleteLater();
|
||||
m_reply = nullptr;
|
||||
QJsonParseError error;
|
||||
QJsonDocument document = QJsonDocument::fromJson(array, &error);
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
qWarning() << array;
|
||||
throw LoadException(error.errorString().toLocal8Bit().constData());
|
||||
}
|
||||
return fromJsonValue<R>(QJsonValue(document.object()));
|
||||
if (document.isNull() || document.isEmpty()) {
|
||||
return std::nullopt;
|
||||
} else if (document.isArray()) {
|
||||
return std::optional<R>(fromJsonValue<R>(document.array()));
|
||||
} else if (document.isObject()){
|
||||
return std::optional<R>(fromJsonValue<R>(document.object()));
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
bool isAvailable() const override {
|
||||
|
@ -169,6 +165,16 @@ protected:
|
|||
*/
|
||||
virtual QString path(const P ¶meters) const = 0;
|
||||
virtual QUrlQuery query(const P ¶meters) const = 0;
|
||||
private:
|
||||
QNetworkReply *m_reply = nullptr;
|
||||
QWaitCondition m_waitCondition;
|
||||
QMutex m_mutex;
|
||||
QMetaObject::Connection m_requestFinishedConnection;
|
||||
|
||||
void requestFinished() {
|
||||
QObject::disconnect(m_requestFinishedConnection);
|
||||
m_waitCondition.wakeAll();
|
||||
}
|
||||
};
|
||||
|
||||
} // NS Support
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue