From 95026dcc62d4ba0ea0707c75bbc54f1ac01fb954 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Wed, 17 Mar 2021 03:27:28 +0100 Subject: [PATCH] Refactor image download code to be reusable --- src/MxcImageProvider.cpp | 220 +++++++++++++++++++++++++++------------ src/MxcImageProvider.h | 31 ++---- 2 files changed, 161 insertions(+), 90 deletions(-) diff --git a/src/MxcImageProvider.cpp b/src/MxcImageProvider.cpp index 35cd0c45..db0f72c9 100644 --- a/src/MxcImageProvider.cpp +++ b/src/MxcImageProvider.cpp @@ -4,106 +4,192 @@ #include "MxcImageProvider.h" +#include + #include +#include +#include +#include + #include "Cache.h" #include "Logging.h" #include "MatrixClient.h" #include "Utils.h" +QHash 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 MxcImageResponse::run() { - if (m_requestedSize.isValid() && !m_encryptionInfo) { - QString fileName = QString("%1_%2x%3_crop") - .arg(m_id) - .arg(m_requestedSize.width()) - .arg(m_requestedSize.height()); + MxcImageProvider::download( + m_id, m_requestedSize, [this](QString, QSize, QImage image, QString) { + if (image.isNull()) { + m_error = "Failed to download image."; + } else { + m_image = image; + } + emit finished(); + }); +} - auto data = cache::image(fileName); - if (!data.isNull()) { - m_image = utils::readImage(data); +void +MxcImageProvider::download(const QString &id, + const QSize &requestedSize, + std::function then) +{ + std::optional encryptionInfo; + auto temp = infos.find("mxc://" + id); + if (temp != infos.end()) + encryptionInfo = *temp; - if (!m_image.isNull()) { - m_image = m_image.scaled( - m_requestedSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); - m_image.setText("mxc url", "mxc://" + m_id); + 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 (!m_image.isNull()) { - emit finished(); + 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; } } } mtx::http::ThumbOpts opts; - opts.mxc_url = "mxc://" + m_id.toStdString(); - opts.width = m_requestedSize.width() > 0 ? m_requestedSize.width() : -1; - opts.height = m_requestedSize.height() > 0 ? m_requestedSize.height() : -1; + opts.mxc_url = "mxc://" + id.toStdString(); + opts.width = requestedSize.width() > 0 ? requestedSize.width() : -1; + opts.height = requestedSize.height() > 0 ? requestedSize.height() : -1; opts.method = "crop"; 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()) { - nhlog::net()->error("Failed to download image {}", - m_id.toStdString()); - m_error = "Failed download"; - emit finished(); + then(id, QSize(), {}, ""); return; } - auto data = QByteArray(res.data(), (int)res.size()); - cache::saveImage(fileName, data); - m_image = utils::readImage(data); - if (!m_image.isNull()) { - m_image = m_image.scaled( - m_requestedSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); + auto data = QByteArray(res.data(), (int)res.size()); + QImage image = utils::readImage(data); + if (!image.isNull()) { + image = image.scaled( + 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 { - 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()) { - m_image = utils::readImage(data); - m_image.setText("mxc url", "mxc://" + m_id); + if (fileInfo.exists()) { + if (encryptionInfo) { + QFile f(fileInfo.absoluteFilePath()); + f.open(QIODevice::ReadOnly); - if (!m_image.isNull()) { - emit finished(); - return; + QByteArray fileData = f.readAll(); + 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; + } + } } + auto data = cache::image(id); + + http::client()->download( + "mxc://" + id.toStdString(), + [fileInfo, requestedSize, then, id, encryptionInfo]( + const std::string &res, + const std::string &, + const std::string &originalFilename, + mtx::http::RequestErr err) { + if (err) { + then(id, QSize(), {}, ""); + return; + } + + auto temp = res; + QFile f(fileInfo.absoluteFilePath()); + if (!f.open(QIODevice::Truncate | QIODevice::WriteOnly)) { + 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()); + QImage image = utils::readImage(data); + image.setText("original filename", + QString::fromStdString(originalFilename)); + image.setText("mxc url", "mxc://" + id); + then( + id, requestedSize, image, fileInfo.absoluteFilePath()); + return; + } + + 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()); } - - http::client()->download( - "mxc://" + m_id.toStdString(), - [this](const std::string &res, - const std::string &, - const std::string &originalFilename, - mtx::http::RequestErr err) { - if (err) { - nhlog::net()->error("Failed to download image {}", - m_id.toStdString()); - m_error = "Failed download"; - emit finished(); - - return; - } - - auto temp = res; - if (m_encryptionInfo) - temp = mtx::crypto::to_string( - mtx::crypto::decrypt_file(temp, m_encryptionInfo.value())); - - auto data = QByteArray(temp.data(), (int)temp.size()); - cache::saveImage(m_id, data); - m_image = utils::readImage(data); - m_image.setText("original filename", - QString::fromStdString(originalFilename)); - m_image.setText("mxc url", "mxc://" + m_id); - - emit finished(); - }); } } diff --git a/src/MxcImageProvider.h b/src/MxcImageProvider.h index f7580bca..7b960836 100644 --- a/src/MxcImageProvider.h +++ b/src/MxcImageProvider.h @@ -10,21 +10,18 @@ #include #include -#include +#include -#include +#include class MxcImageResponse : public QQuickImageResponse , public QRunnable { public: - MxcImageResponse(const QString &id, - const QSize &requestedSize, - boost::optional encryptionInfo) + MxcImageResponse(const QString &id, const QSize &requestedSize) : m_id(id) , m_requestedSize(requestedSize) - , m_encryptionInfo(encryptionInfo) { setAutoDelete(false); } @@ -40,7 +37,6 @@ public: QString m_id, m_error; QSize m_requestedSize; QImage m_image; - boost::optional m_encryptionInfo; }; class MxcImageProvider @@ -50,24 +46,13 @@ class MxcImageProvider Q_OBJECT public slots: QQuickImageResponse *requestImageResponse(const QString &id, - const QSize &requestedSize) override - { - boost::optional info; - auto temp = infos.find("mxc://" + id); - if (temp != infos.end()) - info = *temp; + const QSize &requestedSize) override; - MxcImageResponse *response = new MxcImageResponse(id, requestedSize, info); - pool.start(response); - return response; - } - - void addEncryptionInfo(mtx::crypto::EncryptedFile info) - { - infos.insert(QString::fromStdString(info.url), info); - } + static void addEncryptionInfo(mtx::crypto::EncryptedFile info); + static void download(const QString &id, + const QSize &requestedSize, + std::function then); private: QThreadPool pool; - QHash infos; };