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"