private async Task DownloadPosts(IList <Post> posts, string output_base) { long sequence_start = Int64.Parse(StartSequence.Text); bool skip_existing = OverwriteExisting.IsChecked == false; long already_received = 0; long total_received = 0; long total_size = posts.Sum(p => p.FileSize); void DownloadProgressHandler(object sender, DownloadProgressChangedEventArgs args) { // ReSharper disable once AccessToModifiedClosure total_received = already_received + args.BytesReceived; UpdateStats(total_received, total_size); } Dictionary <string, long> post_counts = null; if (RadioTaggedFilename.IsChecked == true) { // If required, download tag counts post_counts = new Dictionary <string, long>(); foreach (Post post in posts) { // Only check these 2 types if (post.HasCharacterTags) { foreach (string tag in post.CharacterTags) { post_counts[tag] = 0; } } if (post.HasCopyrightTags) { foreach (string tag in post.CopyrightTags) { post_counts[tag] = 0; } } } List <string> keys = post_counts.Keys.ToList(); _NewMaximum(keys.Count); for (int i = 0; i < keys.Count; ++i) { if (CancelQueued) { ResetView("Download canceled"); return; } _SetStatus($"Getting post count for {keys[i]}"); _Progress(i + 1); post_counts[keys[i]] = await CurrentBoard.PostCount(ImageTag.ParseTag(keys[i])); UpdateCounter(); } } // Pre-compute all file names Dictionary <long, string> file_names = new Dictionary <long, string>(); for (int i = 0; i < posts.Count; ++i) { Post post = posts[i]; string output_file = String.Empty; string file_ext = String.IsNullOrWhiteSpace(post.FileExt) ? Path.GetExtension(post.FileUrl)?.Substring(1) : post.FileExt; if (RadioID.IsChecked == true) { output_file = $"{post.ID}.{file_ext}"; } else if (RadioMD5.IsChecked == true) { output_file = $"{post.MD5}.{file_ext}"; } else if (RadioSequence.IsChecked == true) { output_file = $"{sequence_start + i}.{file_ext}"; } else if (RadioTaggedFilename.IsChecked == true) { output_file = $"{post.TaggedFileString(post_counts)}.{file_ext}"; } if (output_file.Length == 0) { WebHelper.Client.DownloadProgressChanged -= DownloadProgressHandler; WebHelper.Client.DownloadProgressChanged += WebClientDownloadProgress; throw new InvalidInputException($"Output path could not be formatted for {post.ID} from {post.Board}"); } // Okay resharper is just showing off output_file = Path.GetInvalidFileNameChars().Aggregate(output_file, (current, c) => current.Replace(c, '_')); file_names[post.ID] = output_file; } WebHelper.Client.DownloadProgressChanged -= WebClientDownloadProgress; WebHelper.Client.DownloadProgressChanged += DownloadProgressHandler; _NewMaximum(posts.Count); for (int i = 0; i < posts.Count; i++) { if (CancelQueued) { ResetView("Download canceled"); break; } already_received = total_received; Post post = posts[i]; // Enable the use of long paths string output_path = $"\\\\?\\{output_base}{Path.DirectorySeparatorChar}{file_names[post.ID]}"; if (skip_existing && File.Exists(output_path)) { continue; } _SetStatus($"Downloading {file_names[post.ID]} ({post.ID})"); _Progress(i + 1); try { await WebHelper.Client.DownloadFileTaskAsync(post.FileUrl, output_path); } catch (WebException e) { MessageBox.Show( $"Download for {post.ID} failed with error: {e.Message}.\nSource URL: \"{post.FileUrl}\"\nTarget file was: \"{output_path}\".\n Attempting to continue."); } UpdateCounter(); } WebHelper.Client.DownloadProgressChanged -= DownloadProgressHandler; WebHelper.Client.DownloadProgressChanged += WebClientDownloadProgress; }