Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Download files from direct URLs via a command line parameter #1873

Merged
merged 11 commits into from
Sep 18, 2023

Conversation

eddoursul
Copy link
Contributor

@eddoursul eddoursul commented Sep 14, 2023

Adds the ability to use MO2's download manager to download files via direct URLs or temporary URLs from S3-compatible storages.

Usage:
ModOrganizer.exe "https://eddoursul.win/Cyberware%20TTW%20Patch.7z"
ModOrganizer.exe download "https://eddoursul.win/Cyberware%20TTW%20Patch.7z"

Copy link
Member

@Holt59 Holt59 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made some minor comments on the review but I don't like the way this is currently done. I think downloading file should be a distinct command, like modorganizer.exe download https://xxx. This is how nxm links should be handled (but are not for historical reasons).

@@ -187,6 +187,8 @@ std::optional<int> CommandLine::process(const std::wstring& line)
// not a shortcut, try a link
if (isNxmLink(qs)) {
m_nxmLink = qs;
} else if (qs.startsWith("https://", Qt::CaseInsensitive) || qs.startsWith("http://", Qt::CaseInsensitive)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think http:// download links should be allowed.

Copy link

@vensko vensko Sep 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is about what user supplies to MO2. Links posted on the internet may begin with http: (this happens), when in fact, the link would be immediately redirected to https. If you want user to see a warning message, manually fix the link and run again, sure, I can add that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand why you would do that but that's a very bad idea, especially knowing what's possible to do with mods. If people still post http:// links on their website, then that's the website faults, MO2 should not have to deal with that, we're in 2023.

I would simply not handle the link. Users are unlikely to run this command manually, so the warning would only be seen by the external tools that would dispatch link, in which case the check could be made there and MO2 would just have to return an error code.

@@ -440,7 +455,10 @@ bool DownloadManager::addDownload(const QStringList &URLs, QString gameName,
h2Conf.setStreamReceiveWindowSize(16777215);
QNetworkRequest request(preferredUrl);
request.setHeader(QNetworkRequest::UserAgentHeader, m_NexusInterface->getAccessManager()->userAgent());
request.setAttribute(QNetworkRequest::CacheSaveControlAttribute, false);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason for these two added attributes?

Copy link

@vensko vensko Sep 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same reason they are used in Nexus downloads https://github.com/ModOrganizer2/modorganizer/blob/qt6/src/nexusinterface.cpp#L821 . Spent half a day pulling my hair out on this.

By default, MO2 caches repeating requests, especially with small files. When this happens, the finished() signal is sent immediately, causing downloadFinished to fire before downloading a single byte. Then the check for size==0 kicks in here https://github.com/ModOrganizer2/modorganizer/blob/qt6/src/downloadmanager.cpp#L2100 , and the download gets marked as failed without showing a single message, because messages don't cover this case.

These two lines disable cache for the request.

@@ -580,7 +598,10 @@ void DownloadManager::startDownload(QNetworkReply *reply, DownloadInfo *newDownl
newDownload->m_State != STATE_READY &&
newDownload->m_State != STATE_FETCHINGMODINFO &&
reply->isFinished()) {
downloadFinished(indexByInfo(newDownload));
int index = indexByInfo(newDownload);
if (index >= 0) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The needs for this should be investigated, although probably not relevant for this PR, but maybe add a TODO comment.

Copy link

@vensko vensko Sep 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the fix to downloadFinished (below) is accepted, this may be redundant (I need to re-check). If it's not accepted, downloadFinished may fire with invalid -1 value, causing the no download index warning.

@@ -916,7 +937,7 @@ void DownloadManager::resumeDownloadInt(int index)

// Check for finished download;
if (info->m_TotalSize <= info->m_Output.size() && info->m_Reply != nullptr
&& info->m_Reply->isOpen() && info->m_Reply->isFinished() && info->m_State != STATE_ERROR) {
&& info->m_Reply->isFinished() && info->m_State != STATE_ERROR) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason for this change?

Copy link

@vensko vensko Sep 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, this is actually a leftover from investigating the cache issue, I used resumeDownloadInt
as a crutch. When MO2 is closed and file exists, resumeDownloadInt would skip this block, because connection is not open at that point, and append the same contents to the file again, doubling its size.
Since resumeDownloadInt is irrelevant to this PR, I have reverted this change.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correction - this is unrelated to the cache issue, happens with disabled cache as well, I brought the change back.

@@ -2076,10 +2097,14 @@ void DownloadManager::nxmRequestFailed(QString gameName, int modID, int fileID,
void DownloadManager::downloadFinished(int index)
{
DownloadInfo *info;
if (index)
if (index > 0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason for this change (and the one below)?

Copy link

@vensko vensko Sep 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

downloadFinished is used in two ways:

  • as a function, accepting an integer argument,
  • as a signal callback.

Currently, downloadFinished has a bug, when it does not run for the first download in the list (index 0), because of the logical mistake here 0==false https://github.com/ModOrganizer2/modorganizer/blob/qt6/src/downloadmanager.cpp#L2079

When it runs with downloadFinished(0), if (index) skips execution and jumps to detecting the signal sender, which yields nothing, because it is not a signal call. As a result, the first file may never see the finishing routine, causing cascade of errors and discrepancy in interface and actual status. In case of the proposed command, this looks like a duplicating paused file, when user tries to redownload the first file.

The first idea was to add if (index >= 0), but then the signal callback info = findDownload(this->sender(), &index) never fires, because during the callback index is also 0.

So I implemented detection of the first row as a fallback after all paths are exhausted.

I expected opposition on this fix and added the additional check in startDownload, catching possible downloadFinished(-1) call, which is an invalid value, causing the no download index warning. If the fix is accepted, the startDownload change probably becomes redundant.

@@ -405,6 +405,9 @@ void MOApplication::externalMessage(const QString& message)
} else if (isNxmLink(message)) {
MessageDialog::showMessage(tr("Download started"), qApp->activeWindow(), false);
m_core->downloadRequestedNXM(message);
} else if (message.startsWith("https://", Qt::CaseInsensitive) || message.startsWith("http://", Qt::CaseInsensitive)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above, I don't think http:// links should be accepted.

@Al12rs
Copy link
Member

Al12rs commented Sep 15, 2023

I agree with Holt, this should be behind a dedicated download command.

@Holt59 Holt59 changed the base branch from qt6 to master September 15, 2023 11:47
@Holt59
Copy link
Member

Holt59 commented Sep 15, 2023

I've changed the target branch to master since qt6 should no longer be used but apparently @Silarn made commits to qt6 that are not in master so this should be fixed before this can be merged (and this will probably need to be rebased).

@eddoursul eddoursul requested a review from Holt59 September 15, 2023 12:57
# Conflicts:
#	src/aboutdialog.cpp
#	src/aboutdialog.h
#	src/commandline.cpp
#	src/commandline.h
#	src/directoryrefresher.cpp
#	src/downloadmanager.cpp
#	src/organizercore.cpp
@eddoursul eddoursul requested a review from Holt59 September 18, 2023 08:58
@Holt59 Holt59 merged commit 59b5b1c into ModOrganizer2:master Sep 18, 2023
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants