Refactor image download code to be reusable

This commit is contained in:
Nicolas Werner 2021-03-17 03:27:28 +01:00
parent 41737ac22c
commit 95026dcc62
No known key found for this signature in database
GPG key ID: C8D75E610773F2D9
2 changed files with 161 additions and 90 deletions

View file

@ -4,106 +4,192 @@
#include "MxcImageProvider.h" #include "MxcImageProvider.h"
#include <optional>
#include <mtxclient/crypto/client.hpp> #include <mtxclient/crypto/client.hpp>
#include <QByteArray>
#include <QFileInfo>
#include <QStandardPaths>
#include "Cache.h" #include "Cache.h"
#include "Logging.h" #include "Logging.h"
#include "MatrixClient.h" #include "MatrixClient.h"
#include "Utils.h" #include "Utils.h"
QHash<QString, mtx::crypto::EncryptedFile> infos;
QQuickImageResponse *
MxcImageProvider::requestImageResponse(const QString &id, const QSize &requestedSize)
{
MxcImageResponse *response = new MxcImageResponse(id, requestedSize);
pool.start(response);
return response;
}
void
MxcImageProvider::addEncryptionInfo(mtx::crypto::EncryptedFile info)
{
infos.insert(QString::fromStdString(info.url), info);
}
void void
MxcImageResponse::run() MxcImageResponse::run()
{ {
if (m_requestedSize.isValid() && !m_encryptionInfo) { MxcImageProvider::download(
QString fileName = QString("%1_%2x%3_crop") m_id, m_requestedSize, [this](QString, QSize, QImage image, QString) {
.arg(m_id) if (image.isNull()) {
.arg(m_requestedSize.width()) m_error = "Failed to download image.";
.arg(m_requestedSize.height()); } else {
m_image = image;
auto data = cache::image(fileName); }
if (!data.isNull()) {
m_image = utils::readImage(data);
if (!m_image.isNull()) {
m_image = m_image.scaled(
m_requestedSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
m_image.setText("mxc url", "mxc://" + m_id);
if (!m_image.isNull()) {
emit finished(); emit finished();
});
}
void
MxcImageProvider::download(const QString &id,
const QSize &requestedSize,
std::function<void(QString, QSize, QImage, QString)> then)
{
std::optional<mtx::crypto::EncryptedFile> encryptionInfo;
auto temp = infos.find("mxc://" + id);
if (temp != infos.end())
encryptionInfo = *temp;
if (requestedSize.isValid() && !encryptionInfo) {
QString fileName =
QString("%1_%2x%3_crop")
.arg(QString::fromUtf8(id.toUtf8().toBase64(QByteArray::Base64UrlEncoding |
QByteArray::OmitTrailingEquals)),
requestedSize.width(),
requestedSize.height());
QFileInfo fileInfo(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) +
"/media_cache",
fileName);
if (fileInfo.exists()) {
QImage image(fileInfo.absoluteFilePath());
if (!image.isNull()) {
image = image.scaled(
requestedSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
if (!image.isNull()) {
then(id, requestedSize, image, fileInfo.absoluteFilePath());
return; return;
} }
} }
} }
mtx::http::ThumbOpts opts; mtx::http::ThumbOpts opts;
opts.mxc_url = "mxc://" + m_id.toStdString(); opts.mxc_url = "mxc://" + id.toStdString();
opts.width = m_requestedSize.width() > 0 ? m_requestedSize.width() : -1; opts.width = requestedSize.width() > 0 ? requestedSize.width() : -1;
opts.height = m_requestedSize.height() > 0 ? m_requestedSize.height() : -1; opts.height = requestedSize.height() > 0 ? requestedSize.height() : -1;
opts.method = "crop"; opts.method = "crop";
http::client()->get_thumbnail( http::client()->get_thumbnail(
opts, [this, fileName](const std::string &res, mtx::http::RequestErr err) { opts,
[fileInfo, requestedSize, then, id](const std::string &res,
mtx::http::RequestErr err) {
if (err || res.empty()) { if (err || res.empty()) {
nhlog::net()->error("Failed to download image {}", then(id, QSize(), {}, "");
m_id.toStdString());
m_error = "Failed download";
emit finished();
return; return;
} }
auto data = QByteArray(res.data(), (int)res.size()); auto data = QByteArray(res.data(), (int)res.size());
cache::saveImage(fileName, data); QImage image = utils::readImage(data);
m_image = utils::readImage(data); if (!image.isNull()) {
if (!m_image.isNull()) { image = image.scaled(
m_image = m_image.scaled( requestedSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
m_requestedSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
} }
m_image.setText("mxc url", "mxc://" + m_id); image.setText("mxc url", "mxc://" + id);
image.save(fileInfo.absoluteFilePath());
emit finished(); then(id, requestedSize, image, fileInfo.absoluteFilePath());
}); });
} else { } else {
auto data = cache::image(m_id); try {
QString fileName = QString::fromUtf8(id.toUtf8().toBase64(
QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals));
QFileInfo fileInfo(
QStandardPaths::writableLocation(QStandardPaths::CacheLocation) +
"/media_cache",
fileName);
if (!data.isNull()) { if (fileInfo.exists()) {
m_image = utils::readImage(data); if (encryptionInfo) {
m_image.setText("mxc url", "mxc://" + m_id); QFile f(fileInfo.absoluteFilePath());
f.open(QIODevice::ReadOnly);
if (!m_image.isNull()) { QByteArray fileData = f.readAll();
emit finished(); auto temp =
mtx::crypto::to_string(mtx::crypto::decrypt_file(
fileData.toStdString(), encryptionInfo.value()));
auto data = QByteArray(temp.data(), (int)temp.size());
QImage image = utils::readImage(data);
image.setText("mxc url", "mxc://" + id);
if (!image.isNull()) {
then(id,
requestedSize,
image,
fileInfo.absoluteFilePath());
return;
}
} else {
QImage image(fileInfo.absoluteFilePath());
if (!image.isNull()) {
then(id,
requestedSize,
image,
fileInfo.absoluteFilePath());
return; return;
} }
} }
}
auto data = cache::image(id);
http::client()->download( http::client()->download(
"mxc://" + m_id.toStdString(), "mxc://" + id.toStdString(),
[this](const std::string &res, [fileInfo, requestedSize, then, id, encryptionInfo](
const std::string &res,
const std::string &, const std::string &,
const std::string &originalFilename, const std::string &originalFilename,
mtx::http::RequestErr err) { mtx::http::RequestErr err) {
if (err) { if (err) {
nhlog::net()->error("Failed to download image {}", then(id, QSize(), {}, "");
m_id.toStdString());
m_error = "Failed download";
emit finished();
return; return;
} }
auto temp = res; auto temp = res;
if (m_encryptionInfo) QFile f(fileInfo.absoluteFilePath());
temp = mtx::crypto::to_string( if (!f.open(QIODevice::Truncate | QIODevice::WriteOnly)) {
mtx::crypto::decrypt_file(temp, m_encryptionInfo.value())); then(id, QSize(), {}, "");
return;
}
f.write(temp.data(), temp.size());
f.close();
if (encryptionInfo) {
temp = mtx::crypto::to_string(mtx::crypto::decrypt_file(
temp, encryptionInfo.value()));
auto data = QByteArray(temp.data(), (int)temp.size()); auto data = QByteArray(temp.data(), (int)temp.size());
cache::saveImage(m_id, data); QImage image = utils::readImage(data);
m_image = utils::readImage(data); image.setText("original filename",
m_image.setText("original filename",
QString::fromStdString(originalFilename)); QString::fromStdString(originalFilename));
m_image.setText("mxc url", "mxc://" + m_id); image.setText("mxc url", "mxc://" + id);
then(
id, requestedSize, image, fileInfo.absoluteFilePath());
return;
}
emit finished(); QImage image(fileInfo.absoluteFilePath());
image.setText("original filename",
QString::fromStdString(originalFilename));
image.setText("mxc url", "mxc://" + id);
image.save(fileInfo.absoluteFilePath());
then(id, requestedSize, image, fileInfo.absoluteFilePath());
}); });
} catch (std::exception &e) {
nhlog::net()->error("Exception while downloading media: {}", e.what());
}
} }
} }

View file

@ -10,21 +10,18 @@
#include <QImage> #include <QImage>
#include <QThreadPool> #include <QThreadPool>
#include <mtx/common.hpp> #include <functional>
#include <boost/optional.hpp> #include <mtx/common.hpp>
class MxcImageResponse class MxcImageResponse
: public QQuickImageResponse : public QQuickImageResponse
, public QRunnable , public QRunnable
{ {
public: public:
MxcImageResponse(const QString &id, MxcImageResponse(const QString &id, const QSize &requestedSize)
const QSize &requestedSize,
boost::optional<mtx::crypto::EncryptedFile> encryptionInfo)
: m_id(id) : m_id(id)
, m_requestedSize(requestedSize) , m_requestedSize(requestedSize)
, m_encryptionInfo(encryptionInfo)
{ {
setAutoDelete(false); setAutoDelete(false);
} }
@ -40,7 +37,6 @@ public:
QString m_id, m_error; QString m_id, m_error;
QSize m_requestedSize; QSize m_requestedSize;
QImage m_image; QImage m_image;
boost::optional<mtx::crypto::EncryptedFile> m_encryptionInfo;
}; };
class MxcImageProvider class MxcImageProvider
@ -50,24 +46,13 @@ class MxcImageProvider
Q_OBJECT Q_OBJECT
public slots: public slots:
QQuickImageResponse *requestImageResponse(const QString &id, QQuickImageResponse *requestImageResponse(const QString &id,
const QSize &requestedSize) override const QSize &requestedSize) override;
{
boost::optional<mtx::crypto::EncryptedFile> info;
auto temp = infos.find("mxc://" + id);
if (temp != infos.end())
info = *temp;
MxcImageResponse *response = new MxcImageResponse(id, requestedSize, info); static void addEncryptionInfo(mtx::crypto::EncryptedFile info);
pool.start(response); static void download(const QString &id,
return response; const QSize &requestedSize,
} std::function<void(QString, QSize, QImage, QString)> then);
void addEncryptionInfo(mtx::crypto::EncryptedFile info)
{
infos.insert(QString::fromStdString(info.url), info);
}
private: private:
QThreadPool pool; QThreadPool pool;
QHash<QString, mtx::crypto::EncryptedFile> infos;
}; };