private async Task <bool> UpdateSingleItem(IGrouping <string, Tuple <UpdateInfo, UpdateItem> > task) { var firstItem = task.First().Item2; var itemSize = firstItem.GetDownloadSize(); var lastTimestamp = DateTime.UtcNow; var lastDownloadedKBytes = 0l; var progress = new Progress <double>(thisPercent => { var timeNow = DateTime.UtcNow; var secondsSinceLastUpdate = (timeNow - lastTimestamp).TotalSeconds; if (secondsSinceLastUpdate < 1 && thisPercent < 100) { return; } //This item: 70% done (1MB / 20MB) //Overall: 50% done (111MB / 1221MB) //Speed: 1234KB/s (average 1111KB/s) var downloadedKBytes = (long)(itemSize.GetRawSize() * (thisPercent / 100d)); var downloadedSize = FileSize.FromKilobytes(downloadedKBytes); var totalDownloadedSize = _completedSize + downloadedSize; var totalPercent = ((double)totalDownloadedSize.GetRawSize() / (double)_overallSize.GetRawSize()) * 100d; var speed = (downloadedKBytes - lastDownloadedKBytes) / secondsSinceLastUpdate; if (double.IsNaN(speed)) { speed = 0; } lastDownloadedKBytes = downloadedKBytes; lastTimestamp = timeNow; labelPercent.Text = $@"This item: {thisPercent:F1}% done ({downloadedSize} / {itemSize}) Overall: {totalPercent:F1}% done ({totalDownloadedSize} / {_overallSize}) Speed: {speed:F1}KB/s"; progressBar1.Value = Math.Min((int)(totalPercent * 10), progressBar1.Maximum); }); SetStatus($"Updating {firstItem.TargetPath.Name}"); SetStatus($"Updating {InstallDirectoryHelper.GetRelativePath(firstItem.TargetPath)}", false, true); var sourcesToAttempt = task.Where(x => !_badUpdateSources.Contains(x.Item1)).OrderBy(x => GetPing(x.Item1)).ToList(); if (sourcesToAttempt.Count == 0) { Console.WriteLine("There are no working sources to download from. Check the log for reasons why the sources failed."); _failedItems.Add(task); return(false); } Exception ex = null; foreach (var source in sourcesToAttempt) { try { // Needed because ZipUpdater doesn't support progress if (source.Item2.RemoteFile is ZipUpdater.ArchiveItem) { labelPercent.Text = $"Extracting... Overall progress: {_completedSize} / {_overallSize}."; } await RetryHelper.RetryOnExceptionAsync(() => source.Item2.Update(progress, _cancelToken.Token), 3, TimeSpan.FromSeconds(3), _cancelToken.Token); _completedSize += source.Item2.GetDownloadSize(); ex = null; break; } catch (OperationCanceledException) { throw; } catch (Exception e) { Console.WriteLine($"Marking source {source.Item1.Source.Origin} as broken because of exception: {e.ToStringDemystified()}"); ex = e; _badUpdateSources.Add(source.Item1); } } // Check if all sources failed if (ex != null) { Console.WriteLine("There are no working sources to download from. Check the log for reasons why the sources failed."); _failedItems.Add(task); _failedExceptions.Add(ex); return(false); } return(true); }