2021-08-28 01:38:33 +03:00
|
|
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
2022-01-01 06:57:53 +03:00
|
|
|
// SPDX-FileCopyrightText: 2022 Nheko Contributors
|
2023-01-02 06:25:33 +03:00
|
|
|
// SPDX-FileCopyrightText: 2023 Nheko Contributors
|
2021-08-28 01:38:33 +03:00
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
|
|
|
|
#include "MxcMediaProxy.h"
|
|
|
|
|
|
|
|
#include <QDir>
|
|
|
|
#include <QFile>
|
|
|
|
#include <QFileInfo>
|
2021-11-15 05:35:35 +03:00
|
|
|
#include <QMediaMetaData>
|
2021-08-28 01:38:33 +03:00
|
|
|
#include <QMediaObject>
|
|
|
|
#include <QMediaPlayer>
|
|
|
|
#include <QMimeDatabase>
|
|
|
|
#include <QStandardPaths>
|
|
|
|
#include <QUrl>
|
|
|
|
|
2021-11-04 06:06:32 +03:00
|
|
|
#if defined(Q_OS_MACOS)
|
|
|
|
// TODO (red_sky): Remove for Qt6. See other ifdef below
|
|
|
|
#include <QTemporaryFile>
|
|
|
|
#endif
|
|
|
|
|
2023-02-14 07:51:31 +03:00
|
|
|
#include "ChatPage.h"
|
2021-08-28 01:38:33 +03:00
|
|
|
#include "EventAccessors.h"
|
|
|
|
#include "Logging.h"
|
|
|
|
#include "MatrixClient.h"
|
|
|
|
#include "timeline/TimelineModel.h"
|
2023-02-14 07:51:31 +03:00
|
|
|
#include "timeline/TimelineViewManager.h"
|
2021-08-28 01:38:33 +03:00
|
|
|
|
2021-11-15 05:35:35 +03:00
|
|
|
MxcMediaProxy::MxcMediaProxy(QObject *parent)
|
|
|
|
: QMediaPlayer(parent)
|
|
|
|
{
|
|
|
|
connect(this, &MxcMediaProxy::eventIdChanged, &MxcMediaProxy::startDownload);
|
|
|
|
connect(this, &MxcMediaProxy::roomChanged, &MxcMediaProxy::startDownload);
|
|
|
|
connect(this,
|
|
|
|
qOverload<QMediaPlayer::Error>(&MxcMediaProxy::error),
|
|
|
|
[this](QMediaPlayer::Error error) {
|
|
|
|
nhlog::ui()->info("Media player error {} and errorStr {}",
|
|
|
|
error,
|
|
|
|
this->errorString().toStdString());
|
|
|
|
});
|
|
|
|
connect(this, &MxcMediaProxy::mediaStatusChanged, [this](QMediaPlayer::MediaStatus status) {
|
|
|
|
nhlog::ui()->info("Media player status {} and error {}", status, this->error());
|
|
|
|
});
|
|
|
|
connect(this,
|
|
|
|
qOverload<const QString &, const QVariant &>(&MxcMediaProxy::metaDataChanged),
|
2022-10-04 00:57:30 +03:00
|
|
|
[this](const QString &t, const QVariant &) {
|
2021-11-15 05:35:35 +03:00
|
|
|
if (t == QMediaMetaData::Orientation)
|
|
|
|
emit orientationChanged();
|
|
|
|
});
|
2023-02-14 07:51:31 +03:00
|
|
|
|
|
|
|
connect(ChatPage::instance()->timelineManager()->rooms(),
|
|
|
|
&RoomlistModel::currentRoomChanged,
|
|
|
|
this,
|
|
|
|
&MxcMediaProxy::pause);
|
2021-11-15 05:35:35 +03:00
|
|
|
}
|
2021-08-28 01:38:33 +03:00
|
|
|
void
|
|
|
|
MxcMediaProxy::setVideoSurface(QAbstractVideoSurface *surface)
|
|
|
|
{
|
2021-12-29 00:30:12 +03:00
|
|
|
if (surface != m_surface) {
|
|
|
|
qDebug() << "Changing surface";
|
|
|
|
m_surface = surface;
|
|
|
|
setVideoOutput(m_surface);
|
|
|
|
emit videoSurfaceChanged();
|
|
|
|
}
|
2021-08-28 01:38:33 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
QAbstractVideoSurface *
|
|
|
|
MxcMediaProxy::getVideoSurface()
|
|
|
|
{
|
2021-09-18 01:22:33 +03:00
|
|
|
return m_surface;
|
2021-08-28 01:38:33 +03:00
|
|
|
}
|
|
|
|
|
2021-11-15 05:35:35 +03:00
|
|
|
int
|
|
|
|
MxcMediaProxy::orientation() const
|
|
|
|
{
|
2021-12-29 06:28:08 +03:00
|
|
|
nhlog::ui()->debug("metadata: {}", availableMetaData().join(QStringLiteral(",")).toStdString());
|
2021-11-15 05:35:35 +03:00
|
|
|
auto orientation = metaData(QMediaMetaData::Orientation).toInt();
|
|
|
|
nhlog::ui()->debug("Video orientation: {}", orientation);
|
|
|
|
return orientation;
|
|
|
|
}
|
|
|
|
|
2021-08-28 01:38:33 +03:00
|
|
|
void
|
|
|
|
MxcMediaProxy::startDownload()
|
|
|
|
{
|
2021-09-18 01:22:33 +03:00
|
|
|
if (!room_)
|
|
|
|
return;
|
|
|
|
if (eventId_.isEmpty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
auto event = room_->eventById(eventId_);
|
|
|
|
if (!event) {
|
|
|
|
nhlog::ui()->error("Failed to load media for event {}, event not found.",
|
|
|
|
eventId_.toStdString());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-12-29 00:30:12 +03:00
|
|
|
QString mxcUrl = QString::fromStdString(mtx::accessors::url(*event));
|
|
|
|
QString mimeType = QString::fromStdString(mtx::accessors::mimetype(*event));
|
2021-09-18 01:22:33 +03:00
|
|
|
|
|
|
|
auto encryptionInfo = mtx::accessors::file(*event);
|
|
|
|
|
|
|
|
// If the message is a link to a non mxcUrl, don't download it
|
2021-12-29 06:28:08 +03:00
|
|
|
if (!mxcUrl.startsWith(QLatin1String("mxc://"))) {
|
2021-09-18 01:22:33 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString suffix = QMimeDatabase().mimeTypeForName(mimeType).preferredSuffix();
|
|
|
|
|
|
|
|
const auto url = mxcUrl.toStdString();
|
2021-12-29 06:28:08 +03:00
|
|
|
const auto name = QString(mxcUrl).remove(QStringLiteral("mxc://"));
|
2021-12-29 00:30:12 +03:00
|
|
|
QFileInfo filename(
|
2021-12-29 06:28:08 +03:00
|
|
|
QStringLiteral("%1/media_cache/media/%2.%3")
|
2021-12-29 00:30:12 +03:00
|
|
|
.arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation), name, suffix));
|
2021-09-18 01:22:33 +03:00
|
|
|
if (QDir::cleanPath(name) != name) {
|
|
|
|
nhlog::net()->warn("mxcUrl '{}' is not safe, not downloading file", url);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
QDir().mkpath(filename.path());
|
|
|
|
|
|
|
|
QPointer<MxcMediaProxy> self = this;
|
|
|
|
|
2021-11-04 06:06:32 +03:00
|
|
|
auto processBuffer = [this, encryptionInfo, filename, self, suffix](QIODevice &device) {
|
2021-09-18 01:22:33 +03:00
|
|
|
if (!self)
|
|
|
|
return;
|
|
|
|
|
|
|
|
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()));
|
2022-10-26 02:10:35 +03:00
|
|
|
buffer.setData(temp.data(), static_cast<int>(temp.size()));
|
2021-09-18 01:22:33 +03:00
|
|
|
} else {
|
|
|
|
buffer.setData(device.readAll());
|
2021-08-28 01:38:33 +03:00
|
|
|
}
|
2021-09-18 01:22:33 +03:00
|
|
|
buffer.open(QIODevice::ReadOnly);
|
|
|
|
buffer.reset();
|
|
|
|
|
2021-11-04 06:06:32 +03:00
|
|
|
QTimer::singleShot(0, this, [this, filename, suffix, encryptionInfo] {
|
|
|
|
#if defined(Q_OS_MACOS)
|
|
|
|
if (encryptionInfo) {
|
|
|
|
// macOS has issues reading from a buffer in setMedia for whatever reason.
|
|
|
|
// Instead, write the buffer to a temporary file and read from that.
|
|
|
|
// This should be fixed in Qt6, so update this when we do that!
|
|
|
|
// TODO: REMOVE IN QT6
|
|
|
|
QTemporaryFile tempFile;
|
|
|
|
tempFile.setFileTemplate(tempFile.fileTemplate() + QLatin1Char('.') + suffix);
|
|
|
|
tempFile.open();
|
|
|
|
tempFile.write(buffer.data());
|
|
|
|
tempFile.close();
|
|
|
|
nhlog::ui()->debug("Playing media from temp buffer file: {}. Remove in QT6!",
|
|
|
|
filename.filePath().toStdString());
|
|
|
|
this->setMedia(QUrl::fromLocalFile(tempFile.fileName()));
|
|
|
|
} else {
|
|
|
|
nhlog::ui()->info(
|
|
|
|
"Playing buffer with size: {}, {}", buffer.bytesAvailable(), buffer.isOpen());
|
|
|
|
this->setMedia(QUrl::fromLocalFile(filename.filePath()));
|
|
|
|
}
|
|
|
|
#else
|
2021-11-04 06:54:51 +03:00
|
|
|
Q_UNUSED(suffix)
|
2021-11-04 06:06:32 +03:00
|
|
|
Q_UNUSED(encryptionInfo)
|
|
|
|
|
2021-09-18 01:22:33 +03:00
|
|
|
nhlog::ui()->info(
|
|
|
|
"Playing buffer with size: {}, {}", buffer.bytesAvailable(), buffer.isOpen());
|
|
|
|
this->setMedia(QMediaContent(filename.fileName()), &buffer);
|
2021-11-04 06:06:32 +03:00
|
|
|
#endif
|
2021-09-18 01:22:33 +03:00
|
|
|
emit loadedChanged();
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
if (filename.isReadable()) {
|
|
|
|
QFile f(filename.filePath());
|
|
|
|
if (f.open(QIODevice::ReadOnly)) {
|
|
|
|
processBuffer(f);
|
|
|
|
return;
|
2021-08-28 01:38:33 +03:00
|
|
|
}
|
2021-09-18 01:22:33 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
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-28 01:38:33 +03:00
|
|
|
}
|