Allow more flexible log reporting using Progress

This commit is contained in:
Matt Nadareski
2026-10-21 22:18:50 -04:00
parent acf1a832d6
commit 08166b7d3c
7 changed files with 494 additions and 369 deletions

View File

@@ -52,7 +52,7 @@ namespace RedumpTool.Features
public BaseFeature(string name, string[] flags, string description, string? detailed = null)
: base(name, flags, description, detailed)
{
_client = new Client();
_client = new Client(ReportProgress);
}
#region Helpers
@@ -107,6 +107,11 @@ namespace RedumpTool.Features
}
}
/// <summary>
/// Progress reporting callback
/// </summary>
protected static void ReportProgress(object? sender, WebClientEventArgs value) => Console.WriteLine(value);
#endregion
}
}

View File

@@ -0,0 +1,108 @@
#if NET20 || NET35 || NET40
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Threading;
using System.Diagnostics;
namespace SabreTools.RedumpLib
{
/// <summary>
/// Provides an IProgress{T} that invokes callbacks for each reported progress value.
/// </summary>
/// <typeparam name="T">Specifies the type of the progress report value.</typeparam>
/// <remarks>
/// Any handler provided to the constructor or event handlers registered with
/// the <see cref="ProgressChanged"/> event are invoked through a
/// <see cref="SynchronizationContext"/> instance captured
/// when the instance is constructed. If there is no current SynchronizationContext
/// at the time of construction, the callbacks will be invoked on the ThreadPool.
/// </remarks>
/// <see href="https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Progress.cs"/>
internal class Progress<T> : IProgress<T> where T : EventArgs
{
/// <summary>The synchronization context captured upon construction. This will never be null.</summary>
private readonly SynchronizationContext? _synchronizationContext;
/// <summary>The handler specified to the constructor. This may be null.</summary>
private readonly Action<T>? _handler;
/// <summary>A cached delegate used to post invocation to the synchronization context.</summary>
private readonly SendOrPostCallback _invokeHandlers;
/// <summary>Initializes the <see cref="Progress{T}"/>.</summary>
public Progress()
{
// Capture the current synchronization context.
// If there is no current context, we use a default instance targeting the ThreadPool.
_synchronizationContext = SynchronizationContext.Current ?? ProgressStatics.DefaultContext;
Debug.Assert(_synchronizationContext is not null);
_invokeHandlers = new SendOrPostCallback(InvokeHandlers);
}
/// <summary>Initializes the <see cref="Progress{T}"/> with the specified callback.</summary>
/// <param name="handler">
/// A handler to invoke for each reported progress value. This handler will be invoked
/// in addition to any delegates registered with the <see cref="ProgressChanged"/> event.
/// Depending on the <see cref="SynchronizationContext"/> instance captured by
/// the <see cref="Progress{T}"/> at construction, it's possible that this handler instance
/// could be invoked concurrently with itself.
/// </param>
/// <exception cref="ArgumentNullException">The <paramref name="handler"/> is null (<see langword="Nothing" /> in Visual Basic).</exception>
public Progress(Action<T> handler) : this()
{
_handler = handler ?? throw new ArgumentNullException(nameof(handler));
}
/// <summary>Raised for each reported progress value.</summary>
/// <remarks>
/// Handlers registered with this event will be invoked on the
/// <see cref="SynchronizationContext"/> captured when the instance was constructed.
/// </remarks>
public event EventHandler<T>? ProgressChanged;
/// <summary>Reports a progress change.</summary>
/// <param name="value">The value of the updated progress.</param>
protected virtual void OnReport(T value)
{
// If there's no handler, don't bother going through the sync context.
// Inside the callback, we'll need to check again, in case
// an event handler is removed between now and then.
Action<T>? handler = _handler;
EventHandler<T>? changedEvent = ProgressChanged;
if (handler is not null || changedEvent is not null)
{
// Post the processing to the sync context.
// (If T is a value type, it will get boxed here.)
_synchronizationContext?.Post(_invokeHandlers, value);
}
}
/// <summary>Reports a progress change.</summary>
/// <param name="value">The value of the updated progress.</param>
void IProgress<T>.Report(T value) { OnReport(value); }
/// <summary>Invokes the action and event callbacks.</summary>
/// <param name="state">The progress value.</param>
private void InvokeHandlers(object? state)
{
T value = (T)state!;
Action<T>? handler = _handler;
EventHandler<T>? changedEvent = ProgressChanged;
handler?.Invoke(value);
changedEvent?.Invoke(this, value);
}
}
/// <summary>Holds static values for <see cref="Progress{T}"/>.</summary>
/// <remarks>This avoids one static instance per type T.</remarks>
internal static class ProgressStatics
{
/// <summary>A default synchronization context that targets the ThreadPool.</summary>
internal static readonly SynchronizationContext DefaultContext = new();
}
}
#endif

View File

