#include "mediaparticlegenerator.h" #include "networkmanager.h" #include "store.h" #include #include #include #include #include #include MediaParticleGenerator::MediaParticleGenerator(Store *store, QObject *parent) : QObject{parent}, m_store{store}, m_qnam{new QNetworkAccessManager{this}} { } bool MediaParticleGenerator::isGenerating() const { return m_isGenerating; } void MediaParticleGenerator::generate(const QUrl &url, const QString &mimeType, const QString &networkId, qint64 durationMs, const QString &streamId) { if (m_isGenerating) { qWarning() << "Already generating media particle, ignoring request"; return; } if (!url.isValid()) { emit errorOccurred("Invalid URL"); return; } assert(!mimeType.isEmpty() && "mimeType should never be empty here"); assert(!networkId.isEmpty() && "networkId should never be empty here"); QFile *file = new QFile{url.toLocalFile()}; assert(file->exists() && "File should exist at this point"); assert(file->size() > 0 && "File should have non-zero size at this point"); cleanup(); setGenerating(true); m_uploadInfo = new UploadInfo{}; m_uploadInfo->networkId = networkId; m_uploadInfo->streamId = streamId; m_uploadInfo->mimeType = mimeType; m_uploadInfo->durationMs = durationMs; m_uploadInfo->file = file; m_uploadInfo->bytes = file->size(); // 1. get presigned url QJsonObject body; body["network_id"] = m_uploadInfo->networkId; body["name"] = url.fileName(); body["content_type"] = m_uploadInfo->mimeType; body["content_length"] = m_uploadInfo->bytes; QNetworkReply *reply = NetworkManager::instance().post("/depot/upload", QJsonDocument(body)); connect(reply, &QNetworkReply::finished, this, [this, reply]() { reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { QByteArray body = reply->readAll(); QJsonDocument doc = QJsonDocument::fromJson(body); QString message = doc.isObject() ? doc.object().value("message").toString() : QString(); if (message.isEmpty()) { message = reply->errorString(); } qWarning() << message; cleanup(); emit errorOccurred("Failed to upload media"); return; } QJsonDocument doc = QJsonDocument::fromJson(reply->readAll()); onUploadUrlReceived(doc); }); } void MediaParticleGenerator::cleanup() { if (m_uploadInfo) { if (m_uploadInfo->file) { m_uploadInfo->file->close(); delete m_uploadInfo->file; } delete m_uploadInfo; m_uploadInfo = nullptr; } setGenerating(false); } void MediaParticleGenerator::onUploadUrlReceived(const QJsonDocument &response) { QJsonObject obj = response.object(); m_uploadInfo->uploadUrl = obj["upload_url"].toString(); QJsonObject uploadHeaders = obj["upload_headers"].toObject(); m_uploadInfo->objectId = obj["object_id"].toString(); if (!m_uploadInfo->file->open(QIODevice::ReadOnly)) { cleanup(); emit errorOccurred("Failed to open media file"); return; } QNetworkRequest request = QNetworkRequest(QUrl(m_uploadInfo->uploadUrl)); for (auto it = uploadHeaders.constBegin(); it != uploadHeaders.constEnd(); ++it) { request.setRawHeader(it.key().toUtf8(), it.value().toString().toUtf8()); } QNetworkReply *reply = m_qnam->put(request, m_uploadInfo->file); connect(reply, &QNetworkReply::finished, this, [this, reply]() { reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { qWarning() << "Failed to upload media: " << reply->errorString(); cleanup(); emit errorOccurred("Failed to upload media"); return; } onUploadComplete(); }); } void MediaParticleGenerator::setGenerating(bool generating) { if (m_isGenerating != generating) { m_isGenerating = generating; emit isGeneratingChanged(); } } void MediaParticleGenerator::onUploadComplete() { auto op = m_store->createParticle(m_uploadInfo->streamId, "media", { {"object_id", m_uploadInfo->objectId}, {"duration_ms", m_uploadInfo->durationMs}, {"mime_type", m_uploadInfo->mimeType} }); connect(op, &Operation::success, this, &MediaParticleGenerator::complete); connect(op, &Operation::success, this, &MediaParticleGenerator::cleanup); }