File indexing completed on 2024-09-29 07:24:44

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