diff options
| -rw-r--r-- | src/mainwindow.cpp | 33 | ||||
| -rw-r--r-- | src/mainwindow.h | 1 | ||||
| -rw-r--r-- | src/modinfo.cpp | 73 | ||||
| -rw-r--r-- | src/modinfo.h | 2 | ||||
| -rw-r--r-- | src/modinforegular.cpp | 4 | ||||
| -rw-r--r-- | src/nexusinterface.cpp | 64 | ||||
| -rw-r--r-- | src/nexusinterface.h | 24 |
7 files changed, 188 insertions, 13 deletions
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index a17b51d5..5e97a959 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -5468,10 +5468,39 @@ void MainWindow::modUpdateCheck(std::multimap<QString, int> IDs) if (m_OrganizerCore.settings().getNexusApiKey(apiKey)) { m_OrganizerCore.doAfterLogin([=]() { this->modUpdateCheck(IDs); }); NexusInterface::instance(&m_PluginContainer)->getAccessManager()->apiCheck(apiKey); - } else { + } else qWarning("You are not currently authenticated with Nexus. Please do so under Settings -> Nexus."); + } +} + +void MainWindow::nxmUpdateInfoAvailable(QString gameName, QVariant userData, QVariant resultData, int) +{ + QString gameNameReal; + for (IPluginGame *game : m_PluginContainer.plugins<IPluginGame>()) { + if (game->gameNexusName() == gameName) { + gameNameReal = game->gameShortName(); + break; + } + } + QVariantList resultList = resultData.toList(); + std::set<QSharedPointer<ModInfo>> finalMods = ModInfo::filteredMods(gameNameReal, resultList, userData.toBool(), true); + + if (finalMods.empty()) { + qInfo("None of your mods appear to have had recent file updates."); + } + + std::set<std::pair<QString, int>> organizedGames; + for (auto mod : finalMods) { + if (mod->canBeUpdated()) { + organizedGames.insert(std::make_pair<QString, int>(mod->getGameName().toLower(), mod->getNexusID())); } } + + if (!finalMods.empty() && organizedGames.empty()) + qWarning("All of your mods have been checked recently. We restrict update checks to help preserve your available API requests."); + + for (auto game : organizedGames) + NexusInterface::instance(&m_PluginContainer)->requestUpdates(game.second, this, QVariant(), game.first, QString()); } void MainWindow::nxmUpdatesAvailable(QString gameName, int modID, QVariant userData, QVariant resultData, int requestID) @@ -5594,7 +5623,7 @@ void MainWindow::nxmModInfoAvailable(QString gameName, int modID, QVariant userD mod->setIsEndorsed(false); } mod->setLastNexusQuery(QDateTime::currentDateTimeUtc()); - mod->setNexusLastModified(QDateTime::fromSecsSinceEpoch(result["updated_timestamp"].toInt())); + mod->setNexusLastModified(QDateTime::fromSecsSinceEpoch(result["updated_timestamp"].toInt(), Qt::UTC)); mod->saveMeta(); } } diff --git a/src/mainwindow.h b/src/mainwindow.h index 59be9800..d2704b72 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -510,6 +510,7 @@ private slots: void modUpdateCheck(std::multimap<QString, int> IDs); + void nxmUpdateInfoAvailable(QString gameName, QVariant userData, QVariant resultData, int requestID); void nxmUpdatesAvailable(QString gameName, int modID, QVariant userData, QVariant resultData, int requestID); void nxmModInfoAvailable(QString gameName, int modID, QVariant userData, QVariant resultData, int requestID); void nxmEndorsementToggled(QString, int, QVariant, QVariant resultData, int); diff --git a/src/modinfo.cpp b/src/modinfo.cpp index a1059e24..3e694baf 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -294,25 +294,82 @@ int ModInfo::checkAllForUpdate(PluginContainer *pluginContainer, QObject *receiv // NexusInterface::instance(pluginContainer)->requestUpdates(game->nexusModOrganizerID(), receiver, QVariant(), game->gameShortName(), QString()); //} - std::set<std::pair<QString, int>> organizedGames; + QDateTime earliest = QDateTime::currentDateTimeUtc(); + QDateTime latest; + std::set<QString> games; for (auto mod : s_Collection) { if (mod->canBeUpdated()) { - organizedGames.insert(std::make_pair<QString, int>(mod->getGameName().toLower(), mod->getNexusID())); + if (mod->getLastNexusUpdate() < earliest) + earliest = mod->getLastNexusUpdate(); + if (mod->getLastNexusUpdate() > latest) + latest = mod->getLastNexusUpdate(); + games.insert(mod->getGameName().toLower()); } } - result = organizedGames.size(); - - if (organizedGames.empty()) - qWarning("All of your mods have been checked recently. We restrict update checks to help preserve your available API requests."); + if (latest < QDateTime::currentDateTimeUtc().addDays(-30)) { + std::set<std::pair<QString, int>> organizedGames; + for (auto mod : s_Collection) { + if (mod->canBeUpdated()) { + organizedGames.insert(std::make_pair<QString, int>(mod->getGameName().toLower(), mod->getNexusID())); + } + } + + result = organizedGames.size(); - for (auto game : organizedGames) { - NexusInterface::instance(pluginContainer)->requestUpdates(game.second, receiver, QVariant(), game.first, QString()); + if (organizedGames.empty()) + qWarning("All of your mods have been checked recently. We restrict update checks to help preserve your available API requests."); + + for (auto game : organizedGames) { + NexusInterface::instance(pluginContainer)->requestUpdates(game.second, receiver, QVariant(), game.first, QString()); + } + } else if (earliest < QDateTime::currentDateTimeUtc().addDays(-30)) { + for (auto gameName : games) + NexusInterface::instance(pluginContainer)->requestUpdateInfo(gameName, NexusInterface::UpdatePeriod::MONTH, receiver, QVariant(true), QString()); + } else if (earliest < QDateTime::currentDateTimeUtc().addDays(-7)) { + for (auto gameName : games) + NexusInterface::instance(pluginContainer)->requestUpdateInfo(gameName, NexusInterface::UpdatePeriod::MONTH, receiver, QVariant(false), QString()); + } else if (earliest < QDateTime::currentDateTimeUtc().addDays(-1)) { + for (auto gameName : games) + NexusInterface::instance(pluginContainer)->requestUpdateInfo(gameName, NexusInterface::UpdatePeriod::WEEK, receiver, QVariant(false), QString()); + } else { + for (auto gameName : games) + NexusInterface::instance(pluginContainer)->requestUpdateInfo(gameName, NexusInterface::UpdatePeriod::DAY, receiver, QVariant(false), QString()); } return result; } +std::set<QSharedPointer<ModInfo>> ModInfo::filteredMods(QString gameName, QVariantList updateData, bool addOldMods, bool markUpdated) +{ + std::set<QSharedPointer<ModInfo>> finalMods; + for (QVariant result : updateData) { + QVariantMap update = result.toMap(); + for (auto mod : s_Collection) + if (mod->getNexusID() == update["mod_id"].toInt() && mod->getGameName().compare(gameName, Qt::CaseInsensitive) == 0) + if (mod->getLastNexusUpdate() > QDateTime::fromSecsSinceEpoch(update["latest_file_update"].toInt(), Qt::UTC)) + finalMods.insert(mod); + } + + if (addOldMods) + for (auto mod : s_Collection) + if (mod->getLastNexusUpdate() < QDateTime::currentDateTimeUtc().addDays(-30) && mod->getGameName().compare(gameName, Qt::CaseInsensitive) == 0) + finalMods.insert(mod); + + if (markUpdated) { + std::set<QSharedPointer<ModInfo>> updates; + for (auto mod : s_Collection) + if (mod->getGameName().compare(gameName, Qt::CaseInsensitive) == 0 && mod->canBeUpdated()) + updates.insert(mod); + std::set<QSharedPointer<ModInfo>> diff; + std::set_difference(updates.begin(), updates.end(), finalMods.begin(), finalMods.end(), std::inserter(diff, diff.end())); + for (auto skipped : diff) { + skipped->setLastNexusUpdate(QDateTime::currentDateTimeUtc()); + skipped->updateNXMInfo(); + } + } + return finalMods; +} int ModInfo::manualUpdateCheck(PluginContainer *pluginContainer, QObject *receiver, std::multimap<QString, int> IDs) { diff --git a/src/modinfo.h b/src/modinfo.h index 348583d2..8c234225 100644 --- a/src/modinfo.h +++ b/src/modinfo.h @@ -195,6 +195,8 @@ public: **/ static int checkAllForUpdate(PluginContainer *pluginContainer, QObject *receiver); + static std::set<QSharedPointer<ModInfo>> filteredMods(QString gameName, QVariantList updateData, bool addOldMods = false, bool markUpdated = false); + /** * @brief create a new mod from the specified directory and add it to the collection * @param dir directory to create from diff --git a/src/modinforegular.cpp b/src/modinforegular.cpp index 10a1317c..9532bf6d 100644 --- a/src/modinforegular.cpp +++ b/src/modinforegular.cpp @@ -235,10 +235,10 @@ void ModInfoRegular::nxmDescriptionAvailable(QString, int, QVariant, QVariant re setEndorsedState(ENDORSED_FALSE); } m_LastNexusQuery = QDateTime::currentDateTimeUtc(); - m_NexusLastModified = QDateTime::fromSecsSinceEpoch(result["updated_timestamp"].toInt()); + m_NexusLastModified = QDateTime::fromSecsSinceEpoch(result["updated_timestamp"].toInt(), Qt::UTC); m_MetaInfoChanged = true; saveMeta(); - disconnect(sender(), SIGNAL(nxmDescriptionAvailable(QString, int, QVariant, QVariant))); + disconnect(sender(), SIGNAL(descriptionAvailable(QString, int, QVariant, QVariant))); emit modDetailsUpdated(true); } diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp index f1fa252a..abddbd3e 100644 --- a/src/nexusinterface.cpp +++ b/src/nexusinterface.cpp @@ -343,6 +343,26 @@ int NexusInterface::requestModInfo(QString gameName, int modID, QObject *receive return -1; } +int NexusInterface::requestUpdateInfo(QString gameName, NexusInterface::UpdatePeriod period, QObject *receiver, QVariant userData, + const QString &subModule, const MOBase::IPluginGame const *game) +{ + if (std::max(m_RemainingDailyRequests, m_RemainingHourlyRequests) >= 200) { + NXMRequestInfo requestInfo(period, NXMRequestInfo::TYPE_CHECKUPDATES, userData, subModule, game); + m_RequestQueue.enqueue(requestInfo); + + connect(this, SIGNAL(nxmUpdateInfoAvailable(QString, QVariant, QVariant, int)), + receiver, SLOT(nxmUpdateInfoAvailable(QString, QVariant, QVariant, int)), Qt::UniqueConnection); + + connect(this, SIGNAL(nxmRequestFailed(QString, int, int, QVariant, int, QNetworkReply::NetworkError, QString)), + receiver, SLOT(nxmRequestFailed(QString, int, int, QVariant, int, QNetworkReply::NetworkError, QString)), Qt::UniqueConnection); + + nextRequest(); + return requestInfo.m_ID; + } + qCritical() << QString("You have fewer than 200 requests remaining (%1). Only downloads and login validation are being allowed.") + .arg(std::max(m_RemainingDailyRequests, m_RemainingHourlyRequests)); + return -1; +} int NexusInterface::requestUpdates(const int &modID, QObject *receiver, QVariant userData, QString gameName, const QString &subModule) @@ -531,6 +551,21 @@ void NexusInterface::nextRequest() case NXMRequestInfo::TYPE_MODINFO: { url = QString("%1/games/%2/mods/%3").arg(info.m_URL).arg(info.m_GameName).arg(info.m_ModID); } break; + case NXMRequestInfo::TYPE_CHECKUPDATES: { + QString period; + switch (info.m_UpdatePeriod) { + case UpdatePeriod::DAY: + period = "1d"; + break; + case UpdatePeriod::WEEK: + period = "1w"; + break; + case UpdatePeriod::MONTH: + period = "1m"; + break; + } + url = QString("%1/games/%2/mods/updated?period=%3").arg(info.m_URL).arg(info.m_GameName).arg(period); + } break; case NXMRequestInfo::TYPE_FILES: case NXMRequestInfo::TYPE_GETUPDATES: { url = QString("%1/games/%2/mods/%3/files").arg(info.m_URL).arg(info.m_GameName).arg(info.m_ModID); @@ -624,6 +659,9 @@ void NexusInterface::requestFinished(std::list<NXMRequestInfo>::iterator iter) case NXMRequestInfo::TYPE_MODINFO: { emit nxmModInfoAvailable(iter->m_GameName, iter->m_ModID, iter->m_UserData, result, iter->m_ID); } break; + case NXMRequestInfo::TYPE_CHECKUPDATES: { + emit nxmUpdateInfoAvailable(iter->m_GameName, iter->m_UserData, result, iter->m_ID); + } break; case NXMRequestInfo::TYPE_FILES: { emit nxmFilesAvailable(iter->m_GameName, iter->m_ModID, iter->m_UserData, result, iter->m_ID); } break; @@ -726,6 +764,7 @@ NexusInterface::NXMRequestInfo::NXMRequestInfo(int modID , m_FileID(0) , m_Reply(nullptr) , m_Type(type) + , m_UpdatePeriod(UpdatePeriod::NONE) , m_UserData(userData) , m_Timeout(nullptr) , m_Reroute(false) @@ -750,6 +789,7 @@ NexusInterface::NXMRequestInfo::NXMRequestInfo(int modID , m_FileID(0) , m_Reply(nullptr) , m_Type(type) + , m_UpdatePeriod(UpdatePeriod::NONE) , m_UserData(userData) , m_Timeout(nullptr) , m_Reroute(false) @@ -773,6 +813,30 @@ NexusInterface::NXMRequestInfo::NXMRequestInfo(int modID , m_FileID(fileID) , m_Reply(nullptr) , m_Type(type) + , m_UpdatePeriod(UpdatePeriod::NONE) + , m_UserData(userData) + , m_Timeout(nullptr) + , m_Reroute(false) + , m_ID(s_NextID.fetchAndAddAcquire(1)) + , m_URL(get_management_url(game)) + , m_SubModule(subModule) + , m_NexusGameID(game->nexusGameID()) + , m_GameName(game->gameNexusName()) + , m_Endorse(false) +{} + +NexusInterface::NXMRequestInfo::NXMRequestInfo(UpdatePeriod period + , NexusInterface::NXMRequestInfo::Type type + , QVariant userData + , const QString &subModule + , MOBase::IPluginGame const *game +) + : m_ModID(0) + , m_ModVersion("0") + , m_FileID(0) + , m_Reply(nullptr) + , m_Type(type) + , m_UpdatePeriod(period) , m_UserData(userData) , m_Timeout(nullptr) , m_Reroute(false) diff --git a/src/nexusinterface.h b/src/nexusinterface.h index 1c283335..06b19947 100644 --- a/src/nexusinterface.h +++ b/src/nexusinterface.h @@ -129,6 +129,15 @@ class NexusInterface : public QObject public: + enum UpdatePeriod { + NONE, + DAY, + WEEK, + MONTH + }; + +public: + ~NexusInterface(); static NexusInterface *instance(PluginContainer *pluginContainer); @@ -202,6 +211,15 @@ public: int requestModInfo(QString gameName, int modID, QObject *receiver, QVariant userData, const QString &subModule, MOBase::IPluginGame const *game); + int requestUpdateInfo(QString gameName, NexusInterface::UpdatePeriod period, QObject *receiver, QVariant userData, + const QString &subModule) + { + return requestUpdateInfo(gameName, period, receiver, userData, subModule, getGame(gameName)); + } + + int requestUpdateInfo(QString gameName, NexusInterface::UpdatePeriod period, QObject *receiver, QVariant userData, + const QString &subModule, MOBase::IPluginGame const *game); + /** * @brief request nexus descriptions for multiple mods at once * @param modID id of the mod the caller is interested in @@ -378,6 +396,7 @@ signals: void nxmDescriptionAvailable(QString gameName, int modID, QVariant userData, QVariant resultData, int requestID); void nxmModInfoAvailable(QString gameName, int modID, QVariant userData, QVariant resultData, int requestID); + void nxmUpdateInfoAvailable(QString gameName, QVariant userData, QVariant resultData, int requestID); void nxmUpdatesAvailable(QString gameName, int modID, QVariant userData, QVariant resultData, int requestID); void nxmFilesAvailable(QString gameName, int modID, QVariant userData, QVariant resultData, int requestID); void nxmFileInfoAvailable(QString gameName, int modID, int fileID, QVariant userData, QVariant resultData, int requestID); @@ -415,8 +434,10 @@ private: TYPE_FILEINFO, TYPE_DOWNLOADURL, TYPE_TOGGLEENDORSEMENT, - TYPE_GETUPDATES + TYPE_GETUPDATES, + TYPE_CHECKUPDATES } m_Type; + UpdatePeriod m_UpdatePeriod; QVariant m_UserData; QTimer *m_Timeout; QString m_URL; @@ -430,6 +451,7 @@ private: NXMRequestInfo(int modID, Type type, QVariant userData, const QString &subModule, MOBase::IPluginGame const *game); NXMRequestInfo(int modID, QString modVersion, Type type, QVariant userData, const QString &subModule, MOBase::IPluginGame const *game); NXMRequestInfo(int modID, int fileID, Type type, QVariant userData, const QString &subModule, MOBase::IPluginGame const *game); + NXMRequestInfo(UpdatePeriod period, Type type, QVariant userData, const QString &subModule, MOBase::IPluginGame const *game); private: static QAtomicInt s_NextID; |
