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) ?? [];
}
///