using Beam.Abstractions; using Beam.Exceptions; using Beam.Models; using HtmlAgilityPack; using Microsoft.Extensions.Logging; namespace Beam.Downloaders { public class UnitFragmentDownloader(UnitDownloaderOptions options, IUnitDownloader? internalDownloader = null) : IUnitDownloader>> { public UnitDownloaderOptions Options { get; } = options; public int LinksPerDownload { get; set; } private IUnitDownloader UnitDownloader { get; } = internalDownloader ?? new UnitDownloader(options); async Task<(bool, Fragment>?)> IUnitDownloader>>.TryDownload(IOrdered[] link, CancellationToken ct, int maximumRetryCount, IProgress? downProgress, IProgress? tryProgress) { Fragment> fragment = new Fragment>(link.Length); if (!Fragment>.TryAcquireUpdater(fragment, out var updater)) throw new AssertionException(Exceptions.Exceptions.fragment_locked); bool isFailure = false; await Parallel.ForEachAsync(link, async (x, pct) => { pct.ThrowIfCancellationRequested(); ct.ThrowIfCancellationRequested(); if (isFailure) return; var (result, downloadedT) = await UnitDownloader.TryDownload([x], ct, maximumRetryCount, downProgress, tryProgress); if (!result) { Interlocked.Exchange(ref isFailure, true); return; } if (downloadedT == null) { Interlocked.Exchange(ref isFailure, true); return; } updater(new Ordered(downloadedT, x.Order)); }); if (!isFailure) Fragment>.SetComplete(fragment, true); Fragment>.TryReleaseUpdater(fragment, updater); return (!isFailure, fragment); } } }