2958a26e4f
Replaces specialized binary and HTML downloaders with a generic, options-driven UnitDownloader and UnitFragmentDownloader pattern. Introduces UnitDownloaderOptions and builder classes for flexible configuration, updates interfaces and method signatures to support progress reporting, and removes redundant binary-specific classes. Updates Playwright and Stealth downloaders to use the new generic base, and adds improved error handling and reporting. Also updates dependency versions and makes minor API consistency improvements across the Fluent and Models layers.
78 lines
3.1 KiB
C#
78 lines
3.1 KiB
C#
using Beam.Abstractions;
|
|
using Beam.Models;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace Beam.Downloaders {
|
|
public class SequentialDownloader<RawType, OutType> : IAsyncEnumerator<OutType> {
|
|
public OutType Current { get; protected set; }
|
|
public DownloadContext<RawType> Context { get; }
|
|
public ILogger? Logger { get; set; }
|
|
public int LastOrder { get; set; } = 0;
|
|
|
|
protected IEnumerator<string> LinksEnumerator;
|
|
|
|
public Func<IUnitDownloader<OutType>> GetUnitDownloader { get; set; }
|
|
|
|
public SequentialDownloader(DownloadContext<RawType> context, Func<DownloadContext<RawType>, IUnitDownloader<OutType>> getUnitDownloader, ILogger? logger = null) {
|
|
Context = context;
|
|
Logger = logger;
|
|
LinksEnumerator = Context.Links.GetEnumerator();
|
|
|
|
try {
|
|
LinksEnumerator.Reset();
|
|
} catch (NotSupportedException) {
|
|
Logger?.LogWarning("Enumerator of type {} does not support resets. This may cause buggy behavior", LinksEnumerator.GetType());
|
|
}
|
|
Current = default(OutType);
|
|
GetUnitDownloader = () => getUnitDownloader(Context);
|
|
}
|
|
|
|
public ValueTask DisposeAsync() {
|
|
GC.SuppressFinalize(this);
|
|
return ValueTask.CompletedTask;
|
|
}
|
|
|
|
public async ValueTask<bool> MoveNextAsync() {
|
|
if (!LinksEnumerator.MoveNext())
|
|
return false;
|
|
|
|
//Logger?.LogInformation("MoveNextAsync()");
|
|
var unit = GetUnitDownloader(); // safe to instantiate per request.
|
|
var idealLinkCount = unit.LinksPerDownload;
|
|
List<Ordered<string>> links = [];
|
|
|
|
//Logger?.LogInformation("MoveNextAsync() \n\t -> Links.Current = {} ", LinksEnumerator.Current.Link.AbsoluteUri);
|
|
links.Add(new Ordered<string>(LinksEnumerator.Current, LastOrder++));
|
|
|
|
while (LinksEnumerator.MoveNext() && !string.IsNullOrWhiteSpace(LinksEnumerator.Current) && links.Count < idealLinkCount)
|
|
links.Add(new Ordered<string>(LinksEnumerator.Current, LastOrder++));
|
|
//Logger?.LogInformation("MoveNextAsync() \n\t -> links.Count = {} ", links.Count);
|
|
if (links.Count == 0) {
|
|
Logger?.LogInformation("Out of links!");
|
|
return false;
|
|
}
|
|
|
|
if (links.Any((x) => string.IsNullOrWhiteSpace(x.Data)))
|
|
return false;
|
|
|
|
var (result, downloadedT) = await unit.TryDownload(
|
|
links.ToArray(),
|
|
Context.CancellationToken,
|
|
downProgress: Context.DownloadReporter,
|
|
tryProgress: Context.RetryReporter);
|
|
|
|
if (!result) {
|
|
Logger?.LogWarning("Failed to download Unit<{}>", typeof(OutType).Name);
|
|
return false; // unit download failed
|
|
}
|
|
if (downloadedT is null) {
|
|
Logger?.LogWarning("Failed to download Unit<{}>", typeof(OutType).Name);
|
|
return false; // unit download failed
|
|
}
|
|
|
|
Current = downloadedT;
|
|
return true;
|
|
}
|
|
}
|
|
}
|