diff --git a/.gitignore b/.gitignore
index fe1924a80..3e7b8a28d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,6 +33,9 @@ bld/
# Don't ignore the root bin folder
!/bin/
+.config/
+.mypy_cache/
+
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c1082feb9..e848adb0b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -27,6 +27,7 @@ All notable changes to this project will be documented in this file.
- [Multiple] Properly clear AD upgrades from changeset (#4037 by: HebaruSan)
- [Multiple] De-over-parallelize Versions tab (#4049 by: HebaruSan)
- [GUI] Use better console hiding API (#4051 by: HebaruSan)
+- [Core] Trigger progress updates for frozen downloads (#4052 by: HebaruSan)
### Internal
diff --git a/Core/Extensions/IOExtensions.cs b/Core/Extensions/IOExtensions.cs
index 0e6e04f16..5f2ec9f6e 100644
--- a/Core/Extensions/IOExtensions.cs
+++ b/Core/Extensions/IOExtensions.cs
@@ -3,6 +3,7 @@
using System.Linq;
using System.Collections.Generic;
using System.Threading;
+using Timer = System.Timers.Timer;
namespace CKAN.Extensions
{
@@ -64,15 +65,37 @@ public static DriveInfo GetDrive(this DirectoryInfo dir)
/// Stream from which to copy
/// Stream to which to copy
/// Callback to notify as we traverse the input, called with count of bytes received
- public static void CopyTo(this Stream src, Stream dest, IProgress progress, CancellationToken cancelToken = default)
+ /// Maximum timespand to elapse between progress updates, will synthesize extra updates as needed
+ public static void CopyTo(this Stream src,
+ Stream dest,
+ IProgress progress,
+ TimeSpan? idleInterval = null,
+ CancellationToken cancelToken = default)
{
// CopyTo says its default buffer is 81920, but we want more than 1 update for a 100 KiB file
const int bufSize = 16384;
var buffer = new byte[bufSize];
long total = 0;
+ var lastProgressTime = DateTime.Now;
+ // Sometimes a server just freezes and times out, send extra updates if requested
+ Timer timer = null;
+ if (idleInterval.HasValue)
+ {
+ timer = new Timer(idleInterval.Value > minProgressInterval
+ ? idleInterval.Value.TotalMilliseconds
+ : minProgressInterval.TotalMilliseconds)
+ {
+ AutoReset = true,
+ };
+ timer.Elapsed += (sender, args) =>
+ {
+ progress.Report(total);
+ lastProgressTime = DateTime.Now;
+ };
+ timer.Start();
+ }
// Make sure we get an initial progress notification at the start
progress.Report(total);
- var lastProgressTime = DateTime.Now;
while (true)
{
var bytesRead = src.Read(buffer, 0, bufSize);
@@ -84,8 +107,10 @@ public static void CopyTo(this Stream src, Stream dest, IProgress progress
total += bytesRead;
cancelToken.ThrowIfCancellationRequested();
var now = DateTime.Now;
- if (now - lastProgressTime >= progressInterval)
+ if (now - lastProgressTime >= minProgressInterval)
{
+ timer?.Stop();
+ timer?.Start();
progress.Report(total);
lastProgressTime = now;
}
@@ -103,6 +128,6 @@ public static IEnumerable BytesFromStream(this Stream s)
}
}
- private static readonly TimeSpan progressInterval = TimeSpan.FromMilliseconds(200);
+ private static readonly TimeSpan minProgressInterval = TimeSpan.FromMilliseconds(200);
}
}
diff --git a/Core/Net/ResumingWebClient.cs b/Core/Net/ResumingWebClient.cs
index f851e86d0..10ddec921 100644
--- a/Core/Net/ResumingWebClient.cs
+++ b/Core/Net/ResumingWebClient.cs
@@ -139,6 +139,7 @@ protected override void OnOpenReadCompleted(OpenReadCompletedEventArgs e)
DownloadProgress?.Invoke((int)(100 * bytesDownloaded / contentLength),
bytesDownloaded, contentLength);
}),
+ TimeSpan.FromSeconds(5),
cancelTokenSrc.Token);
// Make sure caller knows we've finished
DownloadProgress?.Invoke(100, contentLength, contentLength);