Add support of multiple component urls

This commit is contained in:
Gres 2020-04-04 17:34:55 +03:00
parent 35cbdb17e8
commit 5f4ef955e1
2 changed files with 86 additions and 42 deletions

View File

@ -1,4 +1,4 @@
#include "updates.h"
#include "updates.h"
#include "debug.h"
#include <QApplication>
@ -39,6 +39,22 @@ QString toString(Action action)
return names.value(action);
}
QStringList toList(const QJsonValue &value)
{
if (value.isString())
return {value.toString()};
if (!value.isArray())
return {};
const auto array = value.toArray();
QStringList result;
for (const auto &i : array) {
if (i.isString())
result.append(i.toString());
}
return result;
}
} // namespace
Loader::Loader(const QUrl &updateUrl, QObject *parent)
@ -96,55 +112,76 @@ QString Loader::toError(QNetworkReply &reply) const
void Loader::applyUserActions()
{
SOFT_ASSERT(model_, return );
if (installer_ || !componentReplyToPath_.empty()) {
if (!currentActions_.empty() || !downloads_.empty()) {
emit error(tr("Update already in process"));
return;
}
auto actions = model_->userActions();
if (actions.empty()) {
currentActions_ = model_->userActions();
if (currentActions_.empty()) {
emit error(tr("No actions to apply"));
return;
}
for (auto &action : actions) {
for (auto &action : currentActions_) {
if (action.first != Action::Install)
continue;
auto &file = action.second;
auto reply = network_->get(QNetworkRequest(file.url));
if (reply->error() != QNetworkReply::NoError) {
finishUpdate(toError(*reply));
break;
}
connect(reply, &QNetworkReply::downloadProgress, //
this, &Loader::updateProgress);
file.downloadPath = downloadPath_ + '/' + file.rawPath;
componentReplyToPath_.emplace(reply, file.downloadPath);
if (!startDownload(file)) {
finishUpdate();
return;
}
}
installer_ = std::make_unique<Installer>(actions);
if (componentReplyToPath_.empty()) // no downloads
if (downloads_.empty()) // no downloads
commitUpdate();
}
void Loader::handleComponentReply(QNetworkReply *reply)
bool Loader::startDownload(File &file)
{
if (file.urls.empty())
return false;
auto reply = network_->get(QNetworkRequest(file.urls.takeFirst()));
downloads_.emplace(reply, &file);
connect(reply, &QNetworkReply::downloadProgress, //
this, &Loader::updateProgress);
if (reply->error() == QNetworkReply::NoError)
return true;
return handleComponentReply(reply);
}
bool Loader::handleComponentReply(QNetworkReply *reply)
{
reply->deleteLater();
if (reply->error() != QNetworkReply::NoError) {
finishUpdate(toError(*reply));
return;
if (currentActions_.empty()) { // aborted?
finishUpdate();
return false;
}
SOFT_ASSERT(componentReplyToPath_.count(reply) == 1, return );
SOFT_ASSERT(downloads_.count(reply) == 1, return false);
auto *file = downloads_[reply];
SOFT_ASSERT(file, return false);
auto replyIt = componentReplyToPath_.find(reply);
const auto &fileName = replyIt->second;
downloads_.erase(reply);
if (reply->error() != QNetworkReply::NoError) {
emit error(toError(*reply));
if (!startDownload(*file))
finishUpdate();
return false;
}
const auto &fileName = file->downloadPath;
auto dir = QFileInfo(fileName).absoluteDir();
if (!dir.exists())
dir.mkpath(".");
@ -155,23 +192,24 @@ void Loader::handleComponentReply(QNetworkReply *reply)
tr("Failed to save downloaded file %1 to %2. Error %3")
.arg(reply->url().toString(), f.fileName(), f.errorString());
finishUpdate(error);
return;
return false;
}
const auto replyData = reply->readAll();
f.write(replyData);
f.close();
componentReplyToPath_.erase(replyIt);
if (componentReplyToPath_.empty())
if (downloads_.empty())
commitUpdate();
return true;
}
void Loader::finishUpdate(const QString &error)
{
installer_.reset();
for (const auto &i : componentReplyToPath_) i.first->deleteLater();
componentReplyToPath_.clear();
currentActions_.clear();
for (const auto &i : downloads_) i.first->deleteLater();
downloads_.clear();
if (!error.isEmpty())
emit this->error(error);
SOFT_ASSERT(model_, return );
@ -180,12 +218,13 @@ void Loader::finishUpdate(const QString &error)
void Loader::commitUpdate()
{
SOFT_ASSERT(installer_, return );
if (installer_->commit()) {
SOFT_ASSERT(!currentActions_.empty(), return );
Installer installer(currentActions_);
if (installer.commit()) {
model_->resetProgress();
emit updated();
} else {
emit error(tr("Update failed: %1").arg(installer_->errorString()));
emit error(tr("Update failed: %1").arg(installer.errorString()));
}
finishUpdate();
}
@ -267,8 +306,12 @@ std::unique_ptr<Model::Component> Model::parse(const QJsonObject &json) const
for (const auto &fileInfo : files) {
const auto object = fileInfo.toObject();
File file;
file.url = object["url"].toString();
if (!file.url.isValid())
for (const auto &s : toList(object["url"])) {
const auto url = QUrl(s);
if (url.isValid())
file.urls.append(url);
}
if (file.urls.isEmpty())
result->checkOnly = true;
file.rawPath = object["path"].toString();
file.md5 = object["md5"].toString();
@ -302,7 +345,7 @@ void Model::updateProgress(Model::Component &component, const QUrl &url,
{
if (!component.files.empty()) {
for (auto &file : component.files) {
if (!url.isEmpty() && file.url != url)
if (!url.isEmpty() && !file.urls.contains(url))
continue;
file.progress = progress;

View File

@ -13,7 +13,7 @@ enum class State { NotAvailable, NotInstalled, UpdateAvailable, Actual };
enum class Action { NoAction, Remove, Install };
struct File {
QUrl url;
QVector<QUrl> urls;
QString rawPath;
QString expandedPath;
QString downloadPath;
@ -130,19 +130,20 @@ signals:
private:
void handleReply(QNetworkReply* reply);
void handleComponentReply(QNetworkReply* reply);
bool handleComponentReply(QNetworkReply* reply);
void handleUpdateReply(QNetworkReply* reply);
QString toError(QNetworkReply& reply) const;
void finishUpdate(const QString& error = {});
void commitUpdate();
void updateProgress(qint64 bytesSent, qint64 bytesTotal);
bool startDownload(File& file);
QNetworkAccessManager* network_;
Model* model_;
QUrl updateUrl_;
QString downloadPath_;
std::map<QNetworkReply*, QString> componentReplyToPath_;
std::unique_ptr<Installer> installer_;
std::map<QNetworkReply*, File*> downloads_;
UserActions currentActions_;
};
class AutoChecker : public QObject