@@ -73,6 +73,11 @@ namespace SabreTools.RedumpLib.Web
private readonly CookieWebClient _internalClient;
#endif
/// <summary>
/// Optional progress callback for logging
/// </summary>
private readonly IProgress<WebClientEventArgs> _progress;
/// <summary>
/// Cookie container for persistence
/// </summary>
@@ -108,7 +113,8 @@ namespace SabreTools.RedumpLib.Web
/// <summary>
/// Constructor
/// </summary>
public Client()
/// <param name="progressHandler">Optional progress callback for logging</param>
public Client(EventHandler<WebClientEventArgs>? progressHandler = null)
{
_cookieContainer = new CookieContainer();
#if NETCOREAPP
@@ -121,7 +127,12 @@ namespace SabreTools.RedumpLib.Web
#else
_internalClient = new CookieWebClient(_cookieContainer);
#endif
Timeout = TimeSpan.FromSeconds(30);
_progress = new Progress<WebClientEventArgs>();
if (progressHandler is not null)
((Progress<WebClientEventArgs>)_progress).ProgressChanged += progressHandler;
}
#region Cookies
@@ -234,15 +245,18 @@ namespace SabreTools.RedumpLib.Web
/// </summary>
/// <param name="username">redump.info username</param>
/// <param name="password">redump.info password</param>
/// <param name="progressHandler">Optional progress callback for logging</param>
/// <returns>True if the user could be logged in, false otherwise, null on error</returns>
public static async Task<bool?> ValidateCredentials(string? username, string? password)
public static async Task<bool?> ValidateCredentials(string? username,
string? password,
EventHandler<WebClientEventArgs>? progressHandler = null)
{
// If options are invalid or we're missing something key, just return
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
return false;
// Try logging in with the supplied credentials otherwise
var redumpClient = new Client();
var redumpClient = new Client(progressHandler);
return await redumpClient.Login(username, password);
}
@@ -257,23 +271,23 @@ namespace SabreTools.RedumpLib.Web
// Check for already logged in
if (_loggedIn)
{
Console.WriteLine("Already logged in!");
_progress.Report("Already logged in!");
return true;
}
// Credentials verification
if (!string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password))
{
Console.WriteLine("Credentials entered, will attempt redump.info login...");
_progress.Report("Credentials entered, will attempt redump.info login...");
}
else if (!string.IsNullOrEmpty(username) && string.IsNullOrEmpty(password))
{
Console.Error.WriteLine("Only a username was specified, will not attempt redump.info login...");
_progress.Report("ERROR: Only a username was specified, will not attempt redump.info login...");
return false;
}
else if (string.IsNullOrEmpty(username))
{
Console.WriteLine("No credentials entered, will not attempt redump.info login...");
_progress.Report("No credentials entered, will not attempt redump.info login...");
return false;
}
@@ -289,7 +303,7 @@ namespace SabreTools.RedumpLib.Web
{
try
{
Console.WriteLine($"Login attempt {i + 1} of {AttemptCount}");
_progress.Report($"Login attempt {i + 1} of {AttemptCount}");
// Get the current information from the login page
var loginPage = await DownloadString(LoginUrl);
@@ -345,7 +359,7 @@ namespace SabreTools.RedumpLib.Web
// An empty response indicates an error
if (string.IsNullOrEmpty(responseContent))
{
Console.Error.WriteLine($"An error occurred while trying to log in on attempt {i + 1}: No response");
_progress.Report($"ERROR: An error occurred while trying to log in on attempt {i + 1}: No response");
DelayHelper.DelayRandom();
continue;
}
@@ -353,7 +367,7 @@ namespace SabreTools.RedumpLib.Web
// A malformed form indicicates a retry is needed
if (responseContent.Contains("The submitted form was invalid. Try submitting again."))
{
Console.Error.WriteLine($"An error occurred while trying to log in on attempt {i + 1}: Invalid result");
_progress.Report($"ERROR: An error occurred while trying to log in on attempt {i + 1}: Invalid result");
DelayHelper.DelayRandom();
continue;
}
@@ -361,12 +375,12 @@ namespace SabreTools.RedumpLib.Web
// "Your posts" indicates login was successful
if (!responseContent.Contains("./search.php?search_id=egosearch"))
{
Console.Error.WriteLine("Invalid credentials entered, continuing without logging in...");
_progress.Report("ERROR: Invalid credentials entered, continuing without logging in...");
return false;
}
// The user was able to be logged in
Console.WriteLine("Credentials accepted! Logged into Redump...");
_progress.Report("Credentials accepted! Logged into Redump...");
_loggedIn = true;
// If the user is a moderator or staff, set accordingly
@@ -377,11 +391,11 @@ namespace SabreTools.RedumpLib.Web
}
catch (Exception ex)
{
Console.Error.WriteLine($"An exception occurred while trying to log in on attempt {i + 1}: {ex}");
_progress.Report($"ERROR: An exception occurred while trying to log in on attempt {i + 1}: {ex}");
}
}
Console.Error.WriteLine($"Could not login to Redump in {AttemptCount} attempts, continuing without logging in...");
_progress.Report($"ERROR: Could not login to Redump in {AttemptCount} attempts, continuing without logging in...");
return false;
}
@@ -399,7 +413,7 @@ namespace SabreTools.RedumpLib.Web
// Only retry a positive number of times
if (AttemptCount <= 0)
{
Console.Error.WriteLine("Invalid number of attempts provided, must be at least 1");
_progress.Report("ERROR: Invalid number of attempts provided, must be at least 1");
return null;
}
@@ -407,7 +421,7 @@ namespace SabreTools.RedumpLib.Web
{
try
{
if (Debug) Console.WriteLine($"DEBUG: DownloadData(\"{uri}\"), Attempt {i + 1} of {AttemptCount}");
if (Debug) _progress.Report($"DEBUG: DownloadData(\"{uri}\"), Attempt {i + 1} of {AttemptCount}");
#if NETCOREAPP
return await _internalClient.GetByteArrayAsync(uri);
#elif NET40
@@ -422,7 +436,7 @@ namespace SabreTools.RedumpLib.Web
DelayHelper.DelayRandom();
}
Console.Error.WriteLine($"Could not download \"{uri}\" after {AttemptCount} attempts");
_progress.Report($"ERROR: Could not download \"{uri}\" after {AttemptCount} attempts");
return null;
}
@@ -437,7 +451,7 @@ namespace SabreTools.RedumpLib.Web
// Only retry a positive number of times
if (AttemptCount <= 0)
{
Console.Error.WriteLine("Invalid number of attempts provided, must be at least 1");
_progress.Report("ERROR: Invalid number of attempts provided, must be at least 1");
return null;
}
@@ -445,13 +459,13 @@ namespace SabreTools.RedumpLib.Web
{
try
{
if (Debug) Console.WriteLine($"DEBUG: DownloadFile(\"{uri}\", \"{fileName}\"), Attempt {i + 1} of {AttemptCount}");
if (Debug) _progress.Report($"DEBUG: DownloadFile(\"{uri}\", \"{fileName}\"), Attempt {i + 1} of {AttemptCount}");
#if NETCOREAPP
// Make the call to get the file
var response = await _internalClient.GetAsync(uri);
if (response?.Content?.Headers is null || !response.IsSuccessStatusCode)
{
if (Debug) Console.Error.WriteLine($"DEBUG: DownloadFile failed, continuing...");
if (Debug) _progress.Report($"DEBUG: DownloadFile failed, continuing...");
continue;
}
@@ -475,7 +489,7 @@ namespace SabreTools.RedumpLib.Web
string? lastFilename = _internalClient.GetLastFilename();
if (lastFilename is null)
{
if (Debug) Console.Error.WriteLine($"DEBUG: DownloadFile failed, continuing...");
if (Debug) _progress.Report($"DEBUG: DownloadFile failed, continuing...");
continue;
}
@@ -485,7 +499,7 @@ namespace SabreTools.RedumpLib.Web
string? lastFilename = _internalClient.GetLastFilename();
if (lastFilename is null)
{
if (Debug) Console.Error.WriteLine($"DEBUG: DownloadFile failed, continuing...");
if (Debug) _progress.Report($"DEBUG: DownloadFile failed, continuing...");
continue;
}
@@ -498,7 +512,7 @@ namespace SabreTools.RedumpLib.Web
DelayHelper.DelayRandom();
}
Console.Error.WriteLine($"Could not download \"{uri}\" after {AttemptCount} attempts");
_progress.Report($"ERROR: Could not download \"{uri}\" after {AttemptCount} attempts");
return null;
}
@@ -512,7 +526,7 @@ namespace SabreTools.RedumpLib.Web
// Only retry a positive number of times
if (AttemptCount <= 0)
{
Console.Error.WriteLine("Invalid number of attempts provided, must be at least 1");
_progress.Report("ERROR: Invalid number of attempts provided, must be at least 1");
return null;
}
@@ -520,7 +534,7 @@ namespace SabreTools.RedumpLib.Web
{
try
{
if (Debug) Console.WriteLine($"DEBUG: DownloadString(\"{uri}\"), Attempt {i + 1} of {AttemptCount}");
if (Debug) _progress.Report($"DEBUG: DownloadString(\"{uri}\"), Attempt {i + 1} of {AttemptCount}");
#if NETCOREAPP
return await _internalClient.GetStringAsync(uri);
#elif NET40
@@ -535,7 +549,7 @@ namespace SabreTools.RedumpLib.Web
DelayHelper.DelayRandom();
}
Console.Error.WriteLine($"Could not download \"{uri}\" after {AttemptCount} attempts");
_progress.Report($"ERROR: Could not download \"{uri}\" after {AttemptCount} attempts");
return null;
}
@@ -549,7 +563,7 @@ namespace SabreTools.RedumpLib.Web
// Only retry a positive number of times
if (AttemptCount <= 0)
{
Console.Error.WriteLine("Invalid number of attempts provided, must be at least 1");
_progress.Report("ERROR: Invalid number of attempts provided, must be at least 1");
return null;
}
@@ -557,13 +571,13 @@ namespace SabreTools.RedumpLib.Web
{
try
{
if (Debug) Console.WriteLine($"DEBUG: GetResolvedURL(\"{uri}\"), Attempt {i + 1} of {AttemptCount}");
if (Debug) _progress.Report($"DEBUG: GetResolvedURL(\"{uri}\"), Attempt {i + 1} of {AttemptCount}");
#if NETCOREAPP
// Make the call to get the file
var response = await _internalClient.GetAsync(uri);
if (response?.Content?.Headers is null || !response.IsSuccessStatusCode)
{
if (Debug) Console.Error.WriteLine($"DEBUG: GetResolvedURL failed, continuing...");
if (Debug) _progress.Report($"DEBUG: GetResolvedURL failed, continuing...");
continue;
}
@@ -573,7 +587,7 @@ namespace SabreTools.RedumpLib.Web
string? lastUrl = _internalClient.LastUrl?.AbsoluteUri;
if (lastUrl is null)
{
if (Debug) Console.Error.WriteLine($"DEBUG: GetResolvedURL failed, continuing...");
if (Debug) _progress.Report($"DEBUG: GetResolvedURL failed, continuing...");
continue;
}
@@ -583,7 +597,7 @@ namespace SabreTools.RedumpLib.Web
string? lastUrl = _internalClient.LastUrl?.AbsoluteUri;
if (lastUrl is null)
{
if (Debug) Console.Error.WriteLine($"DEBUG: GetResolvedURL failed, continuing...");
if (Debug) _progress.Report($"DEBUG: GetResolvedURL failed, continuing...");
continue;
}
@@ -596,7 +610,7 @@ namespace SabreTools.RedumpLib.Web
DelayHelper.DelayRandom();
}
Console.Error.WriteLine($"Could not download \"{uri}\" after {AttemptCount} attempts");
_progress.Report($"ERROR: Could not download \"{uri}\" after {AttemptCount} attempts");
return null;
}
@@ -621,21 +635,21 @@ namespace SabreTools.RedumpLib.Web
// If the web client failed, return null
if (dumpsPage is null)
{
if (Debug) Console.WriteLine($"DEBUG: CheckSingleDiscsPage(\"{url}\") - Client failure");
if (Debug) _progress.Report($"DEBUG: CheckSingleDiscsPage(\"{url}\") - Client failure");
return null;
}
// If we have no dumps left
if (dumpsPage.Contains("No discs found."))
{
if (Debug) Console.WriteLine($"DEBUG: CheckSingleDiscsPage(\"{url}\") - No discs found");
if (Debug) _progress.Report($"DEBUG: CheckSingleDiscsPage(\"{url}\") - No discs found");
return ids;
}
// If we have a single disc page already
if (dumpsPage.Contains("<h3>Disc</h3>"))
{
if (Debug) Console.WriteLine($"DEBUG: CheckSingleDiscsPage(\"{url}\") - Single disc page");
if (Debug) _progress.Report($"DEBUG: CheckSingleDiscsPage(\"{url}\") - Single disc page");
string? lastUrl = await GetResolvedURL(url);
if (lastUrl is not null)
{
@@ -661,7 +675,7 @@ namespace SabreTools.RedumpLib.Web
}
catch (Exception ex)
{
Console.Error.WriteLine($"An exception has occurred: {ex}");
_progress.Report($"ERROR: An exception has occurred: {ex}");
continue;
}
}
@@ -685,7 +699,7 @@ namespace SabreTools.RedumpLib.Web
List<int>? ids = await CheckSingleDiscsEndpoint(endpoint);
if (ids is null)
{
if (Debug) Console.WriteLine($"DEBUG: CheckSingleDiscsPage(\"{outDir}\") - Client failure");
if (Debug) _progress.Report($"DEBUG: CheckSingleDiscsPage(\"{outDir}\") - Client failure");
return null;
}
@@ -703,7 +717,7 @@ namespace SabreTools.RedumpLib.Web
}
catch (Exception ex)
{
Console.Error.WriteLine($"An exception has occurred: {ex}");
_progress.Report($"ERROR: An exception has occurred: {ex}");
continue;
}
}
@@ -727,14 +741,14 @@ namespace SabreTools.RedumpLib.Web
// If the web client failed, return null
if (dumpsPage is null)
{
if (Debug) Console.WriteLine($"DEBUG: CheckSingleQueuePage() - Client failure");
if (Debug) _progress.Report($"DEBUG: CheckSingleQueuePage() - Client failure");
return null;
}
// If we have no dumps left
if (dumpsPage.Contains("No discs found."))
{
if (Debug) Console.WriteLine($"DEBUG: CheckSingleQueuePage() - No discs found");
if (Debug) _progress.Report($"DEBUG: CheckSingleQueuePage() - No discs found");
return ids;
}
@@ -752,7 +766,7 @@ namespace SabreTools.RedumpLib.Web
}
catch (Exception ex)
{
Console.WriteLine($"An exception has occurred: {ex}");
_progress.Report($"An exception has occurred: {ex}");
continue;
}
}
@@ -774,7 +788,7 @@ namespace SabreTools.RedumpLib.Web
List<int>? ids = await CheckSingleQueueEndpoint(endpoint);
if (ids is null)
{
if (Debug) Console.WriteLine($"DEBUG: CheckSingleQueuePage(\"{outDir}\") - Client failure");
if (Debug) _progress.Report($"DEBUG: CheckSingleQueuePage(\"{outDir}\") - Client failure");
return null;
}
@@ -792,7 +806,7 @@ namespace SabreTools.RedumpLib.Web
}
catch (Exception ex)
{
Console.Error.WriteLine($"An exception has occurred: {ex}");
_progress.Report($"ERROR: An exception has occurred: {ex}");
continue;
}
}
@@ -813,12 +827,12 @@ namespace SabreTools.RedumpLib.Web
{
try
{
if (Debug) Console.WriteLine($"DEBUG: DownloadDatabase(\"{outDir}\", {subfolder})");
if (Debug) _progress.Report($"DEBUG: DownloadDatabase(\"{outDir}\", {subfolder})");
// If the user is not logged in, this is invalid
if (!_loggedIn)
{
Console.Error.WriteLine("Database download requires valid login credentials!");
_progress.Report("ERROR: Database download requires valid login credentials!");
return false;
}
@@ -829,7 +843,7 @@ namespace SabreTools.RedumpLib.Web
// If no output directory is defined, use the current directory instead
if (string.IsNullOrEmpty(outDir))
{
if (Debug) Console.WriteLine("DEBUG: Output directory was not provided, setting to current directory");
if (Debug) _progress.Report("DEBUG: Output directory was not provided, setting to current directory");
outDir = Environment.CurrentDirectory;
}
@@ -844,7 +858,7 @@ namespace SabreTools.RedumpLib.Web
}
catch (Exception ex)
{
Console.Error.WriteLine($"An exception has occurred: {ex}");
_progress.Report($"ERROR: An exception has occurred: {ex}");
return false;
}
}
@@ -859,19 +873,19 @@ namespace SabreTools.RedumpLib.Web
{
try
{
if (Debug) Console.WriteLine($"DEBUG: DownloadSinglePack(\"{packType}\", {system})");
if (Debug) _progress.Report($"DEBUG: DownloadSinglePack(\"{packType}\", {system})");
// If the system is invalid, we can't do anything
if (system is null || !system.Available || string.IsNullOrEmpty(system.Code))
{
if (Debug) Console.WriteLine($"DEBUG: {system} is not marked as available on Redump, skipping...");
if (Debug) _progress.Report($"DEBUG: {system} is not marked as available on Redump, skipping...");
return null;
}
// If the pack is not supported for the system
if (!PackTypeToAvailable(packType, system))
{
if (Debug) Console.WriteLine($"DEBUG: {packType} is not available for {system}, skipping...");
if (Debug) _progress.Report($"DEBUG: {packType} is not available for {system}, skipping...");
return null;
}
@@ -887,7 +901,7 @@ namespace SabreTools.RedumpLib.Web
}
catch (Exception ex)
{
Console.Error.WriteLine($"An exception has occurred: {ex}");
_progress.Report($"ERROR: An exception has occurred: {ex}");
return null;
}
}
@@ -906,19 +920,19 @@ namespace SabreTools.RedumpLib.Web
{
try
{
if (Debug) Console.WriteLine($"DEBUG: DownloadSinglePack(\"{packType}\", {system}, \"{outDir}\")");
if (Debug) _progress.Report($"DEBUG: DownloadSinglePack(\"{packType}\", {system}, \"{outDir}\")");
// If the system is invalid, we can't do anything
if (system is null || !system.Available || string.IsNullOrEmpty(system.Code))
{
if (Debug) Console.WriteLine($"DEBUG: {system} is not marked as available on Redump, skipping...");
if (Debug) _progress.Report($"DEBUG: {system} is not marked as available on Redump, skipping...");
return false;
}
// If the pack is not supported for the system
if (!PackTypeToAvailable(packType, system))
{
if (Debug) Console.WriteLine($"DEBUG: {packType} is not available for {system}, skipping...");
if (Debug) _progress.Report($"DEBUG: {packType} is not available for {system}, skipping...");
return false;
}
@@ -933,7 +947,7 @@ namespace SabreTools.RedumpLib.Web
// If no output directory is defined, use the current directory instead
if (string.IsNullOrEmpty(outDir))
{
if (Debug) Console.WriteLine("DEBUG: Output directory was not provided, setting to current directory");
if (Debug) _progress.Report("DEBUG: Output directory was not provided, setting to current directory");
outDir = Environment.CurrentDirectory;
}
@@ -948,7 +962,7 @@ namespace SabreTools.RedumpLib.Web
}
catch (Exception ex)
{
Console.Error.WriteLine($"An exception has occurred: {ex}");
_progress.Report($"ERROR: An exception has occurred: {ex}");
return false;
}
}
@@ -962,7 +976,7 @@ namespace SabreTools.RedumpLib.Web
public async Task<string?> DownloadSingleDiscPage(int id)
{
string paddedId = id.ToString().PadLeft(6, '0');
Console.WriteLine($"Processing ID: {paddedId}");
_progress.Report($"Processing ID: {paddedId}");
try
{
// Try to retrieve the data
@@ -972,21 +986,21 @@ namespace SabreTools.RedumpLib.Web
if (discPage is null)
{
Console.Error.WriteLine($"An error occurred retrieving ID {paddedId}!");
_progress.Report($"ERROR: An error occurred retrieving ID {paddedId}!");
return null;
}
else if (discPage.Contains($"Disc with ID \"{id}\" doesn't exist"))
{
Console.Error.WriteLine($"ID {paddedId} could not be found!");
_progress.Report($"ERROR: ID {paddedId} could not be found!");
return null;
}
Console.WriteLine($"ID {paddedId} has been successfully downloaded");
_progress.Report($"ID {paddedId} has been successfully downloaded");
return discPage;
}
catch (Exception ex)
{
Console.Error.WriteLine($"An exception has occurred: {ex}");
_progress.Report($"ERROR: An exception has occurred: {ex}");
return null;
}
}
@@ -1011,7 +1025,7 @@ namespace SabreTools.RedumpLib.Web
string paddedId = id.ToString().PadLeft(6, '0');
string paddedIdDir = Path.Combine(outDir, paddedId);
Console.WriteLine($"Processing ID: {paddedId}");
_progress.Report($"Processing ID: {paddedId}");
try
{
// Try to retrieve the data
@@ -1021,7 +1035,7 @@ namespace SabreTools.RedumpLib.Web
if (discPage is null)
{
Console.Error.WriteLine($"An error occurred retrieving ID {paddedId}!");
_progress.Report($"ERROR: An error occurred retrieving ID {paddedId}!");
return false;
}
else if (discPage.Contains($"Disc with ID \"{id}\" doesn't exist"))
@@ -1038,7 +1052,7 @@ namespace SabreTools.RedumpLib.Web
catch { }
}
Console.Error.WriteLine($"ID {paddedId} could not be found!");
_progress.Report($"ERROR: ID {paddedId} could not be found!");
return false;
}
@@ -1055,14 +1069,14 @@ namespace SabreTools.RedumpLib.Web
// If both pages contain the same modified date, skip it
if (oldResult.Success && newResult.Success && oldResult.Groups[1].Value == newResult.Groups[1].Value)
{
Console.WriteLine($"ID {paddedId} has not been changed since last download, skipping...");
_progress.Report($"ID {paddedId} has not been changed since last download, skipping...");
return false;
}
// If neither page contains a modified date, skip it
else if (!oldResult.Success && !newResult.Success)
{
Console.WriteLine($"ID {paddedId} has not been changed since last download, skipping...");
_progress.Report($"ID {paddedId} has not been changed since last download, skipping...");
return false;
}
}
@@ -1072,7 +1086,7 @@ namespace SabreTools.RedumpLib.Web
var hasModifiedDate = Constants.ModifiedRegex.Match(discPage);
if (!hasAddedDate.Success && !hasModifiedDate.Success)
{
Console.WriteLine($"ID {paddedId} retieved an empty page, skipping...");
_progress.Report($"ID {paddedId} retieved an empty page, skipping...");
return false;
}
@@ -1093,7 +1107,7 @@ namespace SabreTools.RedumpLib.Web
string? changesPage = await DownloadString(uri);
if (!IgnoreErrors && changesPage is null)
{
if (Debug) Console.Error.WriteLine($"DEBUG: Downloading changes page for {id} failed!");
if (Debug) _progress.Report($"DEBUG: Downloading changes page for {id} failed!");
return false;
}
@@ -1114,7 +1128,7 @@ namespace SabreTools.RedumpLib.Web
string? editPage = await DownloadString(uri);
if (!IgnoreErrors && editPage is null)
{
if (Debug) Console.Error.WriteLine($"DEBUG: Downloading edit page for {id} failed!");
if (Debug) _progress.Report($"DEBUG: Downloading edit page for {id} failed!");
return false;
}
@@ -1139,7 +1153,7 @@ namespace SabreTools.RedumpLib.Web
string? remoteName = await DownloadFile(uri, Path.Combine(paddedIdDir, $"{paddedId}.cue"));
if (!IgnoreErrors && remoteName is null)
{
if (Debug) Console.Error.WriteLine($"DEBUG: Downloading cuesheet for {id} failed!");
if (Debug) _progress.Report($"DEBUG: Downloading cuesheet for {id} failed!");
return false;
}
}
@@ -1156,7 +1170,7 @@ namespace SabreTools.RedumpLib.Web
string? remoteName = await DownloadFile(uri, Path.Combine(paddedIdDir, $"{paddedId}.sbi"));
if (!IgnoreErrors && remoteName is null)
{
if (Debug) Console.Error.WriteLine($"DEBUG: Downloading SBI for {id} failed!");
if (Debug) _progress.Report($"DEBUG: Downloading SBI for {id} failed!");
return false;
}
}
@@ -1173,7 +1187,7 @@ namespace SabreTools.RedumpLib.Web
string? remoteName = await DownloadFile(uri, Path.Combine(paddedIdDir, $"{paddedId}.lsd"));
if (!IgnoreErrors && remoteName is null)
{
if (Debug) Console.Error.WriteLine($"DEBUG: Downloading LSD for {id} failed!");
if (Debug) _progress.Report($"DEBUG: Downloading LSD for {id} failed!");
return false;
}
}
@@ -1187,12 +1201,12 @@ namespace SabreTools.RedumpLib.Web
discStreamWriter.Flush();
}
Console.WriteLine($"ID {paddedId} has been successfully downloaded");
_progress.Report($"ID {paddedId} has been successfully downloaded");
return true;
}
catch (Exception ex)
{
Console.Error.WriteLine($"An exception has occurred: {ex}");
_progress.Report($"ERROR: An exception has occurred: {ex}");
return false;
}
}
@@ -1205,7 +1219,7 @@ namespace SabreTools.RedumpLib.Web
public async Task<string?> DownloadSingleQueuePage(int id)
{
string paddedId = id.ToString().PadLeft(6, '0');
Console.WriteLine($"Processing queue ID: {paddedId}");
_progress.Report($"Processing queue ID: {paddedId}");
try
{
// Try to retrieve the data
@@ -1215,21 +1229,21 @@ namespace SabreTools.RedumpLib.Web
if (discPage is null)
{
Console.Error.WriteLine($"An error occurred retrieving ID {paddedId}!");
_progress.Report($"ERROR: An error occurred retrieving ID {paddedId}!");
return null;
}
else if (discPage.Contains("Not found"))
{
Console.Error.WriteLine($"ID {paddedId} could not be found!");
_progress.Report($"ERROR: ID {paddedId} could not be found!");
return null;
}
Console.WriteLine($"ID {paddedId} has been successfully downloaded");
_progress.Report($"ID {paddedId} has been successfully downloaded");
return discPage;
}
catch (Exception ex)
{
Console.Error.WriteLine($"An exception has occurred: {ex}");
_progress.Report($"ERROR: An exception has occurred: {ex}");
return null;
}
}
@@ -1246,13 +1260,13 @@ namespace SabreTools.RedumpLib.Web
// If no output directory is defined, use the current directory instead
if (string.IsNullOrEmpty(outDir))
{
if (Debug) Console.WriteLine("DEBUG: Output directory was not provided, setting to current directory");
if (Debug) _progress.Report("DEBUG: Output directory was not provided, setting to current directory");
outDir = Environment.CurrentDirectory;
}
string paddedId = id.ToString().PadLeft(6, '0');
string paddedIdDir = Path.Combine(outDir, paddedId);
Console.WriteLine($"Processing queue ID: {paddedId}");
_progress.Report($"Processing queue ID: {paddedId}");
try
{
// Try to retrieve the data
@@ -1262,7 +1276,7 @@ namespace SabreTools.RedumpLib.Web
if (discPage is null)
{
Console.Error.WriteLine($"An error occurred retrieving ID {paddedId}!");
_progress.Report($"ERROR: An error occurred retrieving ID {paddedId}!");
return false;
}
else if (discPage.Contains("Not found"))
@@ -1279,7 +1293,7 @@ namespace SabreTools.RedumpLib.Web
catch { }
}
Console.Error.WriteLine($"ID {paddedId} could not be found!");
_progress.Report($"ERROR: ID {paddedId} could not be found!");
return false;
}
@@ -1298,14 +1312,14 @@ namespace SabreTools.RedumpLib.Web
// // If both pages contain the same ID, skip it
// if (oldResult.Success && newResult.Success && oldResult.Groups[1].Value == newResult.Groups[1].Value)
// {
// Console.WriteLine($"ID {paddedId} has not been changed since last download, skipping...");
// Progress.Report($"ID {paddedId} has not been changed since last download, skipping...");
// return false;
// }
// // If neither page contains an ID, skip it
// else if (!oldResult.Success && !newResult.Success)
// {
// Console.WriteLine($"ID {paddedId} has not been changed since last download, skipping...");
// Progress.Report($"ID {paddedId} has not been changed since last download, skipping...");
// return false;
// }
@@ -1316,7 +1330,7 @@ namespace SabreTools.RedumpLib.Web
// If the downloaded data is invalid or otherwise empty, skip it
if (oldResult.Success && !newResult.Success)
{
Console.WriteLine($"ID {paddedId} retieved an empty page, skipping...");
_progress.Report($"ID {paddedId} retieved an empty page, skipping...");
return false;
}
}
@@ -1331,12 +1345,12 @@ namespace SabreTools.RedumpLib.Web
discStreamWriter.Flush();
}
Console.WriteLine($"ID {paddedId} has been successfully downloaded");
_progress.Report($"ID {paddedId} has been successfully downloaded");
return true;
}
catch (Exception ex)
{
Console.Error.WriteLine($"An exception has occurred: {ex}");
_progress.Report($"ERROR: An exception has occurred: {ex}");
return false;
}
}
@@ -1355,30 +1369,24 @@ namespace SabreTools.RedumpLib.Web
// Determine if the pack type is valid
if (!Enum.IsDefined(typeof(PackType), packType))
{
if (Debug) Console.Error.WriteLine($"DEBUG: {packType} is not a recognized pack type, skipping...");
if (Debug) _progress.Report($"DEBUG: {packType} is not a recognized pack type, skipping...");
return [];
}
var packsDictionary = new Dictionary<PhysicalSystem, byte[]>();
foreach (var system in systems)
{
string longName = system.Name ?? $"UNKNOWN_{system}";
if (Debug)
Console.WriteLine(longName);
else
Console.Write($"\r{longName}{new string(' ', Console.BufferWidth - longName!.Length - 1)}");
_progress.Report(system.Name);
byte[]? pack = await DownloadSinglePack(packType, system);
if (pack is not null)
packsDictionary.Add(system, pack);
}
if (Debug)
Console.WriteLine("Complete!");
else
Console.Write($"\rComplete!{new string(' ', Console.BufferWidth - 10)}");
// Report final completion
_progress.Report("Complete!");
Console.WriteLine();
// Empty report for padding
_progress.Report("");
return packsDictionary;
}
@@ -1393,27 +1401,21 @@ namespace SabreTools.RedumpLib.Web
// Determine if the pack type is valid
if (!Enum.IsDefined(typeof(PackType), packType))
{
if (Debug) Console.Error.WriteLine($"DEBUG: {packType} is not a recognized pack type, skipping...");
if (Debug) _progress.Report($"DEBUG: {packType} is not a recognized pack type, skipping...");
return false;
}
foreach (var system in systems)
{
string longName = system.Name ?? $"UNKNOWN_{system}";
if (Debug)
Console.WriteLine(longName);
else
Console.Write($"\r{longName}{new string(' ', Console.BufferWidth - longName!.Length - 1)}");
_progress.Report(system.Name);
await DownloadSinglePack(packType, system, outDir);
}
if (Debug)
Console.WriteLine("Complete!");
else
Console.Write($"\rComplete!{new string(' ', Console.BufferWidth - 10)}");
// Report final completion
_progress.Report("Complete!");
Console.WriteLine();
// Empty report for padding
_progress.Report("");
return true;
}
@@ -1429,7 +1431,7 @@ namespace SabreTools.RedumpLib.Web
// Determine if the pack type is valid
if (!Enum.IsDefined(typeof(PackType), packType))
{
if (Debug) Console.Error.WriteLine($"DEBUG: {packType} is not a recognized pack type, skipping...");
if (Debug) _progress.Report($"DEBUG: {packType} is not a recognized pack type, skipping...");
return false;
}
@@ -1438,76 +1440,33 @@ namespace SabreTools.RedumpLib.Web
// If the system is invalid, we can't do anything
if (!system.Available)
{
if (Debug) Console.WriteLine($"DEBUG: {system} is not marked as available on Redump, skipping...");
if (Debug) _progress.Report($"DEBUG: {system} is not marked as available on Redump, skipping...");
continue;
}
// If the system is unknown, we can't do anything
if (string.IsNullOrEmpty(system.Name))
{
if (Debug) Console.WriteLine($"DEBUG: {system} is not a recognized system, skipping...");
if (Debug) _progress.Report($"DEBUG: {system} is not a recognized system, skipping...");
continue;
}
// If the pack is not supported for the system
if (!PackTypeToAvailable(packType, system))
{
if (Debug) Console.WriteLine($"DEBUG: {packType} is not available for {system}, skipping...");
if (Debug) _progress.Report($"DEBUG: {packType} is not available for {system}, skipping...");
continue;
}
if (Debug)
{
Console.WriteLine(system.Name);
}
else
{
// If the buffer does not provide a width, just print on a new line
int bufferWidth = Console.BufferWidth;
if (bufferWidth == 0)
{
Console.WriteLine(system.Name);
}
// Otherwise, overwrite the line if possible
else
{
int clearLength = bufferWidth - system.Name.Length - 1;
if (clearLength < 0)
clearLength = 0;
Console.Write($"\r{system.Name}{new string(' ', clearLength)}");
}
}
_progress.Report(system.Name);
await DownloadSinglePack(packType, system, outDir, subfolder);
}
if (Debug)
{
Console.WriteLine("Complete!");
}
else
{
// If the buffer does not provide a width, just print on a new line
int bufferWidth = Console.BufferWidth;
if (bufferWidth == 0)
{
Console.WriteLine("Complete!");
}
// Report final completion
_progress.Report("Complete!");
// Otherwise, overwrite the line if possible
else
{
int clearLength = bufferWidth - 10;
if (clearLength < 0)
clearLength = 0;
Console.Write($"\rComplete!{new string(' ', clearLength)}");
}
}
Console.WriteLine();
// Empty report for padding
_progress.Report("");
return true;
}

