nheko/src/ui/MxcAnimatedImage.cpp

210 lines
6.8 KiB
C++
Raw Normal View History

2021-08-29 05:20:23 +02:00
// SPDX-FileCopyrightText: 2021 Nheko Contributors
// SPDX-FileCopyrightText: 2022 Nheko Contributors
2021-08-29 05:20:23 +02:00
//
// SPDX-License-Identifier: GPL-3.0-or-later
#include "MxcAnimatedImage.h"
#include <QDir>
#include <QFileInfo>
#include <QMimeDatabase>
2022-09-30 03:27:05 +02:00
#include <QMovie>
2021-08-29 05:20:23 +02:00
#include <QQuickWindow>
#include <QSGImageNode>
#include <QStandardPaths>
#include "EventAccessors.h"
#include "Logging.h"
#include "MatrixClient.h"
#include "timeline/TimelineModel.h"
void
MxcAnimatedImage::startDownload()
{
2021-09-18 00:22:33 +02:00
if (!room_)
return;
if (eventId_.isEmpty())
return;
2021-08-29 05:20:23 +02:00
2021-09-18 00:22:33 +02:00
auto event = room_->eventById(eventId_);
if (!event) {
nhlog::ui()->error("Failed to load media for event {}, event not found.",
eventId_.toStdString());
return;
}
2021-08-29 05:20:23 +02:00
2021-09-18 00:22:33 +02:00
QByteArray mimeType = QString::fromStdString(mtx::accessors::mimetype(*event)).toUtf8();
2021-08-29 05:20:23 +02:00
2022-09-30 03:27:05 +02:00
static const auto movieFormats = QMovie::supportedFormats();
QByteArray imageFormat;
const auto imageFormats = QImageReader::imageFormatsForMimeType(mimeType);
if (!imageFormats.isEmpty())
imageFormat = imageFormats.front();
animatable_ = movieFormats.contains(imageFormat);
2021-09-18 00:22:33 +02:00
animatableChanged();
2021-08-29 05:20:23 +02:00
2021-09-18 00:22:33 +02:00
if (!animatable_)
return;
2021-08-29 05:20:23 +02:00
2021-12-28 22:30:12 +01:00
QString mxcUrl = QString::fromStdString(mtx::accessors::url(*event));
2021-08-29 05:20:23 +02:00
2021-09-18 00:22:33 +02:00
auto encryptionInfo = mtx::accessors::file(*event);
2021-08-29 05:20:23 +02:00
2021-09-18 00:22:33 +02:00
// If the message is a link to a non mxcUrl, don't download it
if (!mxcUrl.startsWith(QLatin1String("mxc://"))) {
2021-09-18 00:22:33 +02:00
return;
}
QString suffix = QMimeDatabase().mimeTypeForName(mimeType).preferredSuffix();
const auto url = mxcUrl.toStdString();
const auto name = QString(mxcUrl).remove(QStringLiteral("mxc://"));
2021-12-28 22:30:12 +01:00
QFileInfo filename(
QStringLiteral("%1/media_cache/media/%2.%3")
.arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation), name, suffix));
2021-09-18 00:22:33 +02:00
if (QDir::cleanPath(name) != name) {
nhlog::net()->warn("mxcUrl '{}' is not safe, not downloading file", url);
return;
}
QDir().mkpath(filename.path());
QPointer<MxcAnimatedImage> self = this;
2022-09-30 03:27:05 +02:00
auto processBuffer = [this, imageFormat, encryptionInfo, self](QIODevice &device) {
2021-09-18 00:22:33 +02:00
if (!self)
return;
2021-08-29 05:20:23 +02:00
2022-05-04 08:26:03 +02:00
try {
if (buffer.isOpen()) {
2022-09-30 03:27:05 +02:00
frameTimer.stop();
movie->setDevice(nullptr);
2022-05-04 08:26:03 +02:00
buffer.close();
}
if (encryptionInfo) {
QByteArray ba = device.readAll();
std::string temp(ba.constData(), ba.size());
temp =
mtx::crypto::to_string(mtx::crypto::decrypt_file(temp, encryptionInfo.value()));
buffer.setData(temp.data(), temp.size());
} else {
buffer.setData(device.readAll());
}
buffer.open(QIODevice::ReadOnly);
buffer.reset();
} catch (const std::exception &e) {
nhlog::net()->error("Failed to setup animated image buffer: {}", e.what());
2021-08-29 05:20:23 +02:00
}
2022-09-30 03:27:05 +02:00
QTimer::singleShot(0, this, [this, imageFormat] {
2021-09-18 00:22:33 +02:00
nhlog::ui()->info(
"Playing movie with size: {}, {}", buffer.bytesAvailable(), buffer.isOpen());
2022-09-30 03:27:05 +02:00
movie->setFormat(imageFormat);
movie->setDevice(&buffer);
if (height() != 0 && width() != 0)
2022-09-30 03:27:05 +02:00
movie->setScaledSize(this->size().toSize());
if (movie->supportsAnimation())
frameTimer.setInterval(movie->nextImageDelay());
2021-09-18 00:22:33 +02:00
if (play_)
2022-09-30 03:27:05 +02:00
frameTimer.start();
2021-09-18 00:22:33 +02:00
else
2022-09-30 03:27:05 +02:00
movie->jumpToImage(0);
2021-09-18 00:22:33 +02:00
emit loadedChanged();
update();
});
};
if (filename.isReadable()) {
QFile f(filename.filePath());
if (f.open(QIODevice::ReadOnly)) {
processBuffer(f);
return;
}
}
http::client()->download(url,
[filename, url, processBuffer](const std::string &data,
const std::string &,
const std::string &,
mtx::http::RequestErr err) {
if (err) {
nhlog::net()->warn("failed to retrieve media {}: {} {}",
url,
err->matrix_error.error,
static_cast<int>(err->status_code));
return;
}
try {
QFile file(filename.filePath());
if (!file.open(QIODevice::WriteOnly))
return;
QByteArray ba(data.data(), (int)data.size());
file.write(ba);
file.close();
QBuffer buf(&ba);
buf.open(QBuffer::ReadOnly);
processBuffer(buf);
} catch (const std::exception &e) {
nhlog::ui()->warn("Error while saving file to: {}", e.what());
}
});
2021-08-29 05:20:23 +02:00
}
void
MxcAnimatedImage::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
{
QQuickItem::geometryChanged(newGeometry, oldGeometry);
if (newGeometry.size() != oldGeometry.size()) {
if (height() != 0 && width() != 0) {
2022-09-30 03:27:05 +02:00
QSizeF r = movie->scaledSize();
r.scale(newGeometry.size(), Qt::KeepAspectRatio);
2022-09-30 03:27:05 +02:00
movie->setScaledSize(r.toSize());
imageDirty = true;
update();
}
}
}
2021-08-29 05:20:23 +02:00
QSGNode *
MxcAnimatedImage::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *)
{
2021-09-18 00:22:33 +02:00
if (!imageDirty)
return oldNode;
imageDirty = false;
QSGImageNode *n = static_cast<QSGImageNode *>(oldNode);
if (!n) {
n = window()->createImageNode();
n->setOwnsTexture(true);
// n->setFlags(QSGNode::OwnedByParent | QSGNode::OwnsGeometry |
// GSGNode::OwnsMaterial);
n->setFlags(QSGNode::OwnedByParent);
}
2022-09-30 03:27:05 +02:00
n->setSourceRect(currentFrame.rect());
if (!currentFrame.isNull())
n->setTexture(window()->createTextureFromImage(currentFrame));
2021-09-18 00:22:33 +02:00
else {
delete n;
return nullptr;
}
2022-09-30 03:27:05 +02:00
QSizeF r = currentFrame.size();
r.scale(size(), Qt::KeepAspectRatio);
n->setRect((width() - r.width()) / 2, (height() - r.height()) / 2, r.width(), r.height());
n->setFiltering(QSGTexture::Linear);
n->setMipmapFiltering(QSGTexture::None);
2021-09-18 00:22:33 +02:00
return n;
2021-08-29 05:20:23 +02:00
}