Client OPDS per Calibre Web (Sailfish OS): core C++ testato, UI Silica, packaging RPM
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
#include "apiclient.h"
|
||||
|
||||
#include "opdsparser.h"
|
||||
#include "settings.h"
|
||||
|
||||
#include <QCryptographicHash>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
#include <QSslError>
|
||||
#include <QStandardPaths>
|
||||
#include <QUrl>
|
||||
|
||||
ApiClient::ApiClient(Settings *settings, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_settings(settings)
|
||||
{
|
||||
m_cacheDir = QStandardPaths::writableLocation(QStandardPaths::CacheLocation)
|
||||
+ QStringLiteral("/covers");
|
||||
QDir().mkpath(m_cacheDir);
|
||||
}
|
||||
|
||||
QString ApiClient::resolveUrl(const QString &relative) const
|
||||
{
|
||||
if (relative.isEmpty())
|
||||
return QString();
|
||||
if (relative.startsWith(QLatin1String("http://"))
|
||||
|| relative.startsWith(QLatin1String("https://")))
|
||||
return relative;
|
||||
const QString base = m_settings->baseUrl();
|
||||
if (relative.startsWith(QLatin1Char('/')))
|
||||
return base + relative;
|
||||
return base + QLatin1Char('/') + relative;
|
||||
}
|
||||
|
||||
void ApiClient::applyAuth(QNetworkRequest &request)
|
||||
{
|
||||
const QString auth = m_settings->authHeader();
|
||||
if (!auth.isEmpty())
|
||||
request.setRawHeader("Authorization", auth.toUtf8());
|
||||
}
|
||||
|
||||
QNetworkReply *ApiClient::startGet(const QUrl &url)
|
||||
{
|
||||
QNetworkRequest request(url);
|
||||
request.setRawHeader("Accept",
|
||||
"application/atom+xml, application/xml, text/xml;q=0.9, */*;q=0.8");
|
||||
request.setRawHeader("User-Agent", "harbour-calibreweb/0.1");
|
||||
applyAuth(request);
|
||||
|
||||
QNetworkReply *reply = m_nam.get(request);
|
||||
if (m_settings->ignoreSslErrors()) {
|
||||
connect(reply, &QNetworkReply::sslErrors, reply,
|
||||
static_cast<void (QNetworkReply::*)(const QList<QSslError> &)>(
|
||||
&QNetworkReply::ignoreSslErrors));
|
||||
}
|
||||
return reply;
|
||||
}
|
||||
|
||||
void ApiClient::getFeed(const QString &url)
|
||||
{
|
||||
QNetworkReply *reply = startGet(QUrl(url));
|
||||
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
|
||||
reply->deleteLater();
|
||||
const int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
if (reply->error() != QNetworkReply::NoError || status >= 400) {
|
||||
QString message;
|
||||
if (status == 401)
|
||||
message = QStringLiteral(
|
||||
"Autenticazione richiesta (401). Controlla utente e password.");
|
||||
else if (status == 404)
|
||||
message = QStringLiteral("Endpoint non trovato (404). Controlla l'indirizzo.");
|
||||
else
|
||||
message = reply->errorString();
|
||||
emit feedError(message);
|
||||
return;
|
||||
}
|
||||
|
||||
const QByteArray body = reply->readAll();
|
||||
QVariantList entries;
|
||||
QString nextUrl;
|
||||
QString feedTitle;
|
||||
QString parseError;
|
||||
if (OpdsParser::parse(body, &entries, &nextUrl, &feedTitle, &parseError)) {
|
||||
emit feedReady(entries, resolveUrl(nextUrl), feedTitle);
|
||||
} else {
|
||||
emit feedError(parseError.isEmpty()
|
||||
? QStringLiteral("Risposta non valida dal server")
|
||||
: parseError);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ApiClient::fetchImage(const QString &url, const QString &id)
|
||||
{
|
||||
if (url.isEmpty()) {
|
||||
emit imageReady(id, QString());
|
||||
return;
|
||||
}
|
||||
|
||||
const QString resolved = resolveUrl(url);
|
||||
const QUrl imageUrl(resolved);
|
||||
|
||||
QString fileName = QString::fromLatin1(
|
||||
QCryptographicHash::hash(imageUrl.path().toUtf8(), QCryptographicHash::Md5).toHex());
|
||||
const QString suffix = QFileInfo(imageUrl.path()).suffix();
|
||||
if (!suffix.isEmpty() && suffix.length() <= 5)
|
||||
fileName += QLatin1Char('.') + suffix;
|
||||
const QString localPath = m_cacheDir + QLatin1Char('/') + fileName;
|
||||
|
||||
if (QFile::exists(localPath)) {
|
||||
emit imageReady(id, localPath);
|
||||
return;
|
||||
}
|
||||
|
||||
QNetworkReply *reply = startGet(imageUrl);
|
||||
connect(reply, &QNetworkReply::finished, this, [this, reply, id, localPath]() {
|
||||
reply->deleteLater();
|
||||
const int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
if (reply->error() != QNetworkReply::NoError || status >= 400) {
|
||||
emit imageReady(id, QString());
|
||||
return;
|
||||
}
|
||||
QFile file(localPath);
|
||||
if (file.open(QIODevice::WriteOnly)) {
|
||||
file.write(reply->readAll());
|
||||
file.close();
|
||||
emit imageReady(id, localPath);
|
||||
} else {
|
||||
emit imageReady(id, QString());
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user