From ef286a938d79f74947a392d049068e4818ccf7fd Mon Sep 17 00:00:00 2001 From: Silarn Date: Tue, 29 Jan 2019 15:12:59 -0600 Subject: Multiple fixes: * Uses current daily/hourly rate limits * Fixed issue with translating API game string to mod game string ** Corrects issues with update checks and downloads --- src/downloadmanager.cpp | 29 +++++- src/downloadmanager.h | 3 + src/mainwindow.cpp | 20 ++-- src/mainwindow.h | 3 +- src/nexusinterface.cpp | 56 +++++------ src/nexusinterface.h | 11 ++- src/nxmaccessmanager.cpp | 10 +- src/nxmaccessmanager.h | 240 +++++++++++++++++++++++------------------------ 8 files changed, 201 insertions(+), 171 deletions(-) diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index 2ba46d74..f5e4d688 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -19,11 +19,11 @@ along with Mod Organizer. If not, see . #include "downloadmanager.h" +#include "organizercore.h" #include "nxmurl.h" #include "nexusinterface.h" #include "nxmaccessmanager.h" #include "iplugingame.h" -#include "downloadmanager.h" #include #include #include "utility.h" @@ -203,6 +203,7 @@ QString DownloadManager::DownloadInfo::currentURL() DownloadManager::DownloadManager(NexusInterface *nexusInterface, QObject *parent) : IDownloadManager(parent), m_NexusInterface(nexusInterface), m_DirWatcher(), m_ShowHidden(false) { + m_OrganizerCore = dynamic_cast(parent); connect(&m_DirWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(directoryChanged(QString))); m_TimeoutTimer.setSingleShot(false); //connect(&m_TimeoutTimer, SIGNAL(timeout()), this, SLOT(checkDownloadTimeout())); @@ -1532,7 +1533,16 @@ void DownloadManager::nxmFileInfoAvailable(QString gameName, int modID, int file info->description = BBCode::convertToHTML(result["changelog_html"].toString()); info->repository = "Nexus"; - info->gameName = gameName; + + QStringList games(m_ManagedGame->validShortNames()); + games += m_ManagedGame->gameShortName(); + for (auto game : games) { + MOBase::IPluginGame *gamePlugin = m_OrganizerCore->getGame(game); + if (gamePlugin->gameNexusName().compare(gameName, Qt::CaseInsensitive) == 0) { + info->gameName = gamePlugin->gameShortName(); + } + } + info->modID = modID; info->fileID = fileID; @@ -1597,10 +1607,21 @@ void DownloadManager::nxmDownloadURLsAvailable(QString gameName, int modID, int m_RequestIDs.erase(idIter); } + QString gameShortName; + QStringList games(m_ManagedGame->validShortNames()); + games += m_ManagedGame->gameShortName(); + for (auto game : games) { + MOBase::IPluginGame *gamePlugin = m_OrganizerCore->getGame(game); + if (gamePlugin->gameNexusName() == gameName) { + gameShortName = gamePlugin->gameShortName(); + break; + } + } + ModRepositoryFileInfo *info = qobject_cast(qvariant_cast(userData)); QVariantList resultList = resultData.toList(); if (resultList.length() == 0) { - removePending(gameName, modID, fileID); + removePending(gameShortName, modID, fileID); emit showMessage(tr("No download server available. Please try again later.")); return; } @@ -1614,7 +1635,7 @@ void DownloadManager::nxmDownloadURLsAvailable(QString gameName, int modID, int foreach (const QVariant &server, resultList) { URLs.append(server.toMap()["URI"].toString()); } - addDownload(URLs, gameName, modID, fileID, info); + addDownload(URLs, gameShortName, modID, fileID, info); } diff --git a/src/downloadmanager.h b/src/downloadmanager.h index 3c36143e..841a9fbc 100644 --- a/src/downloadmanager.h +++ b/src/downloadmanager.h @@ -40,6 +40,7 @@ namespace MOBase { class IPluginGame; } class NexusInterface; class PluginContainer; +class OrganizerCore; /*! * \brief manages downloading of files and provides progress information for gui elements @@ -532,6 +533,8 @@ private: NexusInterface *m_NexusInterface; + OrganizerCore *m_OrganizerCore; + QVector> m_PendingDownloads; QVector m_ActiveDownloads; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 02720d44..094a44af 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -402,9 +402,9 @@ MainWindow::MainWindow(QSettings &initSettings connect(NexusInterface::instance(&pluginContainer)->getAccessManager(), SIGNAL(validateFailed(QString)), this, SLOT(validationFailed(QString))); connect(NexusInterface::instance(&pluginContainer)->getAccessManager(), SIGNAL(credentialsReceived(const QString&, bool)), this, SLOT(updateWindowTitle(const QString&, bool))); - connect(NexusInterface::instance(&pluginContainer)->getAccessManager(), SIGNAL(credentialsReceived(const QString&, bool)), - NexusInterface::instance(&m_PluginContainer), SLOT(setRateMax(const QString&, bool))); - connect(NexusInterface::instance(&pluginContainer), SIGNAL(requestsChanged(int, int)), this, SLOT(updateAPICounter(int, int))); + connect(NexusInterface::instance(&pluginContainer)->getAccessManager(), SIGNAL(credentialsReceived(const QString&, bool, std::tuple)), + NexusInterface::instance(&m_PluginContainer), SLOT(setRateMax(const QString&, bool, std::tuple))); + connect(NexusInterface::instance(&pluginContainer), SIGNAL(requestsChanged(int, std::tuple)), this, SLOT(updateAPICounter(int, std::tuple))); connect(&TutorialManager::instance(), SIGNAL(windowTutorialFinished(QString)), this, SLOT(windowTutorialFinished(QString))); connect(ui->tabWidget, SIGNAL(currentChanged(int)), &TutorialManager::instance(), SIGNAL(tabChanged(int))); @@ -439,11 +439,6 @@ MainWindow::MainWindow(QSettings &initSettings connect(&m_SaveMetaTimer, SIGNAL(timeout()), this, SLOT(saveModMetas())); m_SaveMetaTimer.start(5000); - m_NexusLimitTimer.setTimerType(Qt::TimerType::PreciseTimer); - m_NexusLimitTimer.setSingleShot(false); - connect(&m_NexusLimitTimer, SIGNAL(timeout()), NexusInterface::instance(&m_PluginContainer), SLOT(calculateRequests())); - m_NexusLimitTimer.start(1000); - setCategoryListVisible(initSettings.value("categorylist_visible", true).toBool()); FileDialogMemory::restore(initSettings); @@ -5640,14 +5635,15 @@ void MainWindow::nxmRequestFailed(QString gameName, int modID, int, QVariant, in } -void MainWindow::updateAPICounter(int queueCount, int requestsRemaining) +void MainWindow::updateAPICounter(int queueCount, std::tuple limits) { - ui->apiRequests->setText(QString("API: Q: %1 | T: %2").arg(queueCount).arg(requestsRemaining)); - if (requestsRemaining > 150) { + ui->apiRequests->setText(QString("API: Q: %1 | D: %2 | H: %3").arg(queueCount).arg(std::get<0>(limits)).arg(std::get<2>(limits))); + int requestsRemaining = std::get<0>(limits) + std::get<2>(limits); + if (requestsRemaining > 300) { QPalette palette = ui->apiRequests->palette(); palette.setColor(ui->apiRequests->backgroundRole(), Qt::darkGreen); ui->apiRequests->setPalette(palette); - } else if (requestsRemaining < 50) { + } else if (requestsRemaining < 150) { QPalette palette = ui->apiRequests->palette(); palette.setColor(ui->apiRequests->backgroundRole(), Qt::darkRed); ui->apiRequests->setPalette(palette); diff --git a/src/mainwindow.h b/src/mainwindow.h index 900f7cba..b3490d07 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -348,7 +348,6 @@ private: QTimer m_CheckBSATimer; QTimer m_SaveMetaTimer; QTimer m_UpdateProblemsTimer; - QTimer m_NexusLimitTimer; QFuture m_MetaSave; @@ -515,7 +514,7 @@ private slots: void nxmDownloadURLs(QString, int modID, int fileID, QVariant userData, QVariant resultData, int requestID); void nxmRequestFailed(QString gameName, int modID, int fileID, QVariant userData, int requestID, QNetworkReply::NetworkError error, const QString &errorString); - void updateAPICounter(int queueCount, int requestsRemaining); + void updateAPICounter(int queueCount, std::tuple limits); void editCategories(); void deselectFilters(); diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp index 1e50f0b2..7481cf96 100644 --- a/src/nexusinterface.cpp +++ b/src/nexusinterface.cpp @@ -148,7 +148,7 @@ QAtomicInt NexusInterface::NXMRequestInfo::s_NextID(0); NexusInterface::NexusInterface(PluginContainer *pluginContainer) - : m_NMMVersion(), m_PluginContainer(pluginContainer), m_RemainingRequests(300), m_MaxRequests(300) + : m_NMMVersion(), m_PluginContainer(pluginContainer), m_RemainingDailyRequests(0), m_RemainingHourlyRequests(0), m_MaxDailyRequests(0), m_MaxHourlyRequests(0) { m_MOVersion = createVersionInfo(); @@ -194,16 +194,13 @@ void NexusInterface::loginCompleted() nextRequest(); } -void NexusInterface::setRateMax(const QString &userName, bool isPremium) +void NexusInterface::setRateMax(const QString&, bool, std::tuple limits) { - if (isPremium) { - m_MaxRequests = 600; - m_RemainingRequests = 600; - } else { - m_MaxRequests = 300; - m_RemainingRequests = 300; - } - emit requestsChanged(m_RequestQueue.size(), m_RemainingRequests); + m_RemainingDailyRequests = std::get<0>(limits); + m_MaxDailyRequests = std::get<1>(limits); + m_RemainingHourlyRequests = std::get<2>(limits); + m_MaxHourlyRequests = std::get<3>(limits); + emit requestsChanged(m_RequestQueue.size(), limits); } void NexusInterface::interpretNexusFileName(const QString &fileName, QString &modName, int &modID, bool query) @@ -474,10 +471,16 @@ void NexusInterface::nextRequest() return; } - if (m_RemainingRequests <= 0) { - qWarning() << tr("You've exceeded the Nexus API rate limit and requests are now being throttled."); - if (!m_RetryTimer.isActive()) - m_RetryTimer.start(); + if (m_RemainingDailyRequests + m_RemainingHourlyRequests <= 0) { + if (!m_RetryTimer.isActive()) { + QTime time = QTime::currentTime(); + QTime targetTime; + targetTime.setHMS((time.hour() + 1) % 23, 0, 5); + m_RetryTimer.start(time.msecsTo(targetTime)); + QString warning("You've exceeded the Nexus API rate limit and requests are now being throttled. " + "Your next batch of requests will be available in approximately %1 minutes and %2 seconds."); + qWarning() << warning.arg(time.secsTo(targetTime) / 60).arg(time.secsTo(targetTime) % 60); + } return; } @@ -490,8 +493,6 @@ void NexusInterface::nextRequest() } } - m_RemainingRequests--; - NXMRequestInfo info = m_RequestQueue.dequeue(); info.m_Timeout = new QTimer(this); info.m_Timeout->setInterval(60000); @@ -545,7 +546,6 @@ void NexusInterface::nextRequest() connect(info.m_Timeout, SIGNAL(timeout()), this, SLOT(requestTimeout())); info.m_Timeout->start(); m_ActiveRequest.push_back(info); - emit requestsChanged(m_RequestQueue.size(), m_RemainingRequests); } @@ -561,8 +561,6 @@ void NexusInterface::requestFinished(std::list::iterator iter) if (reply->error() != QNetworkReply::NoError) { int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if (statusCode == 429) { - m_RemainingRequests = 0; - emit requestsChanged(m_RequestQueue.size(), m_RemainingRequests); qWarning("Requests have hit the rate limit threshold and are now being throttled. This request will be retried."); qWarning("Error: %s", reply->errorString().toUtf8().constData()); m_RequestQueue.enqueue(*iter); @@ -617,6 +615,18 @@ void NexusInterface::requestFinished(std::list::iterator iter) } } } + + m_RemainingDailyRequests = reply->rawHeader("x-rl-daily-remaining").toInt(); + m_MaxDailyRequests = reply->rawHeader("x-rl-daily-limit").toInt(); + m_RemainingHourlyRequests = reply->rawHeader("x-rl-hourly-remaining").toInt(); + m_MaxHourlyRequests = reply->rawHeader("x-rl-hourly-limit").toInt(); + + emit requestsChanged(m_RequestQueue.size(), std::tuple(std::make_tuple( + m_RemainingDailyRequests, + m_MaxDailyRequests, + m_RemainingHourlyRequests, + m_MaxHourlyRequests + ))); } @@ -668,14 +678,6 @@ void NexusInterface::requestTimeout() } } -void NexusInterface::calculateRequests() -{ - if (m_RemainingRequests < m_MaxRequests) { - m_RemainingRequests++; - emit requestsChanged(m_RequestQueue.size(), m_RemainingRequests); - } -} - namespace { QString get_management_url(MOBase::IPluginGame const *game) { diff --git a/src/nexusinterface.h b/src/nexusinterface.h index a68948e6..e60ebf3f 100644 --- a/src/nexusinterface.h +++ b/src/nexusinterface.h @@ -348,12 +348,11 @@ signals: void nxmDownloadURLsAvailable(QString gameName, int modID, int fileID, QVariant userData, QVariant resultData, int requestID); void nxmEndorsementToggled(QString gameName, int modID, QVariant userData, QVariant resultData, int requestID); void nxmRequestFailed(QString gameName, int modID, int fileID, QVariant userData, int requestID, QNetworkReply::NetworkError error, const QString &errorString); - void requestsChanged(int queueCount, int requestsRemaining); + void requestsChanged(int queueCount, std::tuple requestsRemaining); public slots: - void setRateMax(const QString &userName, bool isPremium); - void calculateRequests(); + void setRateMax(const QString&, bool, std::tuple limits); private slots: @@ -426,8 +425,10 @@ private: QTimer m_RetryTimer; - int m_RemainingRequests; - int m_MaxRequests; + int m_RemainingDailyRequests; + int m_RemainingHourlyRequests; + int m_MaxDailyRequests; + int m_MaxHourlyRequests; }; diff --git a/src/nxmaccessmanager.cpp b/src/nxmaccessmanager.cpp index 6847ba25..d9bb2905 100644 --- a/src/nxmaccessmanager.cpp +++ b/src/nxmaccessmanager.cpp @@ -252,7 +252,15 @@ void NXMAccessManager::validateFinished() QString test = jdoc.toJson(); QString name = credentialsData.value("name").toString(); bool premium = credentialsData.value("is_premium?").toBool(); - emit credentialsReceived(name, premium); + + std::tuple limits(std::make_tuple( + m_ValidateReply->rawHeader("x-rl-daily-remaining").toInt(), + m_ValidateReply->rawHeader("x-rl-daily-limit").toInt(), + m_ValidateReply->rawHeader("x-rl-hourly-remaining").toInt(), + m_ValidateReply->rawHeader("x-rl-hourly-limit").toInt() + )); + + emit credentialsReceived(name, premium, limits); m_ValidateReply->deleteLater(); m_ValidateReply = nullptr; diff --git a/src/nxmaccessmanager.h b/src/nxmaccessmanager.h index b316ef77..7fdb508f 100644 --- a/src/nxmaccessmanager.h +++ b/src/nxmaccessmanager.h @@ -1,120 +1,120 @@ -/* -Copyright (C) 2012 Sebastian Herbord. All rights reserved. - -This file is part of Mod Organizer. - -Mod Organizer is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -Mod Organizer is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Mod Organizer. If not, see . -*/ - -#ifndef NXMACCESSMANAGER_H -#define NXMACCESSMANAGER_H - - -#include -#include -#include -#include -#include - -namespace MOBase { class IPluginGame; } - -/** - * @brief access manager extended to handle nxm links - **/ -class NXMAccessManager : public QNetworkAccessManager -{ - Q_OBJECT -public: - - explicit NXMAccessManager(QObject *parent, const QString &moVersion); - - ~NXMAccessManager(); - - void setNMMVersion(const QString &nmmVersion); - - bool validated() const; - - bool validateAttempted() const; - bool validateWaiting() const; - - void apiCheck(const QString &apiKey); - - void showCookies() const; - - void clearCookies(); - - QString userAgent(const QString &subModule = QString()) const; - - QString apiKey() const; - - void startValidationCheck(); - - void refuseValidation(); - -signals: - - /** - * @brief emitted when a nxm:// link is opened - * - * @param url the nxm-link - **/ - void requestNXMDownload(const QString &url); - - /** - * @brief emitted after a successful login or if login was not necessary - * - * @param necessary true if a login was necessary and succeeded, false if the user is still logged in - **/ - void validateSuccessful(bool necessary); - - void validateFailed(const QString &message); - - void credentialsReceived(const QString &userName, bool premium); - -private slots: - - void validateFinished(); - void validateError(QNetworkReply::NetworkError errorCode); - void validateTimeout(); - -protected: - - virtual QNetworkReply *createRequest( - QNetworkAccessManager::Operation operation, const QNetworkRequest &request, - QIODevice *device); - -private: - - QTimer m_ValidateTimeout; - QNetworkReply *m_ValidateReply; - QProgressDialog *m_ProgressDialog { nullptr }; - - QString m_MOVersion; - QString m_NMMVersion; - - QString m_ApiKey; - - bool m_ValidateAttempted; - enum { - VALIDATE_NOT_CHECKED, - VALIDATE_CHECKING, - VALIDATE_NOT_VALID, - VALIDATE_ATTEMPT_FAILED, - VALIDATE_REFUSED, - VALIDATE_VALID - } m_ValidateState = VALIDATE_NOT_CHECKED; - -}; - -#endif // NXMACCESSMANAGER_H +/* +Copyright (C) 2012 Sebastian Herbord. All rights reserved. + +This file is part of Mod Organizer. + +Mod Organizer is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Mod Organizer is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Mod Organizer. If not, see . +*/ + +#ifndef NXMACCESSMANAGER_H +#define NXMACCESSMANAGER_H + + +#include +#include +#include +#include +#include + +namespace MOBase { class IPluginGame; } + +/** + * @brief access manager extended to handle nxm links + **/ +class NXMAccessManager : public QNetworkAccessManager +{ + Q_OBJECT +public: + + explicit NXMAccessManager(QObject *parent, const QString &moVersion); + + ~NXMAccessManager(); + + void setNMMVersion(const QString &nmmVersion); + + bool validated() const; + + bool validateAttempted() const; + bool validateWaiting() const; + + void apiCheck(const QString &apiKey); + + void showCookies() const; + + void clearCookies(); + + QString userAgent(const QString &subModule = QString()) const; + + QString apiKey() const; + + void startValidationCheck(); + + void refuseValidation(); + +signals: + + /** + * @brief emitted when a nxm:// link is opened + * + * @param url the nxm-link + **/ + void requestNXMDownload(const QString &url); + + /** + * @brief emitted after a successful login or if login was not necessary + * + * @param necessary true if a login was necessary and succeeded, false if the user is still logged in + **/ + void validateSuccessful(bool necessary); + + void validateFailed(const QString &message); + + void credentialsReceived(const QString &userName, bool premium, std::tuple limits); + +private slots: + + void validateFinished(); + void validateError(QNetworkReply::NetworkError errorCode); + void validateTimeout(); + +protected: + + virtual QNetworkReply *createRequest( + QNetworkAccessManager::Operation operation, const QNetworkRequest &request, + QIODevice *device); + +private: + + QTimer m_ValidateTimeout; + QNetworkReply *m_ValidateReply; + QProgressDialog *m_ProgressDialog { nullptr }; + + QString m_MOVersion; + QString m_NMMVersion; + + QString m_ApiKey; + + bool m_ValidateAttempted; + enum { + VALIDATE_NOT_CHECKED, + VALIDATE_CHECKING, + VALIDATE_NOT_VALID, + VALIDATE_ATTEMPT_FAILED, + VALIDATE_REFUSED, + VALIDATE_VALID + } m_ValidateState = VALIDATE_NOT_CHECKED; + +}; + +#endif // NXMACCESSMANAGER_H -- cgit v1.3.1