Files
Beam/Beam.Downloaders/UnitFragmentDownloaderBinary.cs
T
qwsdcvghyu89 7ed05abdb8 refactor: modularize Beam into new projects and interfaces
- Introduced modularity by splitting Beam into new projects: Beam.Abstractions, Beam.Models, and Beam.Downloaders.
- Refactored existing classes into appropriate namespaces and projects.
- Replaced specific implementations with abstractions (e.g., SourceLinkBuilder to LinkBuilder, State to IState, etc.).
- Updated interfaces: added ITemplate, IArticleData, IDownloadReport, and others for improved extensibility.
- Removed deprecated classes like SourceLinkBuilder and StateChangerFactory.
- Enhanced link handling in downloaders by refactoring to use `string` over `SourceLink`.
- Consolidated shared logic under Beam.Abstractions.
2025-09-22 01:51:46 +10:00

75 lines
3.2 KiB
C#

using Beam.Abstractions;
using Beam.Exceptions;
using Beam.Models;
using Microsoft.Extensions.Logging;
namespace Beam.Downloaders {
/// <summary>
/// Groups multiple binary downloads into a single Fragment, applying
/// failure detection and exponential-back-off retries for each link.
/// </summary>
public class UnitFragmentDownloaderBinary<T>
: IUnitDownloader<Fragment<Ordered<T>>> {
public UnitFragmentDownloaderBinary(HttpClient client,
AsyncTransformer<ByteDocument, T> transformer,
AsyncDownloadFailurePredicate<ByteDocument>?[]? failurePredicate = null,
int fragmentSize = 4,
ILogger? logger = null,
IUnitDownloader<T>? internalDownloader = null) {
Client = client;
Transformer = transformer;
FailurePredicate = failurePredicate;
UnitDownloader = internalDownloader
?? new UnitDownloaderBinary<T>(Client, Transformer, FailurePredicate);
LinksPerDownload = fragmentSize;
Logger = logger;
}
public HttpClient Client { get; }
public AsyncTransformer<ByteDocument, T> Transformer { get; }
public AsyncDownloadFailurePredicate<ByteDocument>?[]? FailurePredicate { get; }
public int LinksPerDownload { get; set; }
public ILogger? Logger { get; set; }
private readonly IUnitDownloader<T> UnitDownloader;
async Task<(bool, Fragment<Ordered<T>>?)> IUnitDownloader<Fragment<Ordered<T>>>.TryDownload(
IOrdered<string>[] link,
CancellationToken ct,
int maximumRetryCount,
IProgress<IRetryReport>? tryProgress) {
var fragment = new Fragment<Ordered<T>>(link.Length);
if (!Fragment<Ordered<T>>.TryAcquireUpdater(fragment, out var updater))
throw new AssertionException(Exceptions.Exceptions.fragment_locked);
var isFailure = false;
await Parallel.ForEachAsync(link, async (orderedLink, pct) => {
pct.ThrowIfCancellationRequested();
ct.ThrowIfCancellationRequested();
var (success, downloaded) =
await UnitDownloader.TryDownload([orderedLink],
ct,
maximumRetryCount,
tryProgress);
if (!success || downloaded is null) {
Interlocked.Exchange(ref isFailure, true);
Logger?.LogError("Failed to retrieve {Link} order={Order}",
orderedLink.Data, orderedLink.Order);
return;
}
updater(new Ordered<T>(downloaded, orderedLink.Order));
});
if (!isFailure)
Fragment<Ordered<T>>.SetComplete(fragment, true);
Fragment<Ordered<T>>.TryReleaseUpdater(fragment, updater);
return (!isFailure, fragment);
}
}
}