File indexing completed on 2024-04-21 15:40:26

0001 // SPDX-FileCopyrightText: 2018-2019 Black Hat <bhat@encom.eu.org>
0002 // SPDX-FileCopyrightText: 2019 Kitsune Ral <kitsune-ral@users.sf.net>
0003 // SPDX-License-Identifier: GPL-3.0-only
0004 
0005 #include "matriximageprovider.h"
0006 
0007 #include <QDebug>
0008 #include <QDir>
0009 #include <QFileInfo>
0010 #include <QStandardPaths>
0011 #include <QThread>
0012 
0013 #include <KLocalizedString>
0014 
0015 #include "controller.h"
0016 #include <Quotient/connection.h>
0017 
0018 using namespace Quotient;
0019 
0020 ThumbnailResponse::ThumbnailResponse(QString id, QSize size)
0021     : mediaId(std::move(id))
0022     , requestedSize(size)
0023     , localFile(QStringLiteral("%1/image_provider/%2-%3x%4.png")
0024                     .arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation),
0025                          mediaId,
0026                          QString::number(requestedSize.width()),
0027                          QString::number(requestedSize.height())))
0028     , errorStr("Image request hasn't started")
0029 {
0030     if (requestedSize.isEmpty()) {
0031         requestedSize.setHeight(100);
0032         requestedSize.setWidth(100);
0033     }
0034     if (mediaId.count('/') != 1) {
0035         if (mediaId.startsWith(QLatin1Char('/'))) {
0036             mediaId = mediaId.mid(1);
0037         } else {
0038             errorStr = i18n("Media id '%1' doesn't follow server/mediaId pattern", mediaId);
0039             Q_EMIT finished();
0040             return;
0041         }
0042     }
0043 
0044     mediaId = mediaId.split(QLatin1Char('?'))[0];
0045 
0046     QImage cachedImage;
0047     if (cachedImage.load(localFile)) {
0048         image = cachedImage;
0049         errorStr.clear();
0050         Q_EMIT finished();
0051         return;
0052     }
0053 
0054     if (!Controller::instance().activeConnection()) {
0055         qWarning() << "Current connection is null";
0056         return;
0057     }
0058 
0059     // Execute a request on the main thread asynchronously
0060     moveToThread(Controller::instance().activeConnection()->thread());
0061     QMetaObject::invokeMethod(this, &ThumbnailResponse::startRequest, Qt::QueuedConnection);
0062 }
0063 
0064 void ThumbnailResponse::startRequest()
0065 {
0066     if (!Controller::instance().activeConnection()) {
0067         return;
0068     }
0069     // Runs in the main thread, not QML thread
0070     Q_ASSERT(QThread::currentThread() == Controller::instance().activeConnection()->thread());
0071     job = Controller::instance().activeConnection()->getThumbnail(mediaId, requestedSize);
0072     // Connect to any possible outcome including abandonment
0073     // to make sure the QML thread is not left stuck forever.
0074     connect(job, &BaseJob::finished, this, &ThumbnailResponse::prepareResult);
0075 }
0076 
0077 void ThumbnailResponse::prepareResult()
0078 {
0079     Q_ASSERT(QThread::currentThread() == job->thread());
0080     Q_ASSERT(job->error() != BaseJob::Pending);
0081     {
0082         QWriteLocker _(&lock);
0083         if (job->error() == BaseJob::Success) {
0084             image = job->thumbnail();
0085 
0086             QString localPath = QFileInfo(localFile).absolutePath();
0087             QDir dir;
0088             if (!dir.exists(localPath)) {
0089                 dir.mkpath(localPath);
0090             }
0091 
0092             image.save(localFile);
0093 
0094             errorStr.clear();
0095         } else if (job->error() == BaseJob::Abandoned) {
0096             errorStr = i18n("Image request has been cancelled");
0097             // qDebug() << "ThumbnailResponse: cancelled for" << mediaId;
0098         } else {
0099             errorStr = job->errorString();
0100             qWarning() << "ThumbnailResponse: no valid image for" << mediaId << "-" << errorStr;
0101         }
0102         job = nullptr;
0103     }
0104     Q_EMIT finished();
0105 }
0106 
0107 QQuickTextureFactory *ThumbnailResponse::textureFactory() const
0108 {
0109     QReadLocker _(&lock);
0110     return QQuickTextureFactory::textureFactoryForImage(image);
0111 }
0112 
0113 QString ThumbnailResponse::errorString() const
0114 {
0115     QReadLocker _(&lock);
0116     return errorStr;
0117 }
0118 
0119 QQuickImageResponse *MatrixImageProvider::requestImageResponse(const QString &id, const QSize &requestedSize)
0120 {
0121     return new ThumbnailResponse(id, requestedSize);
0122 }
0123 
0124 #include "moc_matriximageprovider.cpp"