From d2ef8744b1e794f85086af9617faaeca6ccd233d Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Thu, 26 Feb 2026 22:54:53 -0500 Subject: [PATCH] Migrate to CommandLine library --- RedumpTool/Downloader.cs | 187 ------------------- RedumpTool/Enumerations.cs | 15 -- RedumpTool/Features/BaseFeature.cs | 97 ++++++++++ RedumpTool/Features/PacksFeature.cs | 65 +++++++ RedumpTool/Features/QueryFeature.cs | 92 +++++++++ RedumpTool/Features/SiteFeature.cs | 97 ++++++++++ RedumpTool/Features/UserFeature.cs | 81 ++++++++ RedumpTool/Features/WIPFeature.cs | 93 ++++++++++ RedumpTool/Program.cs | 226 ++++------------------- RedumpTool/RedumpTool.csproj | 3 + SabreTools.RedumpLib/Web/RedumpClient.cs | 2 +- SabreTools.RedumpLib/Web/WIP.cs | 4 +- 12 files changed, 562 insertions(+), 400 deletions(-) delete mode 100644 RedumpTool/Downloader.cs delete mode 100644 RedumpTool/Enumerations.cs create mode 100644 RedumpTool/Features/BaseFeature.cs create mode 100644 RedumpTool/Features/PacksFeature.cs create mode 100644 RedumpTool/Features/QueryFeature.cs create mode 100644 RedumpTool/Features/SiteFeature.cs create mode 100644 RedumpTool/Features/UserFeature.cs create mode 100644 RedumpTool/Features/WIPFeature.cs diff --git a/RedumpTool/Downloader.cs b/RedumpTool/Downloader.cs deleted file mode 100644 index 46b9adc..0000000 --- a/RedumpTool/Downloader.cs +++ /dev/null @@ -1,187 +0,0 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using SabreTools.RedumpLib.Web; - -namespace RedumpTool -{ - /// - /// Contains logic for dealing with downloads - /// - public class Downloader - { - #region Properties - - /// - /// Which Redump feature is being used - /// - public Feature Feature { get; set; } - - /// - /// Minimum ID for downloading page information (Feature.Site, Feature.WIP only) - /// - public int MinimumId { get; set; } - - /// - /// Maximum ID for downloading page information (Feature.Site, Feature.WIP only) - /// - public int MaximumId { get; set; } - - /// - /// Quicksearch text for downloading - /// - public string? QueryString { get; set; } - - /// - /// Directory to save all outputted files to - /// - public string? OutDir { get; set; } - - /// - /// Use named subfolders for discrete download sets (Feature.Packs only) - /// - public bool UseSubfolders { get; set; } - - /// - /// Use the last modified page to try to grab all new discs (Feature.Site, Feature.WIP only) - /// - public bool OnlyNew { get; set; } - - /// - /// Only list the page IDs but don't download - /// - public bool OnlyList { get; set; } - - /// - /// Don't replace forward slashes with `-` in queries - /// - public bool NoSlash { get; set; } - - /// - /// Force continuing downloads until user cancels or pages run out - /// - public bool Force { get; set; } - - /// - /// Redump username - /// - public string? Username { get; set; } - - /// - /// Redump password - /// - public string? Password { get; set; } - - #endregion - - #region Private Vars - - /// - /// Current HTTP rc to use - /// - private readonly RedumpClient _client; - - #endregion - - /// - /// Constructor - /// - public Downloader() - { - _client = new RedumpClient(); - } - - /// - /// Constructor - /// - /// Preconfigured client - public Downloader(RedumpClient client) - { - _client = client; - } - - /// - /// Run the downloads that should go - /// - /// List of IDs that were processed on success, empty on error - /// Packs will never return anything other than empty - public async Task> Download() - { - // Login to Redump, if possible - if (!_client.LoggedIn) - await _client.Login(Username ?? string.Empty, Password ?? string.Empty); - - // Create output list - List processedIds = []; - - switch (Feature) - { - case Feature.Packs: - await Packs.DownloadPacks(_client, OutDir, UseSubfolders); - break; - case Feature.Quicksearch: - processedIds = await ProcessQuicksearch(); - break; - case Feature.Site: - processedIds = await ProcessSite(); - break; - case Feature.User: - processedIds = await ProcessUser(); - break; - case Feature.WIP: - processedIds = await ProcessWIP(); - break; - case Feature.NONE: - default: - return []; - } - - return processedIds; - } - - /// - /// Process the Quicksearch feature - /// - private async Task> ProcessQuicksearch() - { - if (OnlyList) - return await Search.ListSearchResults(_client, QueryString, NoSlash); - else - return await Search.DownloadSearchResults(_client, QueryString, OutDir, NoSlash); - } - - /// - /// Process the Site feature - /// - private async Task> ProcessSite() - { - if (OnlyNew) - return await Discs.DownloadLastModified(_client, OutDir, Force); - else - return await Discs.DownloadSiteRange(_client, OutDir, MinimumId, MaximumId); - } - - /// - /// Process the User feature - /// - private async Task> ProcessUser() - { - if (OnlyList) - return await User.ListUser(_client, Username); - else if (OnlyNew) - return await User.DownloadUserLastModified(_client, Username, OutDir); - else - return await User.DownloadUser(_client, Username, OutDir); - } - - /// - /// Process the WIP feature - /// - private async Task> ProcessWIP() - { - if (OnlyNew) - return await WIP.DownloadLastSubmitted(_client, OutDir) ?? []; - else - return await WIP.DownloadWIPRange(_client, OutDir, MinimumId, MaximumId); - } - } -} diff --git a/RedumpTool/Enumerations.cs b/RedumpTool/Enumerations.cs deleted file mode 100644 index 4e5d9ce..0000000 --- a/RedumpTool/Enumerations.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace RedumpTool -{ - /// - /// Determines what download type to initate - /// - public enum Feature - { - NONE, - Site, - WIP, - Packs, - User, - Quicksearch, - } -} diff --git a/RedumpTool/Features/BaseFeature.cs b/RedumpTool/Features/BaseFeature.cs new file mode 100644 index 0000000..fe08504 --- /dev/null +++ b/RedumpTool/Features/BaseFeature.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.IO; +using SabreTools.CommandLine; +using SabreTools.CommandLine.Inputs; +using SabreTools.RedumpLib.Web; + +namespace RedumpTool.Features +{ + internal abstract class BaseFeature : Feature + { + #region Inputs + + private const string _debugName = "debug"; + internal readonly FlagInput DebugInput = new(_debugName, ["-d", "--debug"], "Enable debug mode"); + + private const string _outputName = "output"; + internal readonly StringInput OutputInput = new(_outputName, ["-o", "--output"], "Set the base output directory"); + + private const string _passwordName = "password"; + internal readonly StringInput PasswordInput = new(_passwordName, ["-p", "--password"], "Redump password"); + + private const string _usernameName = "username"; + internal readonly StringInput UsernameInput = new(_usernameName, ["-u", "--username"], "Redump username"); + + #endregion + + #region Fields + + /// + /// Client to use for external connections + /// + protected RedumpClient _client; + + #endregion + + public BaseFeature(string name, string[] flags, string description, string? detailed = null) + : base(name, flags, description, detailed) + { + _client = new RedumpClient(); + } + + #region Helpers + + /// + /// Validate and create the possible supplied output directory + /// + /// Full directory path + /// True if the output directory was validated and created, false otherwise + protected static bool ValidateAndCreateOutputDirectory(string? directory) + { + if (string.IsNullOrEmpty(directory)) + { + Console.Error.WriteLine("No output directory set!"); + return false; + } + else + { + // Create the output directory, if it doesn't exist + try + { + if (!Directory.Exists(directory)) + Directory.CreateDirectory(directory); + + return true; + } + catch (Exception ex) + { + Console.Error.WriteLine($"An exception has occurred: {ex}"); + return false; + } + } + } + + /// + /// Output the set of processed IDs to console, if possible + /// + /// Set of processed IDs + /// True if there were any IDs to process, false otherwise + protected static bool PrintProcessedIds(List processedIds) + { + if (processedIds.Count > 0) + { + string formattedIds = string.Join(", ", [.. processedIds.ConvertAll(i => i.ToString())]); + Console.WriteLine($"Processed IDs: {formattedIds}"); + return true; + } + else + { + Console.WriteLine("No results were found"); + return false; + } + } + + #endregion + } +} diff --git a/RedumpTool/Features/PacksFeature.cs b/RedumpTool/Features/PacksFeature.cs new file mode 100644 index 0000000..7cd0430 --- /dev/null +++ b/RedumpTool/Features/PacksFeature.cs @@ -0,0 +1,65 @@ +using SabreTools.CommandLine.Inputs; +using SabreTools.RedumpLib.Web; + +namespace RedumpTool.Features +{ + internal sealed class PacksFeature : BaseFeature + { + #region Feature Definition + + public const string DisplayName = "packs"; + + private static readonly string[] _flags = ["packs"]; + + private const string _description = "Download available packs"; + + #endregion + + #region Inputs + + private const string _subfoldersName = "subfolders"; + internal readonly FlagInput SubfoldersInput = new(_subfoldersName, ["-s", "--subfolders"], "Download packs to named subfolders"); + + #endregion + + public PacksFeature() + : base(DisplayName, _flags, _description) + { + RequiresInputs = false; + + // Common + Add(DebugInput); + Add(OutputInput); + Add(UsernameInput); + Add(PasswordInput); + + // Specific + Add(SubfoldersInput); + } + + /// + public override bool Execute() + { + // Get values needed more than once + string? outputDirectory = OutputInput.Value; + + // Output directory validation + if (!ValidateAndCreateOutputDirectory(outputDirectory)) + return false; + + // Login to Redump, if necessary + if (!_client.LoggedIn) + _client.Login(UsernameInput.Value ?? string.Empty, PasswordInput.Value ?? string.Empty).Wait(); + + // Start the processing + var processingTask = Packs.DownloadPacks(_client, outputDirectory, SubfoldersInput.Value); + + // Retrieve the result + processingTask.Wait(); + return processingTask.Result; + } + + /// + public override bool VerifyInputs() => true; + } +} diff --git a/RedumpTool/Features/QueryFeature.cs b/RedumpTool/Features/QueryFeature.cs new file mode 100644 index 0000000..151f6bb --- /dev/null +++ b/RedumpTool/Features/QueryFeature.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using SabreTools.CommandLine.Inputs; +using SabreTools.RedumpLib.Web; + +namespace RedumpTool.Features +{ + internal sealed class QueryFeature : BaseFeature + { + #region Feature Definition + + public const string DisplayName = "query"; + + private static readonly string[] _flags = ["query"]; + + private const string _description = "Download pages and related files from a Redump-compatible query"; + + #endregion + + #region Inputs + + private const string _listName = "list"; + internal readonly FlagInput ListInput = new(_listName, ["-l", "--list"], "Only list the page IDs for that query"); + + private const string _noSlashName = "noslash"; + internal readonly FlagInput NoSlashInput = new(_noSlashName, ["-ns", "--noslash"], "Don't replace forward slashes with '-'"); + + private const string _queryName = "query"; + internal readonly StringInput QueryInput = new(_queryName, ["-q", "--query"], "Redump-compatible query to run"); + + #endregion + + public QueryFeature() + : base(DisplayName, _flags, _description) + { + RequiresInputs = false; + + // Common + Add(DebugInput); + Add(OutputInput); + Add(UsernameInput); + Add(PasswordInput); + + // Specific + Add(QueryInput); + Add(ListInput); + Add(NoSlashInput); + } + + /// + public override bool Execute() + { + // Get values needed more than once + bool onlyList = ListInput.Value; + string? outputDirectory = OutputInput.Value; + string? queryString = QueryInput.Value; + + // Output directory validation + if (!onlyList && !ValidateAndCreateOutputDirectory(outputDirectory)) + return false; + + // Query verification (and cleanup) + if (string.IsNullOrEmpty(queryString)) + { + Console.Error.WriteLine("Please enter a query for searching"); + return false; + } + + // Login to Redump, if necessary + if (!_client.LoggedIn) + _client.Login(UsernameInput.Value ?? string.Empty, PasswordInput.Value ?? string.Empty).Wait(); + + // Start the processing + Task> processingTask; + if (onlyList) + processingTask = Search.ListSearchResults(_client, queryString, NoSlashInput.Value); + else + processingTask = Search.DownloadSearchResults(_client, queryString, outputDirectory, NoSlashInput.Value); + + // Retrieve the result + processingTask.Wait(); + var processedIds = processingTask.Result; + + // Display the processed IDs + return PrintProcessedIds(processedIds); + } + + /// + public override bool VerifyInputs() => true; + } +} diff --git a/RedumpTool/Features/SiteFeature.cs b/RedumpTool/Features/SiteFeature.cs new file mode 100644 index 0000000..b3d3ffc --- /dev/null +++ b/RedumpTool/Features/SiteFeature.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using SabreTools.CommandLine.Inputs; +using SabreTools.RedumpLib.Web; + +namespace RedumpTool.Features +{ + internal sealed class SiteFeature : BaseFeature + { + #region Feature Definition + + public const string DisplayName = "site"; + + private static readonly string[] _flags = ["site"]; + + private const string _description = "Download pages and related files from the main site"; + + #endregion + + #region Inputs + + private const string _forceName = "force"; + internal readonly FlagInput ForceInput = new(_forceName, ["-f", "--force"], "Force continuing downloads until user cancels (requires only new)"); + + private const string _maximumName = "maximum"; + internal readonly Int32Input MaximumInput = new(_maximumName, ["-max", "--maximum"], "Upper bound for page numbers (cannot be used with only new)"); + + private const string _minimumName = "minimum"; + internal readonly Int32Input MinimumInput = new(_minimumName, ["-min", "--minimum"], "Lower bound for page numbers (cannot be used with only new)"); + + private const string _onlyNewName = "onlynew"; + internal readonly FlagInput OnlyNewInput = new(_onlyNewName, ["-n", "--onlynew"], "Use the last modified view (cannot be used with min and max)"); + + #endregion + + public SiteFeature() + : base(DisplayName, _flags, _description) + { + RequiresInputs = false; + + // Common + Add(DebugInput); + Add(OutputInput); + Add(UsernameInput); + Add(PasswordInput); + + // Specific + Add(MinimumInput); + Add(MaximumInput); + Add(OnlyNewInput); + Add(ForceInput); + } + + /// + public override bool Execute() + { + // Get values needed more than once + string? outputDirectory = OutputInput.Value; + int minId = MinimumInput.Value ?? -1; + int maxId = MaximumInput.Value ?? -1; + bool onlyNew = OnlyNewInput.Value; + + // Output directory validation + if (!ValidateAndCreateOutputDirectory(outputDirectory)) + return false; + + // Range verification + if (!onlyNew && (minId < 0 || maxId < 0)) + { + Console.WriteLine("Please enter a valid range of Redump IDs"); + return false; + } + + // Login to Redump, if necessary + if (!_client.LoggedIn) + _client.Login(UsernameInput.Value ?? string.Empty, PasswordInput.Value ?? string.Empty).Wait(); + + // Start the processing + Task> processingTask; + if (onlyNew) + processingTask = Discs.DownloadLastModified(_client, outputDirectory, ForceInput.Value); + else + processingTask = Discs.DownloadSiteRange(_client, outputDirectory, minId, maxId); + + // Retrieve the result + processingTask.Wait(); + var processedIds = processingTask.Result; + + // Display the processed IDs + return PrintProcessedIds(processedIds); + } + + /// + public override bool VerifyInputs() => true; + } +} diff --git a/RedumpTool/Features/UserFeature.cs b/RedumpTool/Features/UserFeature.cs new file mode 100644 index 0000000..7c16f3d --- /dev/null +++ b/RedumpTool/Features/UserFeature.cs @@ -0,0 +1,81 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using SabreTools.CommandLine.Inputs; +using SabreTools.RedumpLib.Web; + +namespace RedumpTool.Features +{ + internal sealed class UserFeature : BaseFeature + { + #region Feature Definition + + public const string DisplayName = "user"; + + private static readonly string[] _flags = ["user"]; + + private const string _description = "Download pages and related files for a particular user"; + + #endregion + + #region Inputs + + private const string _listName = "list"; + internal readonly FlagInput ListInput = new(_listName, ["-l", "--list"], "Only list the page IDs for that user"); + + private const string _onlyNewName = "onlynew"; + internal readonly FlagInput OnlyNewInput = new(_onlyNewName, ["-n", "--onlynew"], "Use the last modified view instead of sequential parsing"); + + #endregion + + public UserFeature() + : base(DisplayName, _flags, _description) + { + RequiresInputs = false; + + // Common + Add(DebugInput); + Add(OutputInput); + Add(UsernameInput); + Add(PasswordInput); + + // Specific + Add(OnlyNewInput); + Add(ListInput); + } + + /// + public override bool Execute() + { + // Get values needed more than once + bool onlyList = ListInput.Value; + string? outputDirectory = OutputInput.Value; + + // Output directory validation + if (!onlyList && !ValidateAndCreateOutputDirectory(outputDirectory)) + return false; + + // Login to Redump, if necessary + if (!_client.LoggedIn) + _client.Login(UsernameInput.Value ?? string.Empty, PasswordInput.Value ?? string.Empty).Wait(); + + // Start the processing + Task> processingTask; + if (onlyList) + processingTask = User.ListUser(_client, UsernameInput.Value); + else if (OnlyNewInput.Value) + processingTask = User.DownloadUserLastModified(_client, UsernameInput.Value, outputDirectory); + else + processingTask = User.DownloadUser(_client, UsernameInput.Value, outputDirectory); + + // Retrieve the result + processingTask.Wait(); + var processedIds = processingTask.Result; + + // Display the processed IDs + return PrintProcessedIds(processedIds); + } + + /// + public override bool VerifyInputs() => true; + } +} diff --git a/RedumpTool/Features/WIPFeature.cs b/RedumpTool/Features/WIPFeature.cs new file mode 100644 index 0000000..ee28854 --- /dev/null +++ b/RedumpTool/Features/WIPFeature.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using SabreTools.CommandLine.Inputs; +using SabreTools.RedumpLib.Web; + +namespace RedumpTool.Features +{ + internal sealed class WIPFeature : BaseFeature + { + #region Feature Definition + + public const string DisplayName = "wip"; + + private static readonly string[] _flags = ["wip"]; + + private const string _description = "Download pages and related files from the WIP list"; + + #endregion + + #region Inputs + + private const string _maximumName = "maximum"; + internal readonly Int32Input MaximumInput = new(_maximumName, ["-max", "--maximum"], "Upper bound for page numbers (cannot be used with only new)"); + + private const string _minimumName = "minimum"; + internal readonly Int32Input MinimumInput = new(_minimumName, ["-min", "--minimum"], "Lower bound for page numbers (cannot be used with only new)"); + + private const string _onlyNewName = "onlynew"; + internal readonly FlagInput OnlyNewInput = new(_onlyNewName, ["-n", "--onlynew"], "Use the last modified view (cannot be used with min and max)"); + + #endregion + + public WIPFeature() + : base(DisplayName, _flags, _description) + { + RequiresInputs = false; + + // Common + Add(DebugInput); + Add(OutputInput); + Add(UsernameInput); + Add(PasswordInput); + + // Specific + Add(MinimumInput); + Add(MaximumInput); + Add(OnlyNewInput); + } + + /// + public override bool Execute() + { + // Get values needed more than once + string? outputDirectory = OutputInput.Value; + int minId = MinimumInput.Value ?? -1; + int maxId = MaximumInput.Value ?? -1; + bool onlyNew = OnlyNewInput.Value; + + // Output directory validation + if (!ValidateAndCreateOutputDirectory(outputDirectory)) + return false; + + // Range verification + if (!onlyNew && (minId < 0 || maxId < 0)) + { + Console.WriteLine("Please enter a valid range of WIP IDs"); + return false; + } + + // Login to Redump, if necessary + if (!_client.LoggedIn) + _client.Login(UsernameInput.Value ?? string.Empty, PasswordInput.Value ?? string.Empty).Wait(); + + // Start the processing + Task> processingTask; + if (onlyNew) + processingTask = WIP.DownloadLastSubmitted(_client, outputDirectory); + else + processingTask = WIP.DownloadWIPRange(_client, outputDirectory, minId, maxId); + + // Retrieve the result + processingTask.Wait(); + var processedIds = processingTask.Result; + + // Display the processed IDs + return PrintProcessedIds(processedIds); + } + + /// + public override bool VerifyInputs() => true; + } +} diff --git a/RedumpTool/Program.cs b/RedumpTool/Program.cs index 3b5da00..3fe6961 100644 --- a/RedumpTool/Program.cs +++ b/RedumpTool/Program.cs @@ -1,5 +1,7 @@ using System; -using System.IO; +using RedumpTool.Features; +using SabreTools.CommandLine; +using SabreTools.CommandLine.Features; namespace RedumpTool { @@ -7,6 +9,9 @@ namespace RedumpTool { public static void Main(string[] args) { + // Create the command set + var commandSet = CreateCommands(); + // Show help if nothing is input if (args.Length == 0) { @@ -15,210 +20,41 @@ namespace RedumpTool return; } - // Derive the feature, if possible - Feature feature = DeriveFeature(args[0]); - if (feature == Feature.NONE) - { - Console.WriteLine("The feature could not be derived"); - ShowHelp(); - return; - } + // Cache the first argument and starting index + string featureName = args[0]; - // Create a new Downloader - var downloader = CreateDownloader(feature, args); - if (downloader is null) + // Try processing the standalone arguments + var topLevel = commandSet.GetTopLevel(featureName); + switch (topLevel) { - Console.WriteLine("A downloader could not be created from the inputs"); - ShowHelp(); - return; - } + case Help help: help.ProcessArgs(args, 0, commandSet); return; + case SiteFeature sf: sf.ProcessArgs(args, 1); sf.Execute(); return; + case WIPFeature wf: wf.ProcessArgs(args, 1); wf.Execute(); return; + case PacksFeature pf: pf.ProcessArgs(args, 1); pf.Execute(); return; + case QueryFeature qf: qf.ProcessArgs(args, 1); qf.Execute(); return; - // Run the download task - var downloaderTask = downloader.Download(); - downloaderTask.Wait(); - - // Get the downloader task results and print, if necessary - var downloaderResult = downloaderTask.Result; - if (downloaderResult.Count > 0) - { - string processedIds = string.Join(", ", [.. downloaderResult.ConvertAll(i => i.ToString())]); - Console.WriteLine($"Processed IDs: {processedIds}"); - } - else if (downloaderResult.Count == 0 && downloader.Feature != Feature.Packs) - { - Console.WriteLine("No results were found"); - ShowHelp(); + default: + Console.WriteLine($"{featureName} is not a known feature"); + ShowHelp(); + return; } } /// - /// Derive the feature from the supplied argument + /// Create the command set for the program /// - /// Possible feature name to derive from - /// True if the feature was set, false otherwise - private static Feature DeriveFeature(string feature) + private static CommandSet CreateCommands() { - return feature.ToLowerInvariant() switch - { - "site" => Feature.Site, - "wip" => Feature.WIP, - "packs" => Feature.Packs, - "user" => Feature.User, - "search" => Feature.Quicksearch, - "query" => Feature.Quicksearch, - _ => Feature.NONE, - }; - } + var commandSet = new CommandSet(); - /// - /// Create a Downloader from a feature and a set of arguments - /// - /// Primary feature to use - /// Arguments list to parse - /// Initialized Downloader on success, null otherwise - private static Downloader? CreateDownloader(Feature feature, string[] args) - { - var downloader = new Downloader() - { - Feature = feature, - MinimumId = -1, - MaximumId = -1, - }; + commandSet.Add(new Help(["-?", "-h", "--help"])); + commandSet.Add(new SiteFeature()); + commandSet.Add(new WIPFeature()); + commandSet.Add(new PacksFeature()); + commandSet.Add(new UserFeature()); + commandSet.Add(new QueryFeature()); - // Loop through all of the arguments - try - { - for (int i = 1; i < args.Length; i++) - { - switch (args[i]) - { - // Output directory - case "-o": - case "--output": - downloader.OutDir = args[++i].Trim('"'); - break; - - // Username - case "-u": - case "--username": - downloader.Username = args[++i]; - break; - - // Password - case "-p": - case "--password": - downloader.Password = args[++i]; - break; - - // Minimum Redump ID - case "-min": - case "--minimum": - if (!int.TryParse(args[++i], out int minimumId)) - minimumId = -1; - - downloader.MinimumId = minimumId; - break; - - // Maximum Redump ID - case "-max": - case "--maximum": - if (!int.TryParse(args[++i], out int maximumId)) - maximumId = -1; - - downloader.MaximumId = maximumId; - break; - - // Quicksearch text - case "-q": - case "--query": - downloader.QueryString = args[++i]; - break; - - // Packs subfolders - case "-s": - case "--subfolders": - downloader.UseSubfolders = true; - break; - - // Use last modified - case "-n": - case "--onlynew": - downloader.OnlyNew = true; - break; - - // List instead of download - case "-l": - case "--list": - downloader.OnlyList = true; - break; - - // Don't filter forward slashes from queries - case "-ns": - case "--noslash": - downloader.NoSlash = true; - break; - - // Force continuation - case "-f": - case "--force": - downloader.Force = true; - break; - - // Everything else - default: - Console.WriteLine($"Unrecognized flag: {args[i]}"); - break; - } - } - } - catch (Exception ex) - { - Console.WriteLine($"An exception has occurred: {ex}"); - return null; - } - - // Output directory validation - if (!downloader.OnlyList && string.IsNullOrEmpty(downloader.OutDir)) - { - Console.WriteLine("No output directory set!"); - return null; - } - else if (!downloader.OnlyList && !string.IsNullOrEmpty(downloader.OutDir)) - { - // Create the output directory, if it doesn't exist - try - { - if (!Directory.Exists(downloader.OutDir)) - Directory.CreateDirectory(downloader.OutDir!); - } - catch (Exception ex) - { - Console.WriteLine($"An exception has occurred: {ex}"); - return null; - } - } - - // Range verification - if (feature == Feature.Site && !downloader.OnlyNew && (downloader.MinimumId < 0 || downloader.MaximumId < 0)) - { - Console.WriteLine("Please enter a valid range of Redump IDs"); - return null; - } - else if (feature == Feature.WIP && !downloader.OnlyNew && (downloader.MinimumId < 0 || downloader.MaximumId < 0)) - { - Console.WriteLine("Please enter a valid range of WIP IDs"); - return null; - } - - // Query verification (and cleanup) - if (feature == Feature.Quicksearch && string.IsNullOrEmpty(downloader.QueryString)) - { - Console.WriteLine("Please enter a query for searching"); - return null; - } - - // Return the downloader - return downloader; + return commandSet; } /// @@ -239,7 +75,7 @@ namespace RedumpTool Console.WriteLine(" -min , --minimum - Lower bound for page numbers (cannot be used with only new)"); Console.WriteLine(" -max , --maximum - Upper bound for page numbers (cannot be used with only new)"); Console.WriteLine(" -n, --onlynew - Use the last modified view (cannot be used with min and max)"); - Console.WriteLine(" -f, --force - Force continuing downloads until user cancels (used with only new)"); + Console.WriteLine(" -f, --force - Force continuing downloads until user cancels (requires only new)"); Console.WriteLine(); Console.WriteLine("wip - Download pages and related files from the WIP list"); Console.WriteLine(" -min , --minimum - Lower bound for page numbers (cannot be used with only new)"); diff --git a/RedumpTool/RedumpTool.csproj b/RedumpTool/RedumpTool.csproj index 163a634..cde0b5b 100644 --- a/RedumpTool/RedumpTool.csproj +++ b/RedumpTool/RedumpTool.csproj @@ -29,5 +29,8 @@ + + + diff --git a/SabreTools.RedumpLib/Web/RedumpClient.cs b/SabreTools.RedumpLib/Web/RedumpClient.cs index f112710..e8cdefd 100644 --- a/SabreTools.RedumpLib/Web/RedumpClient.cs +++ b/SabreTools.RedumpLib/Web/RedumpClient.cs @@ -145,7 +145,7 @@ namespace SabreTools.RedumpLib.Web { try { - Console.WriteLine($"Login attempt {i} of {MaxLoginAttempts}"); + Console.WriteLine($"Login attempt {i + 1} of {MaxLoginAttempts}"); // Get the current token from the login page var loginPage = await DownloadString(Constants.LoginUrl); diff --git a/SabreTools.RedumpLib/Web/WIP.cs b/SabreTools.RedumpLib/Web/WIP.cs index 4820e7b..3aab3b9 100644 --- a/SabreTools.RedumpLib/Web/WIP.cs +++ b/SabreTools.RedumpLib/Web/WIP.cs @@ -16,9 +16,9 @@ namespace SabreTools.RedumpLib.Web /// RedumpClient for connectivity /// Output directory to save data to /// All disc IDs in last submitted range, empty on error - public static async Task?> DownloadLastSubmitted(RedumpClient rc, string? outDir) + public static async Task> DownloadLastSubmitted(RedumpClient rc, string? outDir) { - return await rc.CheckSingleWIPPage(Constants.WipDumpsUrl, outDir, false); + return await rc.CheckSingleWIPPage(Constants.WipDumpsUrl, outDir, false) ?? []; } ///