Game-compatibility - improved (#2367)

* Game-compatibility - improved

* +

none of these texts have a translation \o/

* Fix

- html_url has been removed, the url is now built dynamically from the issue_number, and the file has decreased in size from 537kb to 355KB;
-Fix QProgressDialog
- change the correct directory, from my ford to the official one

* TR
This commit is contained in:
DanielSvoboda 2025-02-08 12:25:55 -03:00 committed by GitHub
parent cb14431ee5
commit d98face501
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
33 changed files with 1824 additions and 181 deletions

View file

@ -19,27 +19,34 @@ CompatibilityInfoClass::CompatibilityInfoClass()
CompatibilityInfoClass::~CompatibilityInfoClass() = default;
void CompatibilityInfoClass::UpdateCompatibilityDatabase(QWidget* parent, bool forced) {
if (!forced)
if (LoadCompatibilityFile())
return;
QNetworkReply* reply = FetchPage(1);
if (!WaitForReply(reply))
if (!forced && LoadCompatibilityFile())
return;
QProgressDialog dialog(tr("Fetching compatibility data, please wait"), tr("Cancel"), 0, 0,
QUrl url("https://github.com/shadps4-emu/shadps4-game-compatibility/releases/latest/download/"
"compatibility_data.json");
QNetworkRequest request(url);
QNetworkReply* reply = m_network_manager->get(request);
QProgressDialog dialog(tr("Fetching compatibility data, please wait"), tr("Cancel"), 0, 100,
parent);
dialog.setWindowTitle(tr("Loading..."));
dialog.setWindowModality(Qt::WindowModal);
dialog.setMinimumDuration(0);
dialog.setValue(0);
int remaining_pages = 0;
if (reply->hasRawHeader("link")) {
QRegularExpression last_page_re("(\\d+)(?=>; rel=\"last\")");
QRegularExpressionMatch last_page_match =
last_page_re.match(QString(reply->rawHeader("link")));
if (last_page_match.hasMatch()) {
remaining_pages = last_page_match.captured(0).toInt() - 1;
}
}
connect(reply, &QNetworkReply::downloadProgress,
[&dialog](qint64 bytesReceived, qint64 bytesTotal) {
if (bytesTotal > 0) {
dialog.setMaximum(bytesTotal);
dialog.setValue(bytesReceived);
}
});
connect(&dialog, &QProgressDialog::canceled, reply, &QNetworkReply::abort);
QEventLoop loop;
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
loop.exec();
if (reply->error() != QNetworkReply::NoError) {
reply->deleteLater();
@ -51,100 +58,23 @@ void CompatibilityInfoClass::UpdateCompatibilityDatabase(QWidget* parent, bool f
return;
}
ExtractCompatibilityInfo(reply->readAll());
QVector<QNetworkReply*> replies(remaining_pages);
QFutureWatcher<void> future_watcher;
for (int i = 0; i < remaining_pages; i++) {
replies[i] = FetchPage(i + 2);
QFile compatibility_file(m_compatibility_filename);
if (!compatibility_file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) {
QMessageBox::critical(parent, tr("Error"),
tr("Unable to open compatibility_data.json for writing."));
reply->deleteLater();
return;
}
future_watcher.setFuture(QtConcurrent::map(replies, WaitForReply));
connect(&future_watcher, &QFutureWatcher<void>::finished, [&]() {
for (int i = 0; i < remaining_pages; i++) {
if (replies[i]->bytesAvailable()) {
if (replies[i]->error() == QNetworkReply::NoError) {
ExtractCompatibilityInfo(replies[i]->readAll());
}
replies[i]->deleteLater();
} else {
// This means the request timed out
return;
}
}
// Writes the received data to the file.
QByteArray json_data = reply->readAll();
compatibility_file.write(json_data);
compatibility_file.close();
reply->deleteLater();
QFile compatibility_file(m_compatibility_filename);
if (!compatibility_file.open(QIODevice::WriteOnly | QIODevice::Truncate |
QIODevice::Text)) {
QMessageBox::critical(parent, tr("Error"),
tr("Unable to open compatibility.json for writing."));
return;
}
QJsonDocument json_doc;
m_compatibility_database["version"] = COMPAT_DB_VERSION;
json_doc.setObject(m_compatibility_database);
compatibility_file.write(json_doc.toJson());
compatibility_file.close();
dialog.reset();
});
connect(&future_watcher, &QFutureWatcher<void>::canceled, [&]() {
// Cleanup if user cancels pulling data
for (int i = 0; i < remaining_pages; i++) {
if (!replies[i]->bytesAvailable()) {
replies[i]->deleteLater();
} else if (!replies[i]->isFinished()) {
replies[i]->abort();
}
}
});
connect(&dialog, &QProgressDialog::canceled, &future_watcher, &QFutureWatcher<void>::cancel);
dialog.setRange(0, remaining_pages);
connect(&future_watcher, &QFutureWatcher<void>::progressValueChanged, &dialog,
&QProgressDialog::setValue);
dialog.exec();
LoadCompatibilityFile();
}
QNetworkReply* CompatibilityInfoClass::FetchPage(int page_num) {
QUrl url = QUrl("https://api.github.com/repos/shadps4-emu/shadps4-game-compatibility/issues");
QUrlQuery query;
query.addQueryItem("per_page", QString("100"));
query.addQueryItem(
"tags", QString("status-ingame status-playable status-nothing status-boots status-menus"));
query.addQueryItem("page", QString::number(page_num));
url.setQuery(query);
QNetworkRequest request(url);
QNetworkReply* reply = m_network_manager->get(request);
return reply;
}
bool CompatibilityInfoClass::WaitForReply(QNetworkReply* reply) {
// Returns true if reply succeeded, false if reply timed out
QTimer timer;
timer.setSingleShot(true);
QEventLoop loop;
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
timer.start(5000);
loop.exec();
if (timer.isActive()) {
timer.stop();
return true;
} else {
disconnect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
reply->abort();
return false;
}
};
CompatibilityEntry CompatibilityInfoClass::GetCompatibilityInfo(const std::string& serial) {
QString title_id = QString::fromStdString(serial);
if (m_compatibility_database.contains(title_id)) {
@ -160,7 +90,7 @@ CompatibilityEntry CompatibilityInfoClass::GetCompatibilityInfo(const std::strin
QDateTime::fromString(compatibility_entry_obj["last_tested"].toString(),
Qt::ISODate),
compatibility_entry_obj["url"].toString(),
compatibility_entry_obj["issue_number"].toInt()};
compatibility_entry_obj["issue_number"].toString()};
return compatibility_entry;
}
}
@ -193,14 +123,6 @@ bool CompatibilityInfoClass::LoadCompatibilityFile() {
return false;
}
// Check database version
int version_number;
if (json_doc.object()["version"].isDouble()) {
if (json_doc.object()["version"].toInt() < COMPAT_DB_VERSION)
return false;
} else
return false;
m_compatibility_database = json_doc.object();
return true;
}