From 4bea346b26f2e34120a42fdf2f1c5485ab93f5c9 Mon Sep 17 00:00:00 2001 From: Silarn Date: Sun, 27 Jan 2019 17:08:42 -0600 Subject: Reworking update checks to use the file update info with a fallback --- src/downloadmanager.h | 1132 ++++++++++++++++++++++++------------------------- 1 file changed, 564 insertions(+), 568 deletions(-) (limited to 'src/downloadmanager.h') diff --git a/src/downloadmanager.h b/src/downloadmanager.h index 4cbe31b7..3c36143e 100644 --- a/src/downloadmanager.h +++ b/src/downloadmanager.h @@ -1,568 +1,564 @@ -/* -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 DOWNLOADMANAGER_H -#define DOWNLOADMANAGER_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace MOBase { class IPluginGame; } - -class NexusInterface; -class PluginContainer; - -/*! - * \brief manages downloading of files and provides progress information for gui elements - **/ -class DownloadManager : public MOBase::IDownloadManager -{ - Q_OBJECT - -public: - - enum DownloadState { - STATE_STARTED = 0, - STATE_DOWNLOADING, - STATE_CANCELING, - STATE_PAUSING, - STATE_CANCELED, - STATE_PAUSED, - STATE_ERROR, - STATE_FETCHINGMODINFO, - STATE_FETCHINGFILEINFO, - STATE_NOFETCH, - STATE_READY, - STATE_INSTALLED, - STATE_UNINSTALLED - }; - -private: - - struct DownloadInfo { - ~DownloadInfo() { delete m_FileInfo; } - unsigned int m_DownloadID; - QString m_FileName; - QFile m_Output; - QNetworkReply *m_Reply; - QTime m_StartTime; - qint64 m_PreResumeSize; - std::pair m_Progress; - std::tuple m_SpeedDiff; - bool m_HasData; - DownloadState m_State; - int m_CurrentUrl; - QStringList m_Urls; - qint64 m_ResumePos; - qint64 m_TotalSize; - QDateTime m_Created; // used as a cache in DownloadManager::getFileTime, may not be valid elsewhere - - int m_Tries; - bool m_ReQueried; - - quint32 m_TaskProgressId; - - MOBase::ModRepositoryFileInfo *m_FileInfo { nullptr }; - - bool m_Hidden; - - static DownloadInfo *createNew(const MOBase::ModRepositoryFileInfo *fileInfo, const QStringList &URLs); - static DownloadInfo *createFromMeta(const QString &filePath, bool showHidden, const QString outputDirectory); - - /** - * @brief rename the file - * this will change the file name as well as the display name. It will automatically - * append .unfinished to the name if this file is still being downloaded - * @param newName the new name to setName - * @param renameFile if true, the file is assumed to exist and renamed. If the file does not - * yet exist, set this to false - **/ - void setName(QString newName, bool renameFile); - - unsigned int downloadID() { return m_DownloadID; } - - bool isPausedState(); - - QString currentURL(); - private: - static unsigned int s_NextDownloadID; - private: - DownloadInfo() : m_TotalSize(0), m_ReQueried(false), m_Hidden(false), m_SpeedDiff(std::tuple(0,0,0,0,0)), m_HasData(false) {} - }; - -public: - - /** - * @brief constructor - * - * @param nexusInterface interface to use to retrieve information from the relevant nexus page - * @param parent parent object - **/ - explicit DownloadManager(NexusInterface *nexusInterface, QObject *parent); - - ~DownloadManager(); - - /** - * @brief determine if a download is currently in progress - * - * @return true if there is currently a download in progress - **/ - bool downloadsInProgress(); - - /** - * @brief determine if a download is currently in progress, does not count paused ones. - * - * @return true if there is currently a download in progress (that is not paused already). - **/ - bool downloadsInProgressNoPause(); - - /** - * @brief set the output directory to write to - * - * @param outputDirectory the new output directory - **/ - void setOutputDirectory(const QString &outputDirectory); - - /** - * @brief disables feedback from the downlods fileSystemWhatcher untill disableDownloadsWatcherEnd() is called - * - **/ - static void startDisableDirWatcher(); - - /** - * @brief re-enables feedback from the downlods fileSystemWhatcher after disableDownloadsWatcherStart() was called - **/ - static void endDisableDirWatcher(); - - /** - * @return current download directory - **/ - QString getOutputDirectory() const { return m_OutputDirectory; } - - /** - * @brief setPreferredServers set the list of preferred servers - */ - void setPreferredServers(const std::map &preferredServers); - - /** - * @brief set the list of supported extensions - * @param extensions list of supported extensions - */ - void setSupportedExtensions(const QStringList &extensions); - - /** - * @brief sets whether hidden files are to be shown after all - */ - void setShowHidden(bool showHidden); - - void setPluginContainer(PluginContainer *pluginContainer); - - /** - * @brief download from an already open network connection - * - * @param reply the network reply to download from - * @param fileInfo information about the file, like mod id, file id, version, ... - * @return true if the download was started, false if it wasn't. The latter currently only happens if there is a duplicate and the user decides not to download again - **/ - bool addDownload(QNetworkReply *reply, const MOBase::ModRepositoryFileInfo *fileInfo); - - /** - * @brief download from an already open network connection - * - * @param reply the network reply to download from - * @param fileName the name to use for the file. This may be overridden by the name in the fileInfo-structure or if the http stream specifies a name - * @param fileInfo information previously retrieved from the nexus network - * @return true if the download was started, false if it wasn't. The latter currently only happens if there is a duplicate and the user decides not to download again - **/ - bool addDownload(QNetworkReply *reply, const QStringList &URLs, const QString &fileName, QString gameName, int modID, int fileID = 0, const MOBase::ModRepositoryFileInfo *fileInfo = new MOBase::ModRepositoryFileInfo()); - - /** - * @brief start a download using a nxm-link - * - * starts a download using a nxm-link. The download manager will first query the nexus - * page for file information. - * @param url a nxm link looking like this: nxm://skyrim/mods/1234/files/4711 - * @todo the game name encoded into the link is currently ignored, all downloads are incorrectly assumed to be for the identified game - **/ - void addNXMDownload(const QString &url); - - /** - * @brief retrieve the total number of downloads, both finished and unfinished including downloads from previous sessions - * - * @return total number of downloads - **/ - int numTotalDownloads() const; - - /** - * @brief retrieve number of pending downloads (nexus downloads for which we don't know the name and url yet) - * @return number of pending downloads - */ - int numPendingDownloads() const; - - /** - * @brief retrieve the info of a pending download - * @param index index of the pending download (index in the range [0, numPendingDownloads()[) - * @return pair of modid, fileid - */ - std::tuple getPendingDownload(int index); - - /** - * @brief retrieve the full path to the download specified by index - * - * @param index the index to look up - * @return absolute path of the file - **/ - QString getFilePath(int index) const; - - /** - * @brief retrieve a descriptive name of the download specified by index - * - * @param index index of the file to look up - * @return display name of the file - **/ - QString getDisplayName(int index) const; - - /** - * @brief retrieve the filename of the download specified by index - * - * @param index index of the file to look up - * @return name of the file - **/ - QString getFileName(int index) const; - - /** - * @brief retrieve the file size of the download specified by index - * - * @param index index of the file to look up - * @return size of the file (total size during download) - */ - qint64 getFileSize(int index) const; - - /** - * @brief retrieve the creation time of the download specified by index - * @param index index of the file to look up - * @return size of the file (total size during download) - */ - QDateTime getFileTime(int index) const; - - /** - * @brief retrieve the current progress of the download specified by index - * - * @param index index of the file to look up - * @return progress of the download in percent (integer) - **/ - std::pair getProgress(int index) const; - - /** - * @brief retrieve the current state of the download - * - * retrieve the current state of the download. A download usually goes through - * the following states: - * started -> downloading -> fetching mod info -> fetching file info -> done - * in case of downloads started via nxm-link, file information is fetched first - * - * @param index index of the file to look up - * @return the download state - **/ - DownloadState getState(int index) const; - - /** - * @param index index of the file to look up - * @return true if the nexus information for this download is not complete - **/ - bool isInfoIncomplete(int index) const; - - /** - * @brief retrieve the nexus mod id of the download specified by index - * - * @param index index of the file to look up - * @return the nexus mod id - **/ - int getModID(int index) const; - - /** - * @brief retrieve the game name of the downlaod specified by the index - * - * @param index index of the file to look up - * @return the game name - **/ - QString getGameName(int index) const; - - /** - * @brief determine if the specified file is supposed to be hidden - * @param index index of the file to look up - * @return true if the specified file is supposed to be hidden - */ - bool isHidden(int index) const; - - /** - * @brief retrieve all nexus info of the download specified by index - * - * @param index index of the file to look up - * @return the nexus mod information - **/ - const MOBase::ModRepositoryFileInfo *getFileInfo(int index) const; - - /** - * @brief mark a download as installed - * - * @param index index of the file to mark installed - */ - void markInstalled(int index); - - void markInstalled(QString download); - - /** - * @brief mark a download as uninstalled - * - * @param index index of the file to mark uninstalled - */ - void markUninstalled(int index); - - void markUninstalled(QString download); - - /** - * @brief refreshes the list of downloads - */ - void refreshList(); - - /** - * @brief Sort function for download servers - * @param LHS - * @param RHS - * @return - */ - static bool ServerByPreference(const std::map &preferredServers, const QVariant &LHS, const QVariant &RHS); - - - virtual int startDownloadURLs(const QStringList &urls); - - virtual int startDownloadNexusFile(int modID, int fileID); - - virtual QString downloadPath(int id); - - /** - * @brief retrieve a download index from the filename - * @param fileName file to look up - * @return index of that download or -1 if it wasn't found - */ - int indexByName(const QString &fileName) const; - - void pauseAll(); - -signals: - - void aboutToUpdate(); - - /** - * @brief signals that the specified download has changed - * - * @param row the row that changed. This corresponds to the download index - **/ - void update(int row); - - /** - * @brief signals the ui that a message should be displayed - * - * @param message the message to display - **/ - void showMessage(const QString &message); - - /** - * @brief emitted whenever the state of a download changes - * @param row the row that changed - * @param state the new state - */ - void stateChanged(int row, DownloadManager::DownloadState state); - - /** - * @brief emitted whenever a download completes successfully, reporting the download speed for the server used - */ - void downloadSpeed(const QString &serverName, int bytesPerSecond); - - /** - * @brief emitted whenever a new download is added to the list - */ - void downloadAdded(); - -public slots: - - /** - * @brief removes the specified download - * - * @param index index of the download to remove - * @param deleteFile if true, the file will also be deleted from disc, otherwise it is only marked as hidden. - **/ - void removeDownload(int index, bool deleteFile); - - /** - * @brief restores the specified download to view (which was previously hidden - * @param index index of the download to restore - */ - void restoreDownload(int index); - - /** - * @brief cancel the specified download. This will lead to the corresponding file to be deleted - * - * @param index index of the download to cancel - **/ - void cancelDownload(int index); - - void pauseDownload(int index); - - void resumeDownload(int index); - - void queryInfo(int index); - - void visitOnNexus(int index); - - void openFile(int index); - - void openInDownloadsFolder(int index); - - void nxmDescriptionAvailable(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); - - void nxmDownloadURLsAvailable(QString gameName, int modID, int fileID, QVariant userData, QVariant resultData, int requestID); - - void nxmRequestFailed(QString gameName, int modID, int fileID, QVariant userData, int requestID, const QString &errorString); - - void managedGameChanged(MOBase::IPluginGame const *gamePlugin); - -private slots: - - void downloadProgress(qint64 bytesReceived, qint64 bytesTotal); - void downloadReadyRead(); - void downloadFinished(int index = 0); - void downloadError(QNetworkReply::NetworkError error); - void metaDataChanged(); - void directoryChanged(const QString &dirctory); - void checkDownloadTimeout(); - -private: - - void createMetaFile(DownloadInfo *info); - DownloadManager::DownloadInfo* getDownloadInfo(QString fileName); - -public: - - /** Get a unique filename for a download. - * - * This allows you multiple versions of download files, useful if the file - * comes from a web site with no version control - * - * @param basename: Name of the file - * - * @return Unique(ish) name - */ - QString getDownloadFileName(const QString &baseName, bool rename = false) const; - -private: - - void startDownload(QNetworkReply *reply, DownloadInfo *newDownload, bool resume); - void resumeDownloadInt(int index); - - /** - * @brief start a download from a url - * - * @param url the url to download from - * @param fileInfo information previously retrieved from the mod page - * @return true if the download was started, false if it wasn't. The latter currently only happens if there is a duplicate and the user decides not to download again - **/ - bool addDownload(const QStringList &URLs, QString gameName, int modID, int fileID, const MOBase::ModRepositoryFileInfo *fileInfo); - - // important: the caller has to lock the list-mutex, otherwise the DownloadInfo-pointer might get invalidated at any time - DownloadInfo *findDownload(QObject *reply, int *index = nullptr) const; - - void removeFile(int index, bool deleteFile); - - void refreshAlphabeticalTranslation(); - - bool ByName(int LHS, int RHS); - - QString getFileNameFromNetworkReply(QNetworkReply *reply); - - void setState(DownloadInfo *info, DownloadManager::DownloadState state); - - DownloadInfo *downloadInfoByID(unsigned int id); - - QDateTime matchDate(const QString &timeString); - - void removePending(QString gameName, int modID, int fileID); - - static QString getFileTypeString(int fileType); - - void writeData(DownloadInfo *info); - -private: - - static const int AUTOMATIC_RETRIES = 3; - -private: - - NexusInterface *m_NexusInterface; - - QVector> m_PendingDownloads; - - QVector m_ActiveDownloads; - - QString m_OutputDirectory; - std::map m_PreferredServers; - QStringList m_SupportedExtensions; - std::set m_RequestIDs; - QVector m_AlphabeticalTranslation; - - QFileSystemWatcher m_DirWatcher; - - //The dirWatcher is actually triggering off normal Mo operations such as deleting downloads or editing .meta files - //so it needs to be disabled during operations that are known to cause the creation or deletion of files in the Downloads folder. - //Notably using QSettings to edit a file creates a temporarily .lock file that causes the Watcher to trigger multiple listRefreshes freezing the ui. - static int m_DirWatcherDisabler; - - - std::map m_DownloadFails; - - bool m_ShowHidden; - - QRegExp m_DateExpression; - - MOBase::IPluginGame const *m_ManagedGame; - - QTimer m_TimeoutTimer; -}; - - - -#endif // DOWNLOADMANAGER_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 DOWNLOADMANAGER_H +#define DOWNLOADMANAGER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace MOBase { class IPluginGame; } + +class NexusInterface; +class PluginContainer; + +/*! + * \brief manages downloading of files and provides progress information for gui elements + **/ +class DownloadManager : public MOBase::IDownloadManager +{ + Q_OBJECT + +public: + + enum DownloadState { + STATE_STARTED = 0, + STATE_DOWNLOADING, + STATE_CANCELING, + STATE_PAUSING, + STATE_CANCELED, + STATE_PAUSED, + STATE_ERROR, + STATE_FETCHINGMODINFO, + STATE_FETCHINGFILEINFO, + STATE_NOFETCH, + STATE_READY, + STATE_INSTALLED, + STATE_UNINSTALLED + }; + +private: + + struct DownloadInfo { + ~DownloadInfo() { delete m_FileInfo; } + unsigned int m_DownloadID; + QString m_FileName; + QFile m_Output; + QNetworkReply *m_Reply; + QTime m_StartTime; + qint64 m_PreResumeSize; + std::pair m_Progress; + std::tuple m_SpeedDiff; + bool m_HasData; + DownloadState m_State; + int m_CurrentUrl; + QStringList m_Urls; + qint64 m_ResumePos; + qint64 m_TotalSize; + QDateTime m_Created; // used as a cache in DownloadManager::getFileTime, may not be valid elsewhere + + int m_Tries; + bool m_ReQueried; + + quint32 m_TaskProgressId; + + MOBase::ModRepositoryFileInfo *m_FileInfo { nullptr }; + + bool m_Hidden; + + static DownloadInfo *createNew(const MOBase::ModRepositoryFileInfo *fileInfo, const QStringList &URLs); + static DownloadInfo *createFromMeta(const QString &filePath, bool showHidden, const QString outputDirectory); + + /** + * @brief rename the file + * this will change the file name as well as the display name. It will automatically + * append .unfinished to the name if this file is still being downloaded + * @param newName the new name to setName + * @param renameFile if true, the file is assumed to exist and renamed. If the file does not + * yet exist, set this to false + **/ + void setName(QString newName, bool renameFile); + + unsigned int downloadID() { return m_DownloadID; } + + bool isPausedState(); + + QString currentURL(); + private: + static unsigned int s_NextDownloadID; + private: + DownloadInfo() : m_TotalSize(0), m_ReQueried(false), m_Hidden(false), m_SpeedDiff(std::tuple(0,0,0,0,0)), m_HasData(false) {} + }; + +public: + + /** + * @brief constructor + * + * @param nexusInterface interface to use to retrieve information from the relevant nexus page + * @param parent parent object + **/ + explicit DownloadManager(NexusInterface *nexusInterface, QObject *parent); + + ~DownloadManager(); + + /** + * @brief determine if a download is currently in progress + * + * @return true if there is currently a download in progress + **/ + bool downloadsInProgress(); + + /** + * @brief determine if a download is currently in progress, does not count paused ones. + * + * @return true if there is currently a download in progress (that is not paused already). + **/ + bool downloadsInProgressNoPause(); + + /** + * @brief set the output directory to write to + * + * @param outputDirectory the new output directory + **/ + void setOutputDirectory(const QString &outputDirectory); + + /** + * @brief disables feedback from the downlods fileSystemWhatcher untill disableDownloadsWatcherEnd() is called + * + **/ + static void startDisableDirWatcher(); + + /** + * @brief re-enables feedback from the downlods fileSystemWhatcher after disableDownloadsWatcherStart() was called + **/ + static void endDisableDirWatcher(); + + /** + * @return current download directory + **/ + QString getOutputDirectory() const { return m_OutputDirectory; } + + /** + * @brief setPreferredServers set the list of preferred servers + */ + void setPreferredServers(const std::map &preferredServers); + + /** + * @brief set the list of supported extensions + * @param extensions list of supported extensions + */ + void setSupportedExtensions(const QStringList &extensions); + + /** + * @brief sets whether hidden files are to be shown after all + */ + void setShowHidden(bool showHidden); + + void setPluginContainer(PluginContainer *pluginContainer); + + /** + * @brief download from an already open network connection + * + * @param reply the network reply to download from + * @param fileInfo information about the file, like mod id, file id, version, ... + * @return true if the download was started, false if it wasn't. The latter currently only happens if there is a duplicate and the user decides not to download again + **/ + bool addDownload(QNetworkReply *reply, const MOBase::ModRepositoryFileInfo *fileInfo); + + /** + * @brief download from an already open network connection + * + * @param reply the network reply to download from + * @param fileName the name to use for the file. This may be overridden by the name in the fileInfo-structure or if the http stream specifies a name + * @param fileInfo information previously retrieved from the nexus network + * @return true if the download was started, false if it wasn't. The latter currently only happens if there is a duplicate and the user decides not to download again + **/ + bool addDownload(QNetworkReply *reply, const QStringList &URLs, const QString &fileName, QString gameName, int modID, int fileID = 0, const MOBase::ModRepositoryFileInfo *fileInfo = new MOBase::ModRepositoryFileInfo()); + + /** + * @brief start a download using a nxm-link + * + * starts a download using a nxm-link. The download manager will first query the nexus + * page for file information. + * @param url a nxm link looking like this: nxm://skyrim/mods/1234/files/4711 + * @todo the game name encoded into the link is currently ignored, all downloads are incorrectly assumed to be for the identified game + **/ + void addNXMDownload(const QString &url); + + /** + * @brief retrieve the total number of downloads, both finished and unfinished including downloads from previous sessions + * + * @return total number of downloads + **/ + int numTotalDownloads() const; + + /** + * @brief retrieve number of pending downloads (nexus downloads for which we don't know the name and url yet) + * @return number of pending downloads + */ + int numPendingDownloads() const; + + /** + * @brief retrieve the info of a pending download + * @param index index of the pending download (index in the range [0, numPendingDownloads()[) + * @return pair of modid, fileid + */ + std::tuple getPendingDownload(int index); + + /** + * @brief retrieve the full path to the download specified by index + * + * @param index the index to look up + * @return absolute path of the file + **/ + QString getFilePath(int index) const; + + /** + * @brief retrieve a descriptive name of the download specified by index + * + * @param index index of the file to look up + * @return display name of the file + **/ + QString getDisplayName(int index) const; + + /** + * @brief retrieve the filename of the download specified by index + * + * @param index index of the file to look up + * @return name of the file + **/ + QString getFileName(int index) const; + + /** + * @brief retrieve the file size of the download specified by index + * + * @param index index of the file to look up + * @return size of the file (total size during download) + */ + qint64 getFileSize(int index) const; + + /** + * @brief retrieve the creation time of the download specified by index + * @param index index of the file to look up + * @return size of the file (total size during download) + */ + QDateTime getFileTime(int index) const; + + /** + * @brief retrieve the current progress of the download specified by index + * + * @param index index of the file to look up + * @return progress of the download in percent (integer) + **/ + std::pair getProgress(int index) const; + + /** + * @brief retrieve the current state of the download + * + * retrieve the current state of the download. A download usually goes through + * the following states: + * started -> downloading -> fetching mod info -> fetching file info -> done + * in case of downloads started via nxm-link, file information is fetched first + * + * @param index index of the file to look up + * @return the download state + **/ + DownloadState getState(int index) const; + + /** + * @param index index of the file to look up + * @return true if the nexus information for this download is not complete + **/ + bool isInfoIncomplete(int index) const; + + /** + * @brief retrieve the nexus mod id of the download specified by index + * + * @param index index of the file to look up + * @return the nexus mod id + **/ + int getModID(int index) const; + + /** + * @brief retrieve the game name of the downlaod specified by the index + * + * @param index index of the file to look up + * @return the game name + **/ + QString getGameName(int index) const; + + /** + * @brief determine if the specified file is supposed to be hidden + * @param index index of the file to look up + * @return true if the specified file is supposed to be hidden + */ + bool isHidden(int index) const; + + /** + * @brief retrieve all nexus info of the download specified by index + * + * @param index index of the file to look up + * @return the nexus mod information + **/ + const MOBase::ModRepositoryFileInfo *getFileInfo(int index) const; + + /** + * @brief mark a download as installed + * + * @param index index of the file to mark installed + */ + void markInstalled(int index); + + void markInstalled(QString download); + + /** + * @brief mark a download as uninstalled + * + * @param index index of the file to mark uninstalled + */ + void markUninstalled(int index); + + void markUninstalled(QString download); + + /** + * @brief refreshes the list of downloads + */ + void refreshList(); + + /** + * @brief Sort function for download servers + * @param LHS + * @param RHS + * @return + */ + static bool ServerByPreference(const std::map &preferredServers, const QVariant &LHS, const QVariant &RHS); + + + virtual int startDownloadURLs(const QStringList &urls); + + virtual int startDownloadNexusFile(int modID, int fileID); + + virtual QString downloadPath(int id); + + /** + * @brief retrieve a download index from the filename + * @param fileName file to look up + * @return index of that download or -1 if it wasn't found + */ + int indexByName(const QString &fileName) const; + + void pauseAll(); + +signals: + + void aboutToUpdate(); + + /** + * @brief signals that the specified download has changed + * + * @param row the row that changed. This corresponds to the download index + **/ + void update(int row); + + /** + * @brief signals the ui that a message should be displayed + * + * @param message the message to display + **/ + void showMessage(const QString &message); + + /** + * @brief emitted whenever the state of a download changes + * @param row the row that changed + * @param state the new state + */ + void stateChanged(int row, DownloadManager::DownloadState state); + + /** + * @brief emitted whenever a download completes successfully, reporting the download speed for the server used + */ + void downloadSpeed(const QString &serverName, int bytesPerSecond); + + /** + * @brief emitted whenever a new download is added to the list + */ + void downloadAdded(); + +public slots: + + /** + * @brief removes the specified download + * + * @param index index of the download to remove + * @param deleteFile if true, the file will also be deleted from disc, otherwise it is only marked as hidden. + **/ + void removeDownload(int index, bool deleteFile); + + /** + * @brief restores the specified download to view (which was previously hidden + * @param index index of the download to restore + */ + void restoreDownload(int index); + + /** + * @brief cancel the specified download. This will lead to the corresponding file to be deleted + * + * @param index index of the download to cancel + **/ + void cancelDownload(int index); + + void pauseDownload(int index); + + void resumeDownload(int index); + + void queryInfo(int index); + + void visitOnNexus(int index); + + void openFile(int index); + + void openInDownloadsFolder(int index); + + void nxmDescriptionAvailable(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); + + void nxmDownloadURLsAvailable(QString gameName, 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 managedGameChanged(MOBase::IPluginGame const *gamePlugin); + +private slots: + + void downloadProgress(qint64 bytesReceived, qint64 bytesTotal); + void downloadReadyRead(); + void downloadFinished(int index = 0); + void downloadError(QNetworkReply::NetworkError error); + void metaDataChanged(); + void directoryChanged(const QString &dirctory); + void checkDownloadTimeout(); + +private: + + void createMetaFile(DownloadInfo *info); + DownloadManager::DownloadInfo* getDownloadInfo(QString fileName); + +public: + + /** Get a unique filename for a download. + * + * This allows you multiple versions of download files, useful if the file + * comes from a web site with no version control + * + * @param basename: Name of the file + * + * @return Unique(ish) name + */ + QString getDownloadFileName(const QString &baseName, bool rename = false) const; + +private: + + void startDownload(QNetworkReply *reply, DownloadInfo *newDownload, bool resume); + void resumeDownloadInt(int index); + + /** + * @brief start a download from a url + * + * @param url the url to download from + * @param fileInfo information previously retrieved from the mod page + * @return true if the download was started, false if it wasn't. The latter currently only happens if there is a duplicate and the user decides not to download again + **/ + bool addDownload(const QStringList &URLs, QString gameName, int modID, int fileID, const MOBase::ModRepositoryFileInfo *fileInfo); + + // important: the caller has to lock the list-mutex, otherwise the DownloadInfo-pointer might get invalidated at any time + DownloadInfo *findDownload(QObject *reply, int *index = nullptr) const; + + void removeFile(int index, bool deleteFile); + + void refreshAlphabeticalTranslation(); + + bool ByName(int LHS, int RHS); + + QString getFileNameFromNetworkReply(QNetworkReply *reply); + + void setState(DownloadInfo *info, DownloadManager::DownloadState state); + + DownloadInfo *downloadInfoByID(unsigned int id); + + void removePending(QString gameName, int modID, int fileID); + + static QString getFileTypeString(int fileType); + + void writeData(DownloadInfo *info); + +private: + + static const int AUTOMATIC_RETRIES = 3; + +private: + + NexusInterface *m_NexusInterface; + + QVector> m_PendingDownloads; + + QVector m_ActiveDownloads; + + QString m_OutputDirectory; + std::map m_PreferredServers; + QStringList m_SupportedExtensions; + std::set m_RequestIDs; + QVector m_AlphabeticalTranslation; + + QFileSystemWatcher m_DirWatcher; + + //The dirWatcher is actually triggering off normal Mo operations such as deleting downloads or editing .meta files + //so it needs to be disabled during operations that are known to cause the creation or deletion of files in the Downloads folder. + //Notably using QSettings to edit a file creates a temporarily .lock file that causes the Watcher to trigger multiple listRefreshes freezing the ui. + static int m_DirWatcherDisabler; + + + std::map m_DownloadFails; + + bool m_ShowHidden; + + MOBase::IPluginGame const *m_ManagedGame; + + QTimer m_TimeoutTimer; +}; + + + +#endif // DOWNLOADMANAGER_H -- cgit v1.3.1 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(-) (limited to 'src/downloadmanager.h') 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 From 70bcd97bd23756a92507006e6202eee7af902cd3 Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Thu, 7 Mar 2019 16:32:17 -0600 Subject: Use MD5 when querying info before bothering the user --- src/downloadlist.cpp | 5 +- src/downloadlistwidget.cpp | 9 +++- src/downloadlistwidget.h | 2 + src/downloadmanager.cpp | 114 ++++++++++++++++++++++++++++++++++++++++++++- src/downloadmanager.h | 37 +++++++++------ src/mainwindow.cpp | 1 + src/nexusinterface.cpp | 60 ++++++++++++++++++++++++ src/nexusinterface.h | 18 +++++++ 8 files changed, 225 insertions(+), 21 deletions(-) (limited to 'src/downloadmanager.h') diff --git a/src/downloadlist.cpp b/src/downloadlist.cpp index 765ee646..dd72abbd 100644 --- a/src/downloadlist.cpp +++ b/src/downloadlist.cpp @@ -104,8 +104,9 @@ QVariant DownloadList::data(const QModelIndex &index, int role) const case DownloadManager::STATE_CANCELED: return tr("Canceled"); case DownloadManager::STATE_PAUSED: return tr("Paused"); case DownloadManager::STATE_ERROR: return tr("Error"); - case DownloadManager::STATE_FETCHINGMODINFO: return tr("Fetching Info 1"); - case DownloadManager::STATE_FETCHINGFILEINFO: return tr("Fetching Info 2"); + case DownloadManager::STATE_FETCHINGMODINFO: return tr("Fetching Info"); + case DownloadManager::STATE_FETCHINGFILEINFO: return tr("Fetching Info"); + case DownloadManager::STATE_FETCHINGMODINFO_MD5: return tr("Fetching Info"); case DownloadManager::STATE_READY: return tr("Downloaded"); case DownloadManager::STATE_INSTALLED: return tr("Installed"); case DownloadManager::STATE_UNINSTALLED: return tr("Uninstalled"); diff --git a/src/downloadlistwidget.cpp b/src/downloadlistwidget.cpp index be9d30e2..f0c4da1a 100644 --- a/src/downloadlistwidget.cpp +++ b/src/downloadlistwidget.cpp @@ -199,8 +199,8 @@ void DownloadListWidget::onCustomContextMenu(const QPoint &point) if (state >= DownloadManager::STATE_READY) { menu.addAction(tr("Install"), this, SLOT(issueInstall())); - if (m_Manager->isInfoIncomplete(m_ContextRow)) - menu.addAction(tr("Query Info"), this, SLOT(issueQueryInfo())); + if (m_Manager->isInfoIncomplete(m_ContextRow)) + menu.addAction(tr("Query Info"), this, SLOT(issueQueryInfoMd5())); else menu.addAction(tr("Visit on Nexus"), this, SLOT(issueVisitOnNexus())); menu.addAction(tr("Open File"), this, SLOT(issueOpenFile())); @@ -252,6 +252,11 @@ void DownloadListWidget::issueQueryInfo() emit queryInfo(m_ContextRow); } +void DownloadListWidget::issueQueryInfoMd5() +{ + emit queryInfoMd5(m_ContextRow); +} + void DownloadListWidget::issueDelete() { if (QMessageBox::question(nullptr, tr("Delete Files?"), diff --git a/src/downloadlistwidget.h b/src/downloadlistwidget.h index 4776d259..ad07b0f1 100644 --- a/src/downloadlistwidget.h +++ b/src/downloadlistwidget.h @@ -78,6 +78,7 @@ public: signals: void installDownload(int index); void queryInfo(int index); + void queryInfoMd5(int index); void removeDownload(int index, bool deleteFile); void restoreDownload(int index); void cancelDownload(int index); @@ -109,6 +110,7 @@ private slots: void issueRemoveFromViewCompleted(); void issueRemoveFromViewUninstalled(); void issueQueryInfo(); + void issueQueryInfoMd5(); private: DownloadManager *m_Manager; diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index c3a6a3e7..3563ecdb 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -262,7 +262,8 @@ void DownloadManager::pauseAll() foreach (DownloadInfo *info, m_ActiveDownloads) { if ((info->m_State < STATE_CANCELED) || (info->m_State == STATE_FETCHINGFILEINFO) || - (info->m_State == STATE_FETCHINGMODINFO)) { + (info->m_State == STATE_FETCHINGMODINFO) || + (info->m_State == STATE_FETCHINGMODINFO_MD5)) { done = false; break; } @@ -819,7 +820,9 @@ void DownloadManager::pauseDownload(int index) } else { setState(info, STATE_PAUSED); } - } else if ((info->m_State == STATE_FETCHINGMODINFO) || (info->m_State == STATE_FETCHINGFILEINFO)) { + } else if ((info->m_State == STATE_FETCHINGMODINFO) || + (info->m_State == STATE_FETCHINGFILEINFO) || + (info->m_State == STATE_FETCHINGMODINFO_MD5)) { setState(info, STATE_READY); } } @@ -952,6 +955,46 @@ void DownloadManager::queryInfo(int index) setState(info, STATE_FETCHINGMODINFO); } +void DownloadManager::queryInfoMd5(int index) +{ + if ((index < 0) || (index >= m_ActiveDownloads.size())) { + reportError(tr("query: invalid download index %1").arg(index)); + return; + } + DownloadInfo *info = m_ActiveDownloads[index]; + + if (info->m_FileInfo->repository != "Nexus") { + qWarning("re-querying file info is currently only possible with Nexus"); + return; + } + + if (info->m_State < DownloadManager::STATE_READY) { + // UI shouldn't allow this + return; + } + + info->m_GamesToQuery << m_ManagedGame->gameShortName(); + info->m_GamesToQuery << m_ManagedGame->validShortNames(); + + QFile downloadFile(info->m_FileName); + if (!downloadFile.exists()) { + downloadFile.setFileName(m_OrganizerCore->downloadsPath() + "\\" + info->m_FileName); + } + if (!downloadFile.exists()) { + qDebug("Can't find download file %s", info->m_FileName); + return; + } + if (!downloadFile.open(QIODevice::ReadOnly)) { + qDebug("Can't open download file %s", info->m_FileName); + return; + } + info->m_Hash = QCryptographicHash::hash(downloadFile.readAll(), QCryptographicHash::Md5); + downloadFile.close(); + + info->m_ReQueried = true; + setState(info, STATE_FETCHINGMODINFO_MD5); +} + void DownloadManager::visitOnNexus(int index) { if ((index < 0) || (index >= m_ActiveDownloads.size())) { @@ -1329,6 +1372,9 @@ void DownloadManager::setState(DownloadManager::DownloadInfo *info, DownloadMana case STATE_FETCHINGFILEINFO: { m_RequestIDs.insert(m_NexusInterface->requestFiles(info->m_FileInfo->gameName, info->m_FileInfo->modID, this, info->m_DownloadID, QString())); } break; + case STATE_FETCHINGMODINFO_MD5: { + m_RequestIDs.insert(m_NexusInterface->requestInfoFromMd5(info->m_GamesToQuery[0], info->m_Hash, this, info->m_DownloadID, QString())); + } break; case STATE_READY: { createMetaFile(info); emit downloadComplete(row); @@ -1682,6 +1728,51 @@ void DownloadManager::nxmDownloadURLsAvailable(QString gameName, int modID, int } +void DownloadManager::nxmFileInfoFromMd5Available(QString gameName, QVariant userData, QVariant resultData, int requestID) +{ + std::set::iterator idIter = m_RequestIDs.find(requestID); + if (idIter == m_RequestIDs.end()) { + return; + } else { + m_RequestIDs.erase(idIter); + } + + auto resultlist = resultData.toList(); + auto results = resultlist[0].toMap(); + auto fileDetails = results["file_details"].toMap(); + auto modDetails = results["mod"].toMap(); + + DownloadInfo *info = downloadInfoByID(userData.toInt()); + + info->m_FileInfo->name = fileDetails["name"].toString(); + info->m_FileInfo->fileID = fileDetails["file_id"].toInt(); + info->m_FileInfo->description = fileDetails["description"].toString(); + info->m_FileInfo->version.parse(fileDetails["version"].toString()); + if (!info->m_FileInfo->version.isValid()) + info->m_FileInfo->version.parse(fileDetails["mod_version"].toString()); + info->m_FileInfo->fileCategory = fileDetails["category_id"].toInt(); + + info->m_FileInfo->modID = modDetails["mod_id"].toInt(); + info->m_FileInfo->modName = modDetails["name"].toString(); + info->m_FileInfo->categoryID = modDetails["category_id"].toInt(); + + QString gameShortName = 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) { + gameShortName = gamePlugin->gameShortName(); + break; + } + } + + info->m_FileInfo->gameName = gameShortName; + + setState(info, STATE_READY); +} + + void DownloadManager::nxmRequestFailed(QString gameName, int modID, int fileID, QVariant userData, int requestID, QNetworkReply::NetworkError error, const QString &errorString) { std::set::iterator idIter = m_RequestIDs.find(requestID); @@ -1691,10 +1782,29 @@ void DownloadManager::nxmRequestFailed(QString gameName, int modID, int fileID, m_RequestIDs.erase(idIter); } + DownloadInfo *userDataInfo = downloadInfoByID(userData.toInt()); + int index = 0; for (QVector::iterator iter = m_ActiveDownloads.begin(); iter != m_ActiveDownloads.end(); ++iter, ++index) { DownloadInfo *info = *iter; + if (info != userDataInfo) + continue; + + // MD5 searches continue until all possible games are done + if (info->m_State == STATE_FETCHINGMODINFO_MD5) { + if (info->m_GamesToQuery.count() >= 2) { + info->m_GamesToQuery.pop_front(); + setState(info, STATE_FETCHINGMODINFO_MD5); + break; + } else { + info->m_State = STATE_READY; + queryInfo(index); + emit update(index); + break; + } + } + if (info->m_FileInfo->modID == modID) { if (info->m_State < STATE_FETCHINGMODINFO) { m_ActiveDownloads.erase(iter); diff --git a/src/downloadmanager.h b/src/downloadmanager.h index 841a9fbc..8033989e 100644 --- a/src/downloadmanager.h +++ b/src/downloadmanager.h @@ -61,6 +61,7 @@ public: STATE_ERROR, STATE_FETCHINGMODINFO, STATE_FETCHINGFILEINFO, + STATE_FETCHINGMODINFO_MD5, STATE_NOFETCH, STATE_READY, STATE_INSTALLED, @@ -86,6 +87,8 @@ private: qint64 m_ResumePos; qint64 m_TotalSize; QDateTime m_Created; // used as a cache in DownloadManager::getFileTime, may not be valid elsewhere + QByteArray m_Hash; + QStringList m_GamesToQuery; int m_Tries; bool m_ReQueried; @@ -153,16 +156,16 @@ public: **/ void setOutputDirectory(const QString &outputDirectory); - /** - * @brief disables feedback from the downlods fileSystemWhatcher untill disableDownloadsWatcherEnd() is called - * - **/ - static void startDisableDirWatcher(); + /** + * @brief disables feedback from the downlods fileSystemWhatcher untill disableDownloadsWatcherEnd() is called + * + **/ + static void startDisableDirWatcher(); - /** - * @brief re-enables feedback from the downlods fileSystemWhatcher after disableDownloadsWatcherStart() was called - **/ - static void endDisableDirWatcher(); + /** + * @brief re-enables feedback from the downlods fileSystemWhatcher after disableDownloadsWatcherStart() was called + **/ + static void endDisableDirWatcher(); /** * @return current download directory @@ -290,7 +293,7 @@ public: * the following states: * started -> downloading -> fetching mod info -> fetching file info -> done * in case of downloads started via nxm-link, file information is fetched first - * + * * @param index index of the file to look up * @return the download state **/ @@ -444,7 +447,9 @@ public slots: void queryInfo(int index); - void visitOnNexus(int index); + void queryInfoMd5(int index); + + void visitOnNexus(int index); void openFile(int index); @@ -458,6 +463,8 @@ public slots: void nxmDownloadURLsAvailable(QString gameName, int modID, int fileID, QVariant userData, QVariant resultData, int requestID); + void nxmFileInfoFromMd5Available(QString gameName, 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 managedGameChanged(MOBase::IPluginGame const *gamePlugin); @@ -547,10 +554,10 @@ private: QFileSystemWatcher m_DirWatcher; - //The dirWatcher is actually triggering off normal Mo operations such as deleting downloads or editing .meta files - //so it needs to be disabled during operations that are known to cause the creation or deletion of files in the Downloads folder. - //Notably using QSettings to edit a file creates a temporarily .lock file that causes the Watcher to trigger multiple listRefreshes freezing the ui. - static int m_DirWatcherDisabler; + //The dirWatcher is actually triggering off normal Mo operations such as deleting downloads or editing .meta files + //so it needs to be disabled during operations that are known to cause the creation or deletion of files in the Downloads folder. + //Notably using QSettings to edit a file creates a temporarily .lock file that causes the Watcher to trigger multiple listRefreshes freezing the ui. + static int m_DirWatcherDisabler; std::map m_DownloadFails; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 3f6da2a6..0bdefe80 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -5518,6 +5518,7 @@ void MainWindow::initDownloadView() connect(ui->downloadView, SIGNAL(installDownload(int)), &m_OrganizerCore, SLOT(installDownload(int))); connect(ui->downloadView, SIGNAL(queryInfo(int)), m_OrganizerCore.downloadManager(), SLOT(queryInfo(int))); + connect(ui->downloadView, SIGNAL(queryInfoMd5(int)), m_OrganizerCore.downloadManager(), SLOT(queryInfoMd5(int))); connect(ui->downloadView, SIGNAL(visitOnNexus(int)), m_OrganizerCore.downloadManager(), SLOT(visitOnNexus(int))); connect(ui->downloadView, SIGNAL(openFile(int)), m_OrganizerCore.downloadManager(), SLOT(openFile(int))); connect(ui->downloadView, SIGNAL(openInDownloadsFolder(int)), m_OrganizerCore.downloadManager(), SLOT(openInDownloadsFolder(int))); diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp index b1f50fd8..33c5983c 100644 --- a/src/nexusinterface.cpp +++ b/src/nexusinterface.cpp @@ -555,6 +555,23 @@ int NexusInterface::requestToggleTracking(QString gameName, int modID, bool trac return -1; } +int NexusInterface::requestInfoFromMd5(QString gameName, QByteArray &hash, QObject *receiver, QVariant userData, + const QString &subModule, MOBase::IPluginGame const *game) +{ + NXMRequestInfo requestInfo(hash, NXMRequestInfo::TYPE_FILEINFO_MD5, userData, subModule, game); + requestInfo.m_Hash = hash; + m_RequestQueue.enqueue(requestInfo); + + connect(this, SIGNAL(nxmFileInfoFromMd5Available(QString, QVariant, QVariant, int)), + receiver, SLOT(nxmFileInfoFromMd5Available(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; +} + IPluginGame* NexusInterface::getGame(QString gameName) const { auto gamePlugins = m_PluginContainer->plugins(); @@ -673,6 +690,9 @@ void NexusInterface::nextRequest() case NXMRequestInfo::TYPE_TRACKEDMODS: { url = QStringLiteral("%1/user/tracked_mods").arg(info.m_URL); } break; + case NXMRequestInfo::TYPE_FILEINFO_MD5: { + url = QStringLiteral("%1/games/%2/mods/md5_search/%3").arg(info.m_URL).arg(info.m_GameName).arg(QString(info.m_Hash.toHex())); + } } } else { url = info.m_URL; @@ -719,6 +739,7 @@ void NexusInterface::requestFinished(std::list::iterator iter) if (reply->error() != QNetworkReply::NoError) { int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + if (statusCode == 429) { if (reply->rawHeader("x-rl-daily-remaining").toInt() || reply->rawHeader("x-rl-hourly-remaining").toInt()) qWarning("You appear to be making requests to the Nexus API too quickly and are being throttled. Please inform the MO2 team."); @@ -793,6 +814,9 @@ void NexusInterface::requestFinished(std::list::iterator iter) case NXMRequestInfo::TYPE_TRACKEDMODS: { emit nxmTrackedModsAvailable(iter->m_UserData, result, iter->m_ID); } break; + case NXMRequestInfo::TYPE_FILEINFO_MD5: { + emit nxmFileInfoFromMd5Available(iter->m_GameName, iter->m_UserData, result, iter->m_ID); + } break; } m_RemainingDailyRequests = reply->rawHeader("x-rl-daily-remaining").toInt(); @@ -890,6 +914,8 @@ NexusInterface::NXMRequestInfo::NXMRequestInfo(int modID , m_NexusGameID(game->nexusGameID()) , m_GameName(game->gameNexusName()) , m_Endorse(false) + , m_Track(false) + , m_Hash(QByteArray()) { } @@ -915,6 +941,8 @@ NexusInterface::NXMRequestInfo::NXMRequestInfo(int modID , m_NexusGameID(game->nexusGameID()) , m_GameName(game->gameNexusName()) , m_Endorse(false) + , m_Track(false) + , m_Hash(QByteArray()) {} NexusInterface::NXMRequestInfo::NXMRequestInfo(int modID @@ -939,6 +967,8 @@ NexusInterface::NXMRequestInfo::NXMRequestInfo(int modID , m_NexusGameID(game->nexusGameID()) , m_GameName(game->gameNexusName()) , m_Endorse(false) + , m_Track(false) + , m_Hash(QByteArray()) {} NexusInterface::NXMRequestInfo::NXMRequestInfo(Type type @@ -960,6 +990,8 @@ NexusInterface::NXMRequestInfo::NXMRequestInfo(Type type , m_NexusGameID(0) , m_GameName("") , m_Endorse(false) + , m_Track(false) + , m_Hash(QByteArray()) {} @@ -984,4 +1016,32 @@ NexusInterface::NXMRequestInfo::NXMRequestInfo(UpdatePeriod period , m_NexusGameID(game->nexusGameID()) , m_GameName(game->gameNexusName()) , m_Endorse(false) + , m_Track(false) + , m_Hash(QByteArray()) +{} + + +NexusInterface::NXMRequestInfo::NXMRequestInfo(QByteArray &hash + , 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(UpdatePeriod::NONE) + , m_UserData(userData) + , m_Timeout(nullptr) + , m_Reroute(false) + , m_ID(s_NextID.fetchAndAddAcquire(1)) + , m_URL(get_management_url()) + , m_SubModule(subModule) + , m_NexusGameID(game->nexusGameID()) + , m_GameName(game->gameNexusName()) + , m_Endorse(false) + , m_Track(false) + , m_Hash(hash) {} diff --git a/src/nexusinterface.h b/src/nexusinterface.h index a0f94562..6f3c9cd1 100644 --- a/src/nexusinterface.h +++ b/src/nexusinterface.h @@ -369,6 +369,20 @@ public: int requestToggleTracking(QString gameName, int modID, bool track, QObject *receiver, QVariant userData, const QString &subModule, MOBase::IPluginGame const *game); + /** + * + */ + int requestInfoFromMd5(QString gameName, QByteArray &hash, QObject *receiver, QVariant userData, const QString &subModule) + { + return requestInfoFromMd5(gameName, hash, receiver, userData, subModule, getGame(gameName)); + } + + /** + * + */ + int requestInfoFromMd5(QString gameName, QByteArray &hash, QObject *receiver, QVariant userData, const QString &subModule, + MOBase::IPluginGame const *game); + /** * @param directory the directory to store cache files **/ @@ -438,6 +452,7 @@ signals: 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); + void nxmFileInfoFromMd5Available(QString gameName, QVariant userData, QVariant resultData, int requestID); void nxmDownloadURLsAvailable(QString gameName, int modID, int fileID, QVariant userData, QVariant resultData, int requestID); void nxmEndorsementsAvailable(QVariant userData, QVariant resultData, int requestID); void nxmEndorsementToggled(QString gameName, int modID, QVariant userData, QVariant resultData, int requestID); @@ -480,6 +495,7 @@ private: TYPE_CHECKUPDATES, TYPE_TOGGLETRACKING, TYPE_TRACKEDMODS, + TYPE_FILEINFO_MD5, } m_Type; UpdatePeriod m_UpdatePeriod; QVariant m_UserData; @@ -492,12 +508,14 @@ private: int m_ID; int m_Endorse; int m_Track; + QByteArray m_Hash; 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(Type type, QVariant userData, const QString &subModule); NXMRequestInfo(UpdatePeriod period, Type type, QVariant userData, const QString &subModule, MOBase::IPluginGame const *game); + NXMRequestInfo(QByteArray &hash, Type type, QVariant userData, const QString &subModule, MOBase::IPluginGame const *game); private: static QAtomicInt s_NextID; -- cgit v1.3.1 From d541f982a55558f11040f7bfb6ad8fece36a58f2 Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Sun, 24 Mar 2019 17:41:58 -0500 Subject: Add another query to get the missing file description --- src/downloadmanager.cpp | 15 +++++++++++---- src/downloadmanager.h | 1 + 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'src/downloadmanager.h') diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index bc1de8fb..006d6994 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -1373,7 +1373,7 @@ void DownloadManager::setState(DownloadManager::DownloadInfo *info, DownloadMana m_RequestIDs.insert(m_NexusInterface->requestFiles(info->m_FileInfo->gameName, info->m_FileInfo->modID, this, info->m_DownloadID, QString())); } break; case STATE_FETCHINGMODINFO_MD5: { - qDebug(qUtf8Printable(QString("Searching %1 for Md5 of %2").arg(info->m_GamesToQuery[0]).arg(QString(info->m_Hash.toHex())))); + qDebug(qUtf8Printable(QString("Searching %1 for MD5 of %2").arg(info->m_GamesToQuery[0]).arg(QString(info->m_Hash.toHex())))); m_RequestIDs.insert(m_NexusInterface->requestInfoFromMd5(info->m_GamesToQuery[0], info->m_Hash, this, info->m_DownloadID, QString())); } break; case STATE_READY: { @@ -1561,8 +1561,9 @@ void DownloadManager::nxmFilesAvailable(QString, int, QVariant userData, QVarian QVariantMap fileInfo = file.toMap(); QString fileName = fileInfo["file_name"].toString(); QString fileNameVariant = fileName.mid(0).replace(' ', '_'); - if ((fileName == info->m_FileName) || (fileName == alternativeLocalName) || - (fileNameVariant == info->m_FileName) || (fileNameVariant == alternativeLocalName)) { + if ((fileName == info->m_RemoteFileName) || (fileNameVariant == info->m_RemoteFileName) || + (fileName == info->m_FileName) || (fileNameVariant == info->m_FileName) || + (fileName == alternativeLocalName) || (fileNameVariant == alternativeLocalName)) { info->m_FileInfo->name = fileInfo["name"].toString(); info->m_FileInfo->version.parse(fileInfo["version"].toString()); if (!info->m_FileInfo->version.isValid()) { @@ -1822,7 +1823,13 @@ void DownloadManager::nxmFileInfoFromMd5Available(QString gameName, QVariant use info->m_FileInfo->gameName = gameShortName; - setState(info, STATE_READY); + //If the file description is not present, send another query to get it + if (info->m_FileInfo->description.isEmpty()) { + info->m_RemoteFileName = fileDetails["file_name"].toString(); + setState(info, STATE_FETCHINGFILEINFO); + } else { + setState(info, STATE_READY); + } } diff --git a/src/downloadmanager.h b/src/downloadmanager.h index 8033989e..feef0eaa 100644 --- a/src/downloadmanager.h +++ b/src/downloadmanager.h @@ -89,6 +89,7 @@ private: QDateTime m_Created; // used as a cache in DownloadManager::getFileTime, may not be valid elsewhere QByteArray m_Hash; QStringList m_GamesToQuery; + QString m_RemoteFileName; int m_Tries; bool m_ReQueried; -- cgit v1.3.1