Added Image download functionality

This commit is contained in:
Digital Artifex
2026-08-17 13:05:17 -04:00
parent e6d7d908b1
commit 1d5bcb9365
9 changed files with 665 additions and 180 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ add_library(
qt_add_qml_module(
KomplexHubPlugin
URI
KomplexHubPlugin
"KomplexHub.Plugin"
VERSION
1.0
SOURCES
+22 -19
View File
@@ -12,28 +12,31 @@ auto CoreServices::networkAccessManager() -> QWeakPointer<QNetworkAccessManager>
if(!s_networkAccessPointer)
{
s_networkAccessManager = new QNetworkAccessManager(QCoreApplication::instance());
s_networkAccessManager->setAutoDeleteReplies(true);
if(!s_networkAccessManager)
{
s_networkAccessManager = new QNetworkAccessManager(QCoreApplication::instance());
s_networkAccessManager->setAutoDeleteReplies(true);
QObject::connect(
s_networkAccessManager,
&QNetworkAccessManager::sslErrors,
s_networkAccessManager,
[] (QNetworkReply *reply, const QList<QSslError> &errors)
{
for(const auto &error : errors)
QObject::connect(
s_networkAccessManager,
&QNetworkAccessManager::sslErrors,
s_networkAccessManager,
[] (QNetworkReply *reply, const QList<QSslError> &errors)
{
LOG_ERROR(
"CoreServices::networkAccessManager",
error.errorString().toStdString().c_str()
);
}
for(const auto &error : errors)
{
LOG_ERROR(
"CoreServices::networkAccessManager",
error.errorString().toStdString().c_str()
);
}
#ifdef KOMPLEX_LOCAL_DEV
reply->ignoreSslErrors();
#endif
}
);
#ifdef KOMPLEX_LOCAL_DEV
reply->ignoreSslErrors();
#endif
}
);
}
s_networkAccessPointer = QSharedPointer<QNetworkAccessManager>(
s_networkAccessManager,
+161 -103
View File
@@ -31,6 +31,44 @@ DownloadManager::DownloadManager(QObject *parent)
this,
&DownloadManager::compileStepsChanged
);
QObject::connect
(
&m_moveProcess,
&QProcess::readyReadStandardOutput,
this,
[this]()
{
QByteArray processData = m_moveProcess.readAllStandardOutput();
if(!processData.isValidUtf8())
{
qWarning() << QStringLiteral("Process output not valid UTF8 data");
return;
}
setCompilerOutput(m_compilerOutput + processData);
}
);
QObject::connect
(
&m_moveProcess,
&QProcess::readyReadStandardError,
this,
[this]()
{
QByteArray processData = m_moveProcess.readAllStandardError();
if(!processData.isValidUtf8())
{
qWarning() << QStringLiteral("Process output not valid UTF8 data");
return;
}
setCompilerOutput(m_compilerOutput + processData);
}
);
}
/**
@@ -39,30 +77,42 @@ DownloadManager::DownloadManager(QObject *parent)
* [3] Optionally ask user if they would like to apply manipulative shader
* [4] Install locally
*/
auto DownloadManager::downloadImage(const QString &author, const QString &authorId, const QString &description, const QUrl &url) noexcept(false) -> void
auto DownloadManager::downloadImage(const QString &author, const QString &authorId, const QString &description, const QUrl &url) -> void
{
QFuture future = QtConcurrent::run
(
[this, url, author, authorId, description]()
{
// QFuture future = QtConcurrent::run
// (
// [this, url, author, authorId, description]()
// {
QNetworkRequest request(url);
QString requestUrl = request.url().toString();
QString id = QUuid::createUuidV7().toString(QUuid::WithoutBraces);
QFuture<QUrl> downloadUri = download(request, id);
setState(Downloading);
downloadUri
//QFuture<QUrl> downloadUri = download(request, id, Get);
if(m_downloadFuture.isRunning())
{
m_downloadFuture.cancelChain();
}
m_downloadFuture = download(request, id, Get);
m_downloadFuture
.then
(
[this, author, description, id](QUrl result) -> QUrl
{
setState(Installing);
ShaderPack metadata;
metadata.setAuthor(author);
metadata.setDescription(description);
metadata.setName(QStringLiteral("Pexels Image (%1)").arg(id));
metadata.setSource(QStringLiteral("./images/%1").arg(result.fileName()));
QUrl packUri
(
QStringLiteral("%1/%2").arg
QStringLiteral("file://%1/%2").arg
(
QStandardPaths::writableLocation(QStandardPaths::TempLocation),
id
@@ -71,7 +121,7 @@ auto DownloadManager::downloadImage(const QString &author, const QString &author
QUrl packImageUri
(
QStringLiteral("%1/%2/images/%3").arg
QStringLiteral("file://%1/%2/images/%3").arg
(
QStandardPaths::writableLocation(QStandardPaths::TempLocation),
id,
@@ -80,7 +130,12 @@ auto DownloadManager::downloadImage(const QString &author, const QString &author
);
QDir packDirectory(packUri.toLocalFile());
packDirectory.mkpath(packUri.toLocalFile());
if(!packDirectory.mkpath(packUri.toLocalFile()))
{
throw FileException(QStringLiteral("Could not create directory"));
}
packDirectory.mkdir(QStringLiteral("images"));
QFile::rename(result.toLocalFile(), packImageUri.toLocalFile());
@@ -99,7 +154,7 @@ auto DownloadManager::downloadImage(const QString &author, const QString &author
throw FileException(packFile.errorString());
}
QByteArray data = metadata.json().toJson(QJsonDocument::Compact);
QByteArray data = metadata.json().toJson(QJsonDocument::Indented);
if(packFile.write(data) != data.length())
{
@@ -113,10 +168,18 @@ auto DownloadManager::downloadImage(const QString &author, const QString &author
}
)
.then
(
[this](QUrl result) -> QUrl
{
return install(result);
}
)
.then
(
[this](QUrl result)
{
install(result);
setLastInstalledFile(result.toLocalFile());
setState(Complete);
}
)
.onCanceled
@@ -127,17 +190,36 @@ auto DownloadManager::downloadImage(const QString &author, const QString &author
}
)
.onFailed
(
[this] (const NetworkException &e)
{
reset();
setError(QStringLiteral("Network Exception %1").arg(QString::number(e.errorCode)), e.message);
}
)
.onFailed
(
[this] (const FileException &e)
{
reset();
setError(QStringLiteral("File Exception %1").arg(QString::number(e.errorCode)), e.message);
}
)
.onFailed
(
[this] ()
{
reset();
setError(QStringLiteral("Unknown Exception"), QString());
}
);
}
);
// downloadUri.waitForFinished();
// }
// );
}
auto DownloadManager::downloadVideo(const QString &author, const QString &authorId, const QString &description, const QUrl &url) noexcept(false) -> void
auto DownloadManager::downloadVideo(const QString &author, const QString &authorId, const QString &description, const QUrl &url) -> void
{
QFuture future = QtConcurrent::run
(
@@ -236,7 +318,7 @@ auto DownloadManager::downloadVideo(const QString &author, const QString &author
);
}
auto DownloadManager::downloadPack(const QString &id) noexcept(false) -> void
auto DownloadManager::downloadPack(const QString &id) -> void
{
QFuture aether = QtConcurrent::run
(
@@ -267,9 +349,16 @@ auto DownloadManager::downloadPack(const QString &id) noexcept(false) -> void
)
.then
(
[this](QUrl result)
[this](QUrl result) -> QUrl
{
install(result);
return install(result);
}
)
.then
(
[this] (QUrl result)
{
setState(Complete);
}
)
.onFailed
@@ -337,6 +426,8 @@ auto DownloadManager::setError(const QString &title, const QString &message) ->
m_errorMessage = std::move(message);
m_errorTitle = std::move(title);
Q_EMIT errorChanged();
setState(Error);
}
void DownloadManager::setCompilerOutput(const QString &compilerOutput)
@@ -383,89 +474,45 @@ auto DownloadManager::install(const QUrl &uri) noexcept(false) -> QUrl
QDir tempDirectory(uri.toLocalFile());
QString installLocation = QStringLiteral("%1/.local/share/komplex/packs/%2").arg
QUrl installLocation = QStringLiteral("%1/.local/share/komplex/packs/%2").arg
(
QStandardPaths::writableLocation(QStandardPaths::HomeLocation),
tempDirectory.dirName()
);
QDir installDirectory(installLocation);
QDir installDirectory(installLocation.toLocalFile());
if(installDirectory.exists())
installDirectory.removeRecursively();
QProcess process;
QObject::connect
(
&process,
&QProcess::readyReadStandardOutput,
this,
[this, &process]()
{
QByteArray processData = process.readAllStandardOutput();
if(!processData.isValidUtf8())
{
qWarning() << QStringLiteral("Process output not valid UTF8 data");
return;
}
setCompilerOutput(m_compilerOutput + processData);
}
);
QObject::connect
(
&process,
&QProcess::readyReadStandardError,
this,
[this, &process]()
{
QByteArray processData = process.readAllStandardError();
if(!processData.isValidUtf8())
{
qWarning() << QStringLiteral("Process output not valid UTF8 data");
return;
}
setCompilerOutput(m_compilerOutput + processData);
}
);
QStringList arguments = QStringList
{
QStringLiteral("-R"),
uri.toLocalFile(),
installLocation
installLocation.toLocalFile()
};
process.start(QStringLiteral("cp"), arguments);
m_moveProcess.start(QStringLiteral("mv"), arguments);
if(!process.waitForStarted(3000))
if(!m_moveProcess.waitForStarted(3000))
{
qWarning() << QStringLiteral("Could not start copy process: %1").arg(process.readAllStandardError());
qWarning() << QStringLiteral("Could not start copy process: %1").arg(m_moveProcess.readAllStandardError());
throw FileException(QStringLiteral("Could not start install process"));
}
if(!process.waitForFinished())
if(!m_moveProcess.waitForFinished())
{
qWarning() << QStringLiteral("Copy process took longer than expected (>30s)");
throw FileException(QStringLiteral("Install process took longer than expected (>30s)"));
}
QUrl installUri(installLocation);
installUri.setScheme(QStringLiteral("file://"));
return installUri;
return installLocation;
}
auto DownloadManager::download(const QNetworkRequest &request, const QString &id, RequestType type) -> QFuture<QUrl>
{
return QtConcurrent::run
(
[this, request, id]() -> QUrl
[this, request, id, type]() -> QUrl
{
QEventLoop loop;
QString filename = id;
@@ -475,10 +522,10 @@ auto DownloadManager::download(const QNetworkRequest &request, const QString &id
filename = QUuid::createUuidV7().toString();
}
QUrl downloadUri = QStringLiteral("%1/%2").arg
QUrl downloadUri = QStringLiteral("file://%1/%2").arg
(
QStandardPaths::writableLocation(QStandardPaths::TempLocation),
filename
request.url().fileName()
);
auto manager = CoreServices::networkAccessManager().toStrongRef();
@@ -493,20 +540,23 @@ auto DownloadManager::download(const QNetworkRequest &request, const QString &id
QFile downloadFile(downloadUri.toLocalFile());
if(!downloadFile.open(QFile::ReadWrite))
if(downloadFile.exists() && !downloadFile.remove())
{
throw FileException(QStringLiteral("Could not open temp file location"));
throw FileException(downloadFile.errorString());
}
QNetworkReply *reply = manager->post(request, nullptr);
QNetworkReply *reply = nullptr;
QObject::connect
(
reply,
&QNetworkReply::finished,
&loop,
&QEventLoop::quit
);
switch(type)
{
case Post:
reply = manager->post(request, nullptr);
break;
default:
case Get:
reply = manager->get(request, nullptr);
break;
}
QObject::connect
(
@@ -545,40 +595,35 @@ auto DownloadManager::download(const QNetworkRequest &request, const QString &id
reply,
&QNetworkReply::readyRead,
this,
[this, &downloadFile, &reply]()
[this, downloadUri, reply]()
{
if(!downloadFile.isOpen())
QFile downloadFile(downloadUri.toLocalFile());
if(!downloadFile.open(QFile::ReadWrite | QFile::Append))
{
return;
throw FileException(QStringLiteral("Could not open temp file location"));
}
quint64 bytes = reply->bytesAvailable();
quint64 bytesWritten = downloadFile.write(reply->read(bytes));
quint64 bytesWritten = downloadFile.write(reply->readAll()/*bytes)*/);
if(bytesWritten != bytes)
{
throw FileException(QStringLiteral("Could not write temp file data"));
}
downloadFile.close();
}
);
QObject::connect
(
reply,
&QNetworkReply::finished,
&loop,
&QEventLoop::quit
);
if(!reply->isFinished())
{
loop.exec();
}
if(reply->bytesAvailable() > 0)
{
quint64 bytes = reply->bytesAvailable();
quint64 bytesWritten = downloadFile.write(reply->read(bytes));
if(bytesWritten != bytes)
{
throw FileException(QStringLiteral("Could not write temp file data"));
}
}
downloadFile.close();
manager.clear();
return downloadUri;
@@ -717,6 +762,19 @@ auto DownloadManager::readShaderToyEntry(const QUrl &uri) noexcept(false) -> Sha
return std::move(entry);
}
auto DownloadManager::lastInstalledFile() const -> const QString &
{
return m_lastInstalledFile;
}
auto DownloadManager::setLastInstalledFile(const QString &lastInstalledFile) -> void
{
if (m_lastInstalledFile == lastInstalledFile)
return;
m_lastInstalledFile = lastInstalledFile;
emit lastInstalledFileChanged();
}
auto DownloadManager::setDownloadedBytes(qint64 downloadedBytes) -> void
{
if (m_downloadedBytes == downloadedBytes)
+18 -6
View File
@@ -68,13 +68,13 @@ public:
* out of it
* @param url
*/
auto downloadImage
Q_INVOKABLE auto downloadImage
(
const QString &author,
const QString &authorId,
const QString &description,
const QUrl &url
) noexcept(false) -> void;
) -> void;
/**
* @brief downloadVideo
@@ -85,13 +85,13 @@ public:
* @param description
* @param url
*/
auto downloadVideo
Q_INVOKABLE auto downloadVideo
(
const QString &author,
const QString &authorId,
const QString &description,
const QUrl &url
) noexcept(false) -> void;
) -> void;
/**
* @brief downloadPack
@@ -100,13 +100,13 @@ public:
* @param id
* @return File info of the installed pack
*/
auto downloadPack(const QString &id) noexcept(false) -> void;
Q_INVOKABLE auto downloadPack(const QString &id) -> void;
auto compilerOutput() const -> const QString & { return m_compilerOutput; }
auto errorTitle() const -> const QString & { return m_errorTitle; }
auto errorMessage() const -> const QString & { return m_errorMessage; }
auto state() const -> State { return m_state; }
auto reset() -> void;
Q_INVOKABLE auto reset() -> void;
auto downloadProgress() -> qreal { return m_downloadProgress; }
auto compileProgress() -> qreal;
auto compileSteps() -> qint64;
@@ -118,6 +118,9 @@ public:
auto downloadedBytes() const -> qint64 { return m_downloadedBytes; }
auto setDownloadedBytes(qint64 downloadedBytes) -> void;
auto lastInstalledFile() const -> const QString &;
auto setLastInstalledFile(const QString &lastInstalledFile) -> void;
protected:
auto setError(const QString &title, const QString &message) -> void;
auto setCompilerOutput(const QString &compilerOutput) -> void;
@@ -136,12 +139,16 @@ signals:
auto compileStepsChanged() -> void;
auto compileStepsCompletedChanged() -> void;
auto lastInstalledFileChanged() -> void;
private:
auto compile(const QUrl &uri) noexcept(false) -> QUrl;
auto install(const QUrl &uri) noexcept(false) -> QUrl;
auto download(const QNetworkRequest &request, const QString &id, RequestType type = Get) -> QFuture<QUrl>;
auto readShaderToyEntry(const QUrl &uri) noexcept(false) -> ShaderToyEntry;
QString m_lastInstalledFile;
QString m_compilerOutput;
QString m_errorTitle;
QString m_errorMessage;
@@ -156,6 +163,10 @@ private:
PackCompiler *m_compiler = nullptr;
QProcess m_moveProcess;
QFuture<QUrl> m_downloadFuture;
Q_PROPERTY(QString compilerOutput READ compilerOutput WRITE setCompilerOutput NOTIFY compilerOutputChanged FINAL)
Q_PROPERTY(QString errorTitle READ errorTitle NOTIFY errorChanged FINAL)
Q_PROPERTY(QString errorMessage READ errorMessage NOTIFY errorChanged FINAL)
@@ -166,6 +177,7 @@ private:
Q_PROPERTY(qint64 downloadedBytes READ downloadedBytes WRITE setDownloadedBytes NOTIFY downloadedBytesChanged FINAL)
Q_PROPERTY(qint64 compileSteps READ compileSteps NOTIFY compileStepsChanged FINAL)
Q_PROPERTY(qint64 compileStepsCompleted READ compileStepsCompleted NOTIFY compileStepsCompletedChanged FINAL)
Q_PROPERTY(QString lastInstalledFile READ lastInstalledFile WRITE setLastInstalledFile NOTIFY lastInstalledFileChanged FINAL)
};
Q_DECLARE_METATYPE(DownloadManager)
+14 -7
View File
@@ -5,49 +5,56 @@ FeaturedImagesModel::FeaturedImagesModel(QObject *parent) : QAbstractListModel{p
m_paginator = new FeaturedImagesPaginator(this);
PaginationNotifier *notifier = static_cast<PaginationNotifier*>(m_paginator);
QObject::connect(
QObject::connect
(
notifier,
&PaginationNotifier::resultsPerPageChanged,
this,
&FeaturedImagesModel::resultsPerPageChanged
);
QObject::connect(
QObject::connect
(
notifier,
&PaginationNotifier::totalResultsChanged,
this,
&FeaturedImagesModel::totalResultsChanged
);
QObject::connect(
QObject::connect
(
notifier,
&PaginationNotifier::totalPagesChanged,
this,
&FeaturedImagesModel::totalPagesChanged
);
QObject::connect(
QObject::connect
(
notifier,
&PaginationNotifier::pageChanged,
this,
&FeaturedImagesModel::pageChanged
);
QObject::connect(
QObject::connect
(
notifier,
&PaginationNotifier::pageChanged,
this,
&FeaturedImagesModel::resetDataModel
);
QObject::connect(
QObject::connect
(
notifier,
&PaginationNotifier::fetchComplete,
this,
&FeaturedImagesModel::resetState
);
QObject::connect(
QObject::connect
(
notifier,
&PaginationNotifier::fetching,
this,