mirror of https://github.com/Nheko-Reborn/nheko
parent
4ddf067408
commit
09c041c8ac
@ -0,0 +1,142 @@ |
||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "MxcMediaProxy.h" |
||||
|
||||
#include <QDir> |
||||
#include <QFile> |
||||
#include <QFileInfo> |
||||
#include <QMediaObject> |
||||
#include <QMediaPlayer> |
||||
#include <QMimeDatabase> |
||||
#include <QStandardPaths> |
||||
#include <QUrl> |
||||
|
||||
#include "EventAccessors.h" |
||||
#include "Logging.h" |
||||
#include "MatrixClient.h" |
||||
#include "timeline/TimelineModel.h" |
||||
|
||||
void |
||||
MxcMediaProxy::setVideoSurface(QAbstractVideoSurface *surface) |
||||
{ |
||||
qDebug() << "Changing surface"; |
||||
m_surface = surface; |
||||
setVideoOutput(m_surface); |
||||
} |
||||
|
||||
QAbstractVideoSurface * |
||||
MxcMediaProxy::getVideoSurface() |
||||
{ |
||||
return m_surface; |
||||
} |
||||
|
||||
void |
||||
MxcMediaProxy::startDownload() |
||||
{ |
||||
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; |
||||
} |
||||
|
||||
QString mxcUrl = QString::fromStdString(mtx::accessors::url(*event)); |
||||
QString originalFilename = QString::fromStdString(mtx::accessors::filename(*event)); |
||||
QString mimeType = QString::fromStdString(mtx::accessors::mimetype(*event)); |
||||
|
||||
auto encryptionInfo = mtx::accessors::file(*event); |
||||
|
||||
// If the message is a link to a non mxcUrl, don't download it
|
||||
if (!mxcUrl.startsWith("mxc://")) { |
||||
return; |
||||
} |
||||
|
||||
QString suffix = QMimeDatabase().mimeTypeForName(mimeType).preferredSuffix(); |
||||
|
||||
const auto url = mxcUrl.toStdString(); |
||||
const auto name = QString(mxcUrl).remove("mxc://"); |
||||
QFileInfo filename(QString("%1/media_cache/media/%2.%3") |
||||
.arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)) |
||||
.arg(name) |
||||
.arg(suffix)); |
||||
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; |
||||
|
||||
auto processBuffer = [this, encryptionInfo, filename, self](QIODevice &device) { |
||||
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())); |
||||
buffer.setData(temp.data(), temp.size()); |
||||
} else { |
||||
buffer.setData(device.readAll()); |
||||
} |
||||
buffer.open(QIODevice::ReadOnly); |
||||
buffer.reset(); |
||||
|
||||
QTimer::singleShot(0, this, [this, self, filename] { |
||||
nhlog::ui()->info("Playing buffer with size: {}, {}", |
||||
buffer.bytesAvailable(), |
||||
buffer.isOpen()); |
||||
self->setMedia(QMediaContent(filename.fileName()), &buffer); |
||||
emit loadedChanged(); |
||||
}); |
||||
}; |
||||
|
||||
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()); |
||||
} |
||||
}); |
||||
} |
@ -0,0 +1,80 @@ |
||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once |
||||
|
||||
#include <QAbstractVideoSurface> |
||||
#include <QBuffer> |
||||
#include <QMediaContent> |
||||
#include <QMediaPlayer> |
||||
#include <QObject> |
||||
#include <QPointer> |
||||
#include <QString> |
||||
|
||||
#include "Logging.h" |
||||
|
||||
class TimelineModel; |
||||
|
||||
// I failed to get my own buffer into the MediaPlayer in qml, so just make our own. For that we just
|
||||
// need the videoSurface property, so that part is really easy!
|
||||
class MxcMediaProxy : public QMediaPlayer |
||||
{ |
||||
Q_OBJECT |
||||
Q_PROPERTY(TimelineModel *roomm READ room WRITE setRoom NOTIFY roomChanged REQUIRED) |
||||
Q_PROPERTY(QString eventId READ eventId WRITE setEventId NOTIFY eventIdChanged) |
||||
Q_PROPERTY(QAbstractVideoSurface *videoSurface READ getVideoSurface WRITE setVideoSurface) |
||||
Q_PROPERTY(bool loaded READ loaded NOTIFY loadedChanged) |
||||
public: |
||||
MxcMediaProxy(QObject *parent = nullptr) |
||||
: 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()); |
||||
}); |
||||
} |
||||
|
||||
bool loaded() const { return buffer.size() > 0; } |
||||
QString eventId() const { return eventId_; } |
||||
TimelineModel *room() const { return room_; } |
||||
void setEventId(QString newEventId) |
||||
{ |
||||
eventId_ = newEventId; |
||||
emit eventIdChanged(); |
||||
} |
||||
void setRoom(TimelineModel *room) |
||||
{ |
||||
room_ = room; |
||||
emit roomChanged(); |
||||
} |
||||
void setVideoSurface(QAbstractVideoSurface *surface); |
||||
QAbstractVideoSurface *getVideoSurface(); |
||||
|
||||
signals: |
||||
void roomChanged(); |
||||
void eventIdChanged(); |
||||
void loadedChanged(); |
||||
void newBuffer(QMediaContent, QIODevice *buf); |
||||
|
||||
private slots: |
||||
void startDownload(); |
||||
|
||||
private: |
||||
TimelineModel *room_ = nullptr; |
||||
QString eventId_; |
||||
QString filename_; |
||||
QBuffer buffer; |
||||
QAbstractVideoSurface *m_surface = nullptr; |
||||
}; |
Loading…
Reference in new issue