Files
2026-07-13 13:55:50 -04:00

370 lines
15 KiB
C#

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using SabreTools.CommandLine.Inputs;
using SabreTools.RedumpLib.Data;
using SabreTools.RedumpLib.Web;
namespace RedumpTool.Features
{
// TODO: Add back every single URL parameter to here
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 _barcodeName = "barcode";
internal readonly StringInput BarcodeInput = new(_barcodeName, ["--barcode"], "Add barcode to filter");
private const string _barcodeExactName = "barcode-exact";
internal readonly BooleanInput BarcodeExactInput = new(_barcodeExactName, ["--barcode-exact"], "Add barcode exact matching to filter [true, false]");
private const string _categoryName = "category";
internal readonly StringInput CategoryInput = new(_categoryName, ["--category"], "Add category to filter");
private const string _commentsName = "comments";
internal readonly StringInput CommentsInput = new(_commentsName, ["--comments"], "Add comments to filter");
private const string _contentsName = "contents";
internal readonly StringInput ContentsInput = new(_contentsName, ["--contents"], "Add contents to filter");
private const string _dumperName = "dumper";
internal readonly StringInput DumperInput = new(_dumperName, ["--dumper"], "Add dumper to filter");
private const string _edcName = "edc";
internal readonly BooleanInput EdcInput = new(_edcName, ["--edc"], "Add EDC status to filter [true, false]");
private const string _editionName = "edition";
internal readonly StringInput EditionInput = new(_editionName, ["--edition"], "Add edition to filter");
private const string _editionExactName = "edition-exact";
internal readonly BooleanInput EditionExactInput = new(_editionExactName, ["--edition-exact"], "Add edition exact matching to filter [true, false]");
private const string _errorsMaxName = "errors-max";
internal readonly Int32Input ErrorsMaximumInput = new(_errorsMaxName, ["--errors-max"], "Add maximum error count to filter");
private const string _errorsMinName = "errors-min";
internal readonly Int32Input ErrorsMinimumInput = new(_errorsMinName, ["--errors-min"], "Add minimum error count to filter");
private const string _languageName = "language";
internal readonly StringInput LanguageInput = new(_languageName, ["--language"], "Add language to filter");
private const string _letterName = "letter";
internal readonly StringInput LetterInput = new(_letterName, ["--letter"], "Add title first letter to filter");
private const string _limitName = "limit";
internal readonly Int32Input LimitInput = new(_limitName, ["--limit"], "Limit number of retrieved result pages");
private const string _listName = "list";
internal readonly FlagInput ListInput = new(_listName, ["-l", "--list"], "Only list the page IDs for the filters");
private const string _maximumName = "maximum";
internal readonly Int32Input MaximumInput = new(_maximumName, ["-max", "--maximum"], "Upper bound for page numbers (incompatible with --onlynew)");
private const string _mediaName = "media";
internal readonly StringInput MediaInput = new(_mediaName, ["--media"], "Add media type to filter");
private const string _minimumName = "minimum";
internal readonly Int32Input MinimumInput = new(_minimumName, ["-min", "--minimum"], "Lower bound for page numbers (incompatible with --onlynew)");
private const string _offsetName = "offset";
internal readonly Int32Input OffsetInput = new(_offsetName, ["--offset"], "Add offset to filter");
private const string _orderName = "order";
internal readonly StringInput OrderInput = new(_orderName, ["--order"], "Add sort order to filter [asc, desc]");
private const string _protectionName = "protection";
internal readonly StringInput ProtectionInput = new(_protectionName, ["--protection"], "Add protection to filter");
private const string _queryName = "query";
internal readonly StringInput QueryInput = new(_queryName, ["--query"], "Add query to filter");
private const string _regionName = "region";
internal readonly StringInput RegionInput = new(_regionName, ["--region"], "Add region to filter");
private const string _ringcodeName = "ringcode";
internal readonly StringInput RingcodeInput = new(_ringcodeName, ["--ringcode"], "Add ringcode to filter");
private const string _serialName = "serial";
internal readonly StringInput SerialInput = new(_serialName, ["--serial"], "Add serial to filter");
private const string _serialExactName = "serial-exact";
internal readonly BooleanInput SerialExactInput = new(_serialExactName, ["--serial-exact"], "Add serial exact matching to filter [true, false]");
private const string _sortName = "sort";
internal readonly StringInput SortInput = new(_sortName, ["--sort"], "Add sort category to filter [title, added, region, system, version, edition, language, serial, status, modified]");
private const string _statusName = "status";
internal readonly StringInput StatusInput = new(_statusName, ["--status"], "Filter by status [grey, red, yellow, blue, green]");
private const string _systemName = "system";
internal readonly StringInput SystemInput = new(_systemName, ["--system"], "Filter by system");
private const string _titleName = "title";
internal readonly StringInput TitleInput = new(_titleName, ["--title"], "Add title to filter");
private const string _titleExactName = "title-exact";
internal readonly BooleanInput TitleExactInput = new(_titleExactName, ["--title-exact"], "Add title exact matching to filter [true, false]");
private const string _titleForeignName = "title-foreign";
internal readonly StringInput TitleForeignInput = new(_titleForeignName, ["--title-foreign"], "Add foreign title to filter");
private const string _titleForeignExactName = "title-foreign-exact";
internal readonly BooleanInput TitleForeignExactInput = new(_titleForeignExactName, ["--title-foreign-exact"], "Add foreign title exact matching to filter [true, false]");
private const string _tracksMaxName = "tracks-max";
internal readonly Int32Input TracksMaximumInput = new(_tracksMaxName, ["--tracks-max"], "Add maximum track count to filter");
private const string _tracksMinName = "tracks-min";
internal readonly Int32Input TracksMinimumInput = new(_tracksMinName, ["--tracks-min"], "Add minimum track count to filter");
#endregion
public SiteFeature()
: base(DisplayName, _flags, _description)
{
RequiresInputs = false;
// Common
Add(DebugInput);
Add(OutputInput);
Add(UsernameInput);
Add(PasswordInput);
Add(AttemptCountInput);
Add(TimeoutInput);
Add(ForceDownloadInput);
Add(ForceContinueInput);
// Specific
Add(LimitInput);
Add(ListInput);
Add(MinimumInput);
Add(MaximumInput);
// Filter
Add(BarcodeInput);
Add(BarcodeExactInput);
Add(CategoryInput);
Add(CommentsInput);
Add(ContentsInput);
Add(DumperInput);
Add(EdcInput);
Add(EditionInput);
Add(EditionExactInput);
Add(ErrorsMaximumInput);
Add(ErrorsMinimumInput);
Add(LanguageInput);
Add(LetterInput);
Add(MediaInput);
Add(OffsetInput);
Add(OrderInput);
Add(ProtectionInput);
Add(QueryInput);
Add(RegionInput);
Add(RingcodeInput);
Add(SerialInput);
Add(SerialExactInput);
Add(SortInput);
Add(StatusInput);
Add(SystemInput);
Add(TitleInput);
Add(TitleExactInput);
Add(TitleForeignInput);
Add(TitleForeignExactInput);
Add(TracksMaximumInput);
Add(TracksMinimumInput);
}
/// <inheritdoc/>
public override bool Execute()
{
// Get common values
string? outDir = OutputInput.Value;
string? username = UsernameInput.Value;
string? password = PasswordInput.Value;
int? attemptCount = AttemptCountInput.Value;
int? timeout = TimeoutInput.Value;
bool forceDownload = ForceDownloadInput.Value;
bool forceContinue = ForceContinueInput.Value;
// Get specific values
int limit = LimitInput.Value ?? -1;
int? minId = MinimumInput.Value;
int? maxId = MaximumInput.Value;
bool onlyList = ListInput.Value;
// Get filter values
string? barcode = BarcodeInput.Value;
bool? barcodeExact = BarcodeExactInput.Value;
DiscCategory? category = CategoryInput.Value.ToDiscCategory();
string? comments = CommentsInput.Value;
string? contents = ContentsInput.Value;
string? dumper = DumperInput.Value;
YesNo? edc = EdcInput.Value?.ToYesNo();
string? edition = EditionInput.Value;
bool? editionExact = EditionExactInput.Value;
long? errorsMax = ErrorsMaximumInput.Value;
long? errorsMin = ErrorsMinimumInput.Value;
LanguageCode? language = LanguageInput.Value.ToLanguageCode();
char? letter = string.IsNullOrEmpty(LetterInput.Value)
? null
: LetterInput.Value![0];
MediaType? media = MediaInput.Value.ToMediaType();
int? offset = OffsetInput.Value;
SortDirection? order = OrderInput.Value.ToSortDirection();
string? protection = ProtectionInput.Value;
string? query = QueryInput.Value;
RegionCode? region = RegionInput.Value.ToRegionCode();
string? ringcode = RingcodeInput.Value;
string? serial = SerialInput.Value;
bool? serialExact = SerialExactInput.Value;
SortCategory? sort = SortInput.Value.ToSortCategory();
DumpStatus? status = StatusInput.Value.ToDumpStatus();
PhysicalSystem? system = SystemInput.Value.ToPhysicalSystem();
string? title = TitleInput.Value;
bool? titleExact = TitleExactInput.Value;
string? titleForeign = TitleForeignInput.Value;
bool? titleForeignExact = TitleForeignExactInput.Value;
long? tracksMax = TracksMaximumInput.Value;
long? tracksMin = TracksMinimumInput.Value;
// Build the disc subpaths
DiscSubpath[] discSubpaths = Constants.AllDiscSubpaths;
// Output directory validation
if (!onlyList && !ValidateAndCreateOutputDirectory(outDir))
return false;
// Range verification
if ((minId is null) ^ (maxId is null))
{
Console.WriteLine("Both a maximum and minimum ID are required");
return false;
}
else if (minId is not null && minId < 0)
{
Console.WriteLine($"{minId} is an invalid minimum ID");
return false;
}
else if (maxId is not null && maxId < 0)
{
Console.WriteLine($"{maxId} is an invalid maximum ID");
return false;
}
// Update redump.info client properties
_client.Debug = DebugInput.Value;
if (attemptCount != null && attemptCount > 0)
_client.AttemptCount = attemptCount.Value;
if (timeout != null && timeout > 0)
_client.Timeout = TimeSpan.FromSeconds(timeout.Value);
_client.Overwrite = forceDownload;
_client.IgnoreErrors = forceContinue;
// Login to redump.info, if necessary
_client.Login(username, password).Wait();
// Start the processing
Task<List<int>> processingTask;
if ((minId is null || maxId is null) && onlyList)
{
processingTask = _client.ListDiscsResults(
advanced: true, // Hardcoded, only toggles the advanced options on page by default
barcode,
barcodeExact,
category,
comments,
contents,
dumper,
edc,
edition,
editionExact,
errorsMax,
errorsMin,
language,
letter,
media,
offset,
order,
protection,
query,
region,
ringcode,
serial,
serialExact,
sort,
status,
system,
title,
titleExact,
titleForeign,
titleForeignExact,
tracksMax,
tracksMin,
limit);
}
else if ((minId is null || maxId is null) && !onlyList)
{
processingTask = _client.DownloadDiscsResults(outDir,
advanced: true, // Hardcoded, only toggles the advanced options on page by default
barcode,
barcodeExact,
category,
comments,
contents,
dumper,
edc,
edition,
editionExact,
errorsMax,
errorsMin,
language,
letter,
media,
offset,
order,
protection,
query,
region,
ringcode,
serial,
serialExact,
sort,
status,
system,
title,
titleExact,
titleForeign,
titleForeignExact,
tracksMax,
tracksMin,
limit: limit,
discSubpaths: discSubpaths);
}
else
{
processingTask = _client.DownloadSiteRange(outDir, minId!.Value, maxId!.Value, discSubpaths: discSubpaths);
}
// Retrieve the result
processingTask.Wait();
var processedIds = processingTask.Result;
// Display the processed IDs
return PrintProcessedIds(processedIds);
}
/// <inheritdoc/>
public override bool VerifyInputs() => true;
}
}