View File

@@ -1,4 +1,3 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using SabreTools.RedumpLib.Data;
@@ -112,79 +111,72 @@ namespace SabreTools.RedumpLib.Web
{
// Keep getting discs pages until there are none left
List<int> ids = [];
try
int pageNumber = 1;
while (true)
{
int pageNumber = 1;
while (true)
if (limit > 0 && pageNumber > limit)
break;
var endpoint = new Endpoints.Discs
{
if (limit > 0 && pageNumber > limit)
break;
System = system,
Region = region,
Language = language,
Media = media,
Category = category,
Status = status,
Letter = letter,
Dumper = dumper,
Title = title,
TitleExact = titleExact,
TitleForeign = titleForeign,
TitleForeignExact = titleForeignExact,
DiscNumber = discNumber,
DiscTitle = discTitle,
DiscTitleExact = discTitleExact,
DiscTitleForeign = discTitleForeign,
DiscTitleForeignExact = discTitleForeignExact,
Serial = serial,
SerialExact = serialExact,
Edition = edition,
EditionExact = editionExact,
Barcode = barcode,
BarcodeExact = barcodeExact,
UniversalHash = universalHash,
TracksMin = tracksMin,
TracksMax = tracksMax,
ErrorsMin = errorsMin,
ErrorsMax = errorsMax,
Edc = edc,
Protection = protection,
Comments = comments,
Contents = contents,
MasteringCode = masteringCode,
MasteringSid = masteringSid,
Toolstamp = toolstamp,
MouldSid = mouldSid,
AdditionalMould = additionalMould,
Ringcode = ringcode,
Offset = offset,
Query = query,
Sort = sort,
Order = order,
Page = pageNumber++,
Advanced = advanced,
};
var endpoint = new Endpoints.Discs
{
System = system,
Region = region,
Language = language,
Media = media,
Category = category,
Status = status,
Letter = letter,
Dumper = dumper,
Title = title,
TitleExact = titleExact,
TitleForeign = titleForeign,
TitleForeignExact = titleForeignExact,
DiscNumber = discNumber,
DiscTitle = discTitle,
DiscTitleExact = discTitleExact,
DiscTitleForeign = discTitleForeign,
DiscTitleForeignExact = discTitleForeignExact,
Serial = serial,
SerialExact = serialExact,
Edition = edition,
EditionExact = editionExact,
Barcode = barcode,
BarcodeExact = barcodeExact,
UniversalHash = universalHash,
TracksMin = tracksMin,
TracksMax = tracksMax,
ErrorsMin = errorsMin,
ErrorsMax = errorsMax,
Edc = edc,
Protection = protection,
Comments = comments,
Contents = contents,
MasteringCode = masteringCode,
MasteringSid = masteringSid,
Toolstamp = toolstamp,
MouldSid = mouldSid,
AdditionalMould = additionalMould,
Ringcode = ringcode,
Offset = offset,
Query = query,
Sort = sort,
Order = order,
Page = pageNumber++,
Advanced = advanced,
};
var pageIds = await client.CheckSingleDiscsEndpoint(outDir, endpoint, discSubpaths);
if (pageIds is null)
return [];
var pageIds = await client.CheckSingleDiscsEndpoint(outDir, endpoint, discSubpaths);
if (pageIds is null)
return [];
// If all page IDs are already found, assume the last page was reached
if (pageIds.TrueForAll(ids.Contains))
break;
// If all page IDs are already found, assume the last page was reached
if (pageIds.TrueForAll(ids.Contains))
break;
ids.AddRange(pageIds);
if (pageIds.Count == 0)
break;
}
}
catch (Exception ex)
{
Console.WriteLine($"An exception occurred while trying to log in: {ex}");
return [];
ids.AddRange(pageIds);
if (pageIds.Count == 0)
break;
}
return ids;
@@ -344,79 +336,72 @@ namespace SabreTools.RedumpLib.Web
{
// Keep getting discs pages until there are none left
List<int> ids = [];
try
int pageNumber = 1;
while (true)
{
int pageNumber = 1;
while (true)
if (limit > 0 && pageNumber > limit)
break;
var endpoint = new Endpoints.Discs
{
if (limit > 0 && pageNumber > limit)
break;
System = system,
Region = region,
Language = language,
Media = media,
Category = category,
Status = status,
Letter = letter,
Dumper = dumper,
Title = title,
TitleExact = titleExact,
TitleForeign = titleForeign,
TitleForeignExact = titleForeignExact,
DiscNumber = discNumber,
DiscTitle = discTitle,
DiscTitleExact = discTitleExact,
DiscTitleForeign = discTitleForeign,
DiscTitleForeignExact = discTitleForeignExact,
Serial = serial,
SerialExact = serialExact,
Edition = edition,
EditionExact = editionExact,
Barcode = barcode,
BarcodeExact = barcodeExact,
UniversalHash = universalHash,
TracksMin = tracksMin,
TracksMax = tracksMax,
ErrorsMin = errorsMin,
ErrorsMax = errorsMax,
Edc = edc,
Protection = protection,
Comments = comments,
Contents = contents,
MasteringCode = masteringCode,
MasteringSid = masteringSid,
Toolstamp = toolstamp,
MouldSid = mouldSid,
AdditionalMould = additionalMould,
Ringcode = ringcode,
Offset = offset,
Query = query,
Sort = sort,
Order = order,
Page = pageNumber++,
Advanced = advanced,
};
var endpoint = new Endpoints.Discs
{
System = system,
Region = region,
Language = language,
Media = media,
Category = category,
Status = status,
Letter = letter,
Dumper = dumper,
Title = title,
TitleExact = titleExact,
TitleForeign = titleForeign,
TitleForeignExact = titleForeignExact,
DiscNumber = discNumber,
DiscTitle = discTitle,
DiscTitleExact = discTitleExact,
DiscTitleForeign = discTitleForeign,
DiscTitleForeignExact = discTitleForeignExact,
Serial = serial,
SerialExact = serialExact,
Edition = edition,
EditionExact = editionExact,
Barcode = barcode,
BarcodeExact = barcodeExact,
UniversalHash = universalHash,
TracksMin = tracksMin,
TracksMax = tracksMax,
ErrorsMin = errorsMin,
ErrorsMax = errorsMax,
Edc = edc,
Protection = protection,
Comments = comments,
Contents = contents,
MasteringCode = masteringCode,
MasteringSid = masteringSid,
Toolstamp = toolstamp,
MouldSid = mouldSid,
AdditionalMould = additionalMould,
Ringcode = ringcode,
Offset = offset,
Query = query,
Sort = sort,
Order = order,
Page = pageNumber++,
Advanced = advanced,
};
var pageIds = await client.CheckSingleDiscsEndpoint(endpoint);
if (pageIds is null)
return [];
var pageIds = await client.CheckSingleDiscsEndpoint(endpoint);
if (pageIds is null)
return [];
// If all page IDs are already found, assume the last page was reached
if (pageIds.TrueForAll(ids.Contains))
break;
// If all page IDs are already found, assume the last page was reached
if (pageIds.TrueForAll(ids.Contains))
break;
ids.AddRange(pageIds);
if (pageIds.Count <= 1)
break;
}
}
catch (Exception ex)
{
Console.WriteLine($"An exception occurred while trying to log in: {ex}");
return [];
ids.AddRange(pageIds);
if (pageIds.Count <= 1)
break;
}
return ids;

