mirror of
https://github.com/SabreTools/SabreTools.RedumpLib.git
synced 2026-09-22 15:04:34 +00:00
Migrate to CommandLine library
This commit is contained in:
@@ -1,187 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using SabreTools.RedumpLib.Web;
|
||||
|
||||
namespace RedumpTool
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains logic for dealing with downloads
|
||||
/// </summary>
|
||||
public class Downloader
|
||||
{
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Which Redump feature is being used
|
||||
/// </summary>
|
||||
public Feature Feature { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Minimum ID for downloading page information (Feature.Site, Feature.WIP only)
|
||||
/// </summary>
|
||||
public int MinimumId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum ID for downloading page information (Feature.Site, Feature.WIP only)
|
||||
/// </summary>
|
||||
public int MaximumId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Quicksearch text for downloading
|
||||
/// </summary>
|
||||
public string? QueryString { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Directory to save all outputted files to
|
||||
/// </summary>
|
||||
public string? OutDir { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Use named subfolders for discrete download sets (Feature.Packs only)
|
||||
/// </summary>
|
||||
public bool UseSubfolders { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Use the last modified page to try to grab all new discs (Feature.Site, Feature.WIP only)
|
||||
/// </summary>
|
||||
public bool OnlyNew { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Only list the page IDs but don't download
|
||||
/// </summary>
|
||||
public bool OnlyList { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Don't replace forward slashes with `-` in queries
|
||||
/// </summary>
|
||||
public bool NoSlash { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Force continuing downloads until user cancels or pages run out
|
||||
/// </summary>
|
||||
public bool Force { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Redump username
|
||||
/// </summary>
|
||||
public string? Username { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Redump password
|
||||
/// </summary>
|
||||
public string? Password { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Vars
|
||||
|
||||
/// <summary>
|
||||
/// Current HTTP rc to use
|
||||
/// </summary>
|
||||
private readonly RedumpClient _client;
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public Downloader()
|
||||
{
|
||||
_client = new RedumpClient();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="client">Preconfigured client</param>
|
||||
public Downloader(RedumpClient client)
|
||||
{
|
||||
_client = client;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Run the downloads that should go
|
||||
/// </summary>
|
||||
/// <returns>List of IDs that were processed on success, empty on error</returns>
|
||||
/// <remarks>Packs will never return anything other than empty</remarks>
|
||||
public async Task<List<int>> Download()
|
||||
{
|
||||
// Login to Redump, if possible
|
||||
if (!_client.LoggedIn)
|
||||
await _client.Login(Username ?? string.Empty, Password ?? string.Empty);
|
||||
|
||||
// Create output list
|
||||
List<int> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process the Quicksearch feature
|
||||
/// </summary>
|
||||
private async Task<List<int>> ProcessQuicksearch()
|
||||
{
|
||||
if (OnlyList)
|
||||
return await Search.ListSearchResults(_client, QueryString, NoSlash);
|
||||
else
|
||||
return await Search.DownloadSearchResults(_client, QueryString, OutDir, NoSlash);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process the Site feature
|
||||
/// </summary>
|
||||
private async Task<List<int>> ProcessSite()
|
||||
{
|
||||
if (OnlyNew)
|
||||
return await Discs.DownloadLastModified(_client, OutDir, Force);
|
||||
else
|
||||
return await Discs.DownloadSiteRange(_client, OutDir, MinimumId, MaximumId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process the User feature
|
||||
/// </summary>
|
||||
private async Task<List<int>> 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process the WIP feature
|
||||
/// </summary>
|
||||
private async Task<List<int>> ProcessWIP()
|
||||
{
|
||||
if (OnlyNew)
|
||||
return await WIP.DownloadLastSubmitted(_client, OutDir) ?? [];
|
||||
else
|
||||
return await WIP.DownloadWIPRange(_client, OutDir, MinimumId, MaximumId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
namespace RedumpTool
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines what download type to initate
|
||||
/// </summary>
|
||||
public enum Feature
|
||||
{
|
||||
NONE,
|
||||
Site,
|
||||
WIP,
|
||||
Packs,
|
||||
User,
|
||||
Quicksearch,
|
||||
}
|
||||
}
|
||||
97
RedumpTool/Features/BaseFeature.cs
Normal file
97
RedumpTool/Features/BaseFeature.cs
Normal file
@@ -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
|
||||
|
||||
/// <summary>
|
||||
/// Client to use for external connections
|
||||
/// </summary>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// Validate and create the possible supplied output directory
|
||||
/// </summary>
|
||||
/// <param name="directory">Full directory path</param>
|
||||
/// <returns>True if the output directory was validated and created, false otherwise</returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Output the set of processed IDs to console, if possible
|
||||
/// </summary>
|
||||
/// <param name="processedIds">Set of processed IDs</param>
|
||||
/// <returns>True if there were any IDs to process, false otherwise</returns>
|
||||
protected static bool PrintProcessedIds(List<int> 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
|
||||
}
|
||||
}
|
||||
65
RedumpTool/Features/PacksFeature.cs
Normal file
65
RedumpTool/Features/PacksFeature.cs
Normal file
@@ -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);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool VerifyInputs() => true;
|
||||
}
|
||||
}
|
||||
92
RedumpTool/Features/QueryFeature.cs
Normal file
92
RedumpTool/Features/QueryFeature.cs
Normal file
@@ -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);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
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<List<int>> 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);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool VerifyInputs() => true;
|
||||
}
|
||||
}
|
||||
97
RedumpTool/Features/SiteFeature.cs
Normal file
97
RedumpTool/Features/SiteFeature.cs
Normal file
@@ -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);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
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<List<int>> 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);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool VerifyInputs() => true;
|
||||
}
|
||||
}
|
||||
81
RedumpTool/Features/UserFeature.cs
Normal file
81
RedumpTool/Features/UserFeature.cs
Normal file
@@ -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);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
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<List<int>> 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);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool VerifyInputs() => true;
|
||||
}
|
||||
}
|
||||
93
RedumpTool/Features/WIPFeature.cs
Normal file
93
RedumpTool/Features/WIPFeature.cs
Normal file
@@ -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);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
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<List<int>> 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);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool VerifyInputs() => true;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Derive the feature from the supplied argument
|
||||
/// Create the command set for the program
|
||||
/// </summary>
|
||||
/// <param name="feature">Possible feature name to derive from</param>
|
||||
/// <returns>True if the feature was set, false otherwise</returns>
|
||||
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();
|
||||
|
||||
/// <summary>
|
||||
/// Create a Downloader from a feature and a set of arguments
|
||||
/// </summary>
|
||||
/// <param name="feature">Primary feature to use</param>
|
||||
/// <param name="args">Arguments list to parse</param>
|
||||
/// <returns>Initialized Downloader on success, null otherwise</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -239,7 +75,7 @@ namespace RedumpTool
|
||||
Console.WriteLine(" -min <MinId>, --minimum <MinId> - Lower bound for page numbers (cannot be used with only new)");
|
||||
Console.WriteLine(" -max <MaxId>, --maximum <MaxId> - 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 <MinId>, --minimum <MinId> - Lower bound for page numbers (cannot be used with only new)");
|
||||
|
||||
@@ -29,5 +29,8 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SabreTools.RedumpLib\SabreTools.RedumpLib.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SabreTools.CommandLine" Version="[1.4.0]" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -16,9 +16,9 @@ namespace SabreTools.RedumpLib.Web
|
||||
/// <param name="rc">RedumpClient for connectivity</param>
|
||||
/// <param name="outDir">Output directory to save data to</param>
|
||||
/// <returns>All disc IDs in last submitted range, empty on error</returns>
|
||||
public static async Task<List<int>?> DownloadLastSubmitted(RedumpClient rc, string? outDir)
|
||||
public static async Task<List<int>> DownloadLastSubmitted(RedumpClient rc, string? outDir)
|
||||
{
|
||||
return await rc.CheckSingleWIPPage(Constants.WipDumpsUrl, outDir, false);
|
||||
return await rc.CheckSingleWIPPage(Constants.WipDumpsUrl, outDir, false) ?? [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user