Merge pull request #304 from Ryex/move-downloads

Fixes https://github.com/PrismLauncher/PrismLauncher/issues/222
This commit is contained in:
flow
2022-11-11 12:48:53 -08:00
committed by GitHub
11 changed files with 405 additions and 42 deletions

View File

@ -1,28 +1,186 @@
#include "BlockedModsDialog.h"
#include "ui_BlockedModsDialog.h"
#include <QPushButton>
#include <QDialogButtonBox>
#include <QDesktopServices>
#include <QDialogButtonBox>
#include <QPushButton>
#include "Application.h"
#include "ui_BlockedModsDialog.h"
#include <QDebug>
#include <QStandardPaths>
BlockedModsDialog::BlockedModsDialog(QWidget *parent, const QString &title, const QString &text, const QString &body, const QList<QUrl> &urls) :
QDialog(parent), ui(new Ui::BlockedModsDialog), urls(urls) {
BlockedModsDialog::BlockedModsDialog(QWidget* parent, const QString& title, const QString& text, QList<BlockedMod>& mods)
: QDialog(parent), ui(new Ui::BlockedModsDialog), mods(mods)
{
ui->setupUi(this);
auto openAllButton = ui->buttonBox->addButton(tr("Open All"), QDialogButtonBox::ActionRole);
connect(openAllButton, &QPushButton::clicked, this, &BlockedModsDialog::openAll);
connect(&watcher, &QFileSystemWatcher::directoryChanged, this, &BlockedModsDialog::directoryChanged);
hashing_task = shared_qobject_ptr<ConcurrentTask>(new ConcurrentTask(this, "MakeHashesTask", 10));
qDebug() << "Mods List: " << mods;
setupWatch();
scanPaths();
this->setWindowTitle(title);
ui->label->setText(text);
ui->textBrowser->setText(body);
ui->labelModsFound->setText(tr("Please download the missing mods."));
update();
}
BlockedModsDialog::~BlockedModsDialog() {
BlockedModsDialog::~BlockedModsDialog()
{
delete ui;
}
void BlockedModsDialog::openAll() {
for(auto &url : urls) {
QDesktopServices::openUrl(url);
void BlockedModsDialog::openAll()
{
for (auto& mod : mods) {
QDesktopServices::openUrl(mod.websiteUrl);
}
}
/// @brief update UI with current status of the blocked mod detection
void BlockedModsDialog::update()
{
QString text;
QString span;
for (auto& mod : mods) {
if (mod.matched) {
// &#x2714; -> html for HEAVY CHECK MARK : ✔
span = QString(tr("<span style=\"color:green\"> &#x2714; Found at %1 </span>")).arg(mod.localPath);
} else {
// &#x2718; -> html for HEAVY BALLOT X : ✘
span = QString(tr("<span style=\"color:red\"> &#x2718; Not Found </span>"));
}
text += QString(tr("%1: <a href='%2'>%2</a> <p>Hash: %3 %4</p> <br/>")).arg(mod.name, mod.websiteUrl, mod.hash, span);
}
ui->textBrowser->setText(text);
if (allModsMatched()) {
ui->labelModsFound->setText(tr("All mods found ✔"));
} else {
ui->labelModsFound->setText(tr("Please download the missing mods."));
}
}
/// @brief Signal fired when a watched direcotry has changed
/// @param path the path to the changed directory
void BlockedModsDialog::directoryChanged(QString path)
{
qDebug() << "Directory changed: " << path;
scanPath(path);
}
/// @brief add the user downloads folder and the global mods folder to the filesystem watcher
void BlockedModsDialog::setupWatch()
{
const QString downloadsFolder = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
const QString modsFolder = APPLICATION->settings()->get("CentralModsDir").toString();
watcher.addPath(downloadsFolder);
watcher.addPath(modsFolder);
}
/// @brief scan all watched folder
void BlockedModsDialog::scanPaths()
{
for (auto& dir : watcher.directories()) {
scanPath(dir);
}
}
/// @brief Scan the directory at path, skip paths that do not contain a file name
/// of a blocked mod we are looking for
/// @param path the directory to scan
void BlockedModsDialog::scanPath(QString path)
{
QDir scan_dir(path);
QDirIterator scan_it(path, QDir::Filter::Files | QDir::Filter::Hidden, QDirIterator::NoIteratorFlags);
while (scan_it.hasNext()) {
QString file = scan_it.next();
if (!checkValidPath(file)) {
continue;
}
auto hash_task = Hashing::createBlockedModHasher(file, ModPlatform::Provider::FLAME, "sha1");
qDebug() << "Creating Hash task for path: " << file;
connect(hash_task.get(), &Task::succeeded, [this, hash_task, file] { checkMatchHash(hash_task->getResult(), file); });
connect(hash_task.get(), &Task::failed, [file] { qDebug() << "Failed to hash path: " << file; });
hashing_task->addTask(hash_task);
}
hashing_task->start();
}
/// @brief check if the computed hash for the provided path matches a blocked
/// mod we are looking for
/// @param hash the computed hash for the provided path
/// @param path the path to the local file being compared
void BlockedModsDialog::checkMatchHash(QString hash, QString path)
{
bool match = false;
qDebug() << "Checking for match on hash: " << hash << "| From path:" << path;
for (auto& mod : mods) {
if (mod.matched) {
continue;
}
if (mod.hash.compare(hash, Qt::CaseInsensitive) == 0) {
mod.matched = true;
mod.localPath = path;
match = true;
qDebug() << "Hash match found:" << mod.name << hash << "| From path:" << path;
break;
}
}
if (match) {
update();
}
}
/// @brief Check if the name of the file at path matches the name of a blocked mod we are searching for
/// @param path the path to check
/// @return boolean: did the path match the name of a blocked mod?
bool BlockedModsDialog::checkValidPath(QString path)
{
QFileInfo file = QFileInfo(path);
QString filename = file.fileName();
for (auto& mod : mods) {
if (mod.name.compare(filename, Qt::CaseInsensitive) == 0) {
qDebug() << "Name match found:" << mod.name << "| From path:" << path;
return true;
}
}
return false;
}
bool BlockedModsDialog::allModsMatched()
{
return std::all_of(mods.begin(), mods.end(), [](auto const& mod) { return mod.matched; });
}
/// qDebug print support for the BlockedMod struct
QDebug operator<<(QDebug debug, const BlockedMod& m)
{
QDebugStateSaver saver(debug);
debug.nospace() << "{ name: " << m.name << ", websiteUrl: " << m.websiteUrl << ", hash: " << m.hash << ", matched: " << m.matched
<< ", localPath: " << m.localPath << "}";
return debug;
}

View File

@ -1,7 +1,23 @@
#pragma once
#include <QDialog>
#include <QString>
#include <QList>
#include <QFileSystemWatcher>
#include "modplatform/helpers/HashUtils.h"
#include "tasks/ConcurrentTask.h"
struct BlockedMod {
QString name;
QString websiteUrl;
QString hash;
bool matched;
QString localPath;
};
QT_BEGIN_NAMESPACE
namespace Ui { class BlockedModsDialog; }
@ -11,12 +27,27 @@ class BlockedModsDialog : public QDialog {
Q_OBJECT
public:
BlockedModsDialog(QWidget *parent, const QString &title, const QString &text, const QString &body, const QList<QUrl> &urls);
BlockedModsDialog(QWidget *parent, const QString &title, const QString &text, QList<BlockedMod> &mods);
~BlockedModsDialog() override;
private:
Ui::BlockedModsDialog *ui;
const QList<QUrl> &urls;
QList<BlockedMod> &mods;
QFileSystemWatcher watcher;
shared_qobject_ptr<ConcurrentTask> hashing_task;
void openAll();
void update();
void directoryChanged(QString path);
void setupWatch();
void scanPaths();
void scanPath(QString path);
void checkMatchHash(QString hash, QString path);
bool checkValidPath(QString path);
bool allModsMatched();
};
QDebug operator<<(QDebug debug, const BlockedMod &m);

View File

@ -13,8 +13,8 @@
<property name="windowTitle">
<string notr="true">BlockedModsDialog</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string notr="true"/>
@ -24,17 +24,7 @@
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="1" column="0">
<item>
<widget class="QTextBrowser" name="textBrowser">
<property name="acceptRichText">
<bool>true</bool>
@ -44,6 +34,27 @@
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="bottomBoxH">
<item>
<widget class="QLabel" name="labelModsFound">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>