View File

@@ -58,6 +58,7 @@ namespace SabreTools.RedumpLib.Web
/// <param name="outDir">Output directory to save data to</param>
/// <param name="includeDatabase">Include database in downloads</param>
/// <param name="useSubfolders">True to use named subfolders to store downloads, false to store directly in the output directory</param>
/// TODO: Figure out how to remove or move these console writes
public static async Task<bool> DownloadPacksForSystems(
this Client client,
PhysicalSystem[] systems,

View File

@@ -1,4 +1,3 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using SabreTools.RedumpLib.Data;
@@ -40,44 +39,37 @@ namespace SabreTools.RedumpLib.Web
{
// Keep getting discs pages until there are none left
List<int> ids = [];
try
int pageNumber = 1;
while (true)
{
int pageNumber = 1;
while (true)
if (limit > 0 && pageNumber > limit)
break;
var endpoint = new Endpoints.Queue
{
if (limit > 0 && pageNumber > limit)
break;
Status = status,
SubType = subType,
System = system,
Submitter = submitter,
DiscId = discId,
Sort = sort,
Order = order,
Page = pageNumber++,
IsDiscHistory = isDiscHistory,
};
var endpoint = new Endpoints.Queue
{
Status = status,
SubType = subType,
System = system,
Submitter = submitter,
DiscId = discId,
Sort = sort,
Order = order,
Page = pageNumber++,
IsDiscHistory = isDiscHistory,
};
var pageIds = await client.CheckSingleQueueEndpoint(outDir, endpoint);
if (pageIds is null)
return [];
var pageIds = await client.CheckSingleQueueEndpoint(outDir, endpoint);
if (pageIds is null)
return [];
// If all page IDs are already found, assume the last page was reached
if (pageIds.TrueForAll(ids.Contains))
break;
// If all page IDs are already found, assume the last page was reached
if (pageIds.TrueForAll(ids.Contains))
break;
ids.AddRange(pageIds);
if (pageIds.Count == 0)
break;
}
}
catch (Exception ex)
{
Console.WriteLine($"An exception occurred while trying to log in: {ex}");
return [];
ids.AddRange(pageIds);
if (pageIds.Count == 0)
break;
}
return ids;
@@ -159,44 +151,37 @@ namespace SabreTools.RedumpLib.Web
{
// Keep getting discs pages until there are none left
List<int> ids = [];
try
int pageNumber = 1;
while (true)
{
int pageNumber = 1;
while (true)
if (limit > 0 && pageNumber > limit)
break;
var endpoint = new Endpoints.Queue
{
if (limit > 0 && pageNumber > limit)
break;
Status = status,
SubType = subType,
System = system,
Submitter = submitter,
DiscId = discId,
Sort = sort,
Order = order,
Page = pageNumber++,
IsDiscHistory = isDiscHistory,
};
var endpoint = new Endpoints.Queue
{
Status = status,
SubType = subType,
System = system,
Submitter = submitter,
DiscId = discId,
Sort = sort,
Order = order,
Page = pageNumber++,
IsDiscHistory = isDiscHistory,
};
var pageIds = await client.CheckSingleQueueEndpoint(endpoint);
if (pageIds is null)
return [];
var pageIds = await client.CheckSingleQueueEndpoint(endpoint);
if (pageIds is null)
return [];
// If all page IDs are already found, assume the last page was reached
if (pageIds.TrueForAll(ids.Contains))
break;
// If all page IDs are already found, assume the last page was reached
if (pageIds.TrueForAll(ids.Contains))
break;
ids.AddRange(pageIds);
if (pageIds.Count <= 1)
break;
}
}
catch (Exception ex)
{
Console.WriteLine($"An exception occurred while trying to log in: {ex}");
return [];
ids.AddRange(pageIds);
if (pageIds.Count <= 1)
break;
}
return ids;

View File

@@ -0,0 +1,82 @@
using System;
using System.Text;
namespace SabreTools.RedumpLib.Web
{
/// <summary>
/// String wrapper for event arguments
/// </summary>
public class WebClientEventArgs : EventArgs
{
/// <summary>
/// String represented by the event arguments
/// </summary>
private readonly string _value;
/// <summary>
/// Indicate if this event is a debug statement
/// </summary>
private readonly bool _debug;
/// <summary>
/// Indicate if this event is an error statement
/// </summary>
private readonly bool _error;
/// <summary>
/// Constructor for string values
/// </summary>
public WebClientEventArgs(string? value, bool debug = false, bool error = false)
{
_value = value ?? string.Empty;
_debug = debug;
_error = error;
}
/// <summary>
/// Constructor for StringBuilder values
/// </summary>
public WebClientEventArgs(StringBuilder? value, bool debug = false, bool error = false)
{
_value = value?.ToString() ?? string.Empty;
_debug = debug;
_error = error;
}
/// <summary>
/// Event arguments are just the value of the string contained within
/// </summary>
public static implicit operator string(WebClientEventArgs args)
{
if (args._debug)
return $"DEBUG: {args._value}";
else if (args._error)
return $"ERROR: {args._value}";
else
return args._value;
}
/// <summary>
/// Event arguments are just the value of the string contained within
/// </summary>
public static implicit operator StringBuilder(WebClientEventArgs args)
{
if (args._debug)
return new($"DEBUG: {args._value}");
else if (args._error)
return new($"ERROR: {args._value}");
else
return new(args._value);
}
/// <summary>
/// Event arguments are just the value of the string contained within
/// </summary>
public static implicit operator WebClientEventArgs(string? str) => new(str);
/// <summary>
/// Event arguments are just the value of the string contained within
/// </summary>
public static implicit operator WebClientEventArgs(StringBuilder? sb) => new(sb);
}
}