File indexing completed on 2024-09-29 10:12:46
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"