forked from sim0n00ps/OF-DL
161 lines
5.4 KiB
C#
161 lines
5.4 KiB
C#
using OF_DL.Models.Downloads;
|
|
using OF_DL.Services;
|
|
|
|
namespace OF_DL.Gui.Services;
|
|
|
|
internal sealed class AvaloniaDownloadEventHandler(
|
|
Action<string> activitySink,
|
|
Action<string> progressStatusUpdate,
|
|
Action<string, long, bool> progressStart,
|
|
Action<long> progressIncrement,
|
|
Action progressStop,
|
|
Func<bool> isCancellationRequested,
|
|
CancellationToken cancellationToken) : IDownloadEventHandler
|
|
{
|
|
private string _lastProgressDescription = string.Empty;
|
|
private string _activeUsername = string.Empty;
|
|
private DateTime _activeUserStartedAtUtc;
|
|
private readonly Dictionary<string, int> _contentObjectCounts = new(StringComparer.Ordinal);
|
|
private bool _hasPerUserCompletionLogged;
|
|
|
|
public CancellationToken CancellationToken { get; } = cancellationToken;
|
|
|
|
public async Task<T> WithStatusAsync<T>(string statusMessage, Func<IStatusReporter, Task<T>> work)
|
|
{
|
|
ThrowIfCancellationRequested();
|
|
progressStart(statusMessage, 0, false);
|
|
try
|
|
{
|
|
AvaloniaStatusReporter statusReporter = new(progressStatusUpdate, isCancellationRequested);
|
|
return await work(statusReporter);
|
|
}
|
|
finally
|
|
{
|
|
progressStop();
|
|
}
|
|
}
|
|
|
|
public async Task<T> WithProgressAsync<T>(string description, long maxValue, bool showSize,
|
|
Func<IProgressReporter, Task<T>> work)
|
|
{
|
|
ThrowIfCancellationRequested();
|
|
_lastProgressDescription = description;
|
|
progressStart(description, maxValue, showSize);
|
|
try
|
|
{
|
|
AvaloniaProgressReporter reporter = new(progressIncrement, isCancellationRequested, CancellationToken);
|
|
return await work(reporter);
|
|
}
|
|
finally
|
|
{
|
|
progressStop();
|
|
}
|
|
}
|
|
|
|
public void OnContentFound(string contentType, int mediaCount, int objectCount)
|
|
{
|
|
ThrowIfCancellationRequested();
|
|
_contentObjectCounts[contentType] = objectCount;
|
|
progressStatusUpdate($"Found {mediaCount} media from {objectCount} {contentType}.");
|
|
}
|
|
|
|
public void OnNoContentFound(string contentType)
|
|
{
|
|
ThrowIfCancellationRequested();
|
|
progressStatusUpdate($"Found 0 {contentType}.");
|
|
}
|
|
|
|
public void OnDownloadComplete(string contentType, DownloadResult result)
|
|
{
|
|
ThrowIfCancellationRequested();
|
|
if (!string.IsNullOrWhiteSpace(_activeUsername) && result.NewDownloads > 0)
|
|
{
|
|
if (_contentObjectCounts.TryGetValue(contentType, out int objectCount) && objectCount > 0)
|
|
{
|
|
activitySink(
|
|
$"Downloaded {result.NewDownloads} media from {objectCount} {contentType.ToLowerInvariant()}.");
|
|
}
|
|
else
|
|
{
|
|
activitySink($"Downloaded {result.NewDownloads} media from {contentType.ToLowerInvariant()}.");
|
|
}
|
|
}
|
|
|
|
progressStatusUpdate(
|
|
$"{contentType} complete. Existing: {result.ExistingDownloads}, New: {result.NewDownloads}, Total: {result.TotalCount}.");
|
|
}
|
|
|
|
public void OnUserStarting(string username)
|
|
{
|
|
ThrowIfCancellationRequested();
|
|
_activeUsername = username;
|
|
_activeUserStartedAtUtc = DateTime.UtcNow;
|
|
_hasPerUserCompletionLogged = false;
|
|
activitySink($"Starting scrape for {username}.");
|
|
progressStatusUpdate($"Scraping data for {username}...");
|
|
}
|
|
|
|
public void OnUserComplete(string username, CreatorDownloadResult result)
|
|
{
|
|
ThrowIfCancellationRequested();
|
|
TimeSpan elapsed = DateTime.UtcNow - _activeUserStartedAtUtc;
|
|
activitySink($"Completed {username} in {elapsed.TotalMinutes:0.0} minutes.");
|
|
_activeUsername = string.Empty;
|
|
_hasPerUserCompletionLogged = true;
|
|
_contentObjectCounts.Clear();
|
|
}
|
|
|
|
public void OnPurchasedTabUserComplete(string username, int paidPostCount, int paidMessagesCount)
|
|
{
|
|
ThrowIfCancellationRequested();
|
|
TimeSpan elapsed = DateTime.UtcNow - _activeUserStartedAtUtc;
|
|
activitySink($"Completed {username} in {elapsed.TotalMinutes:0.0} minutes.");
|
|
_activeUsername = string.Empty;
|
|
_hasPerUserCompletionLogged = true;
|
|
_contentObjectCounts.Clear();
|
|
}
|
|
|
|
public void OnScrapeComplete(TimeSpan elapsed)
|
|
{
|
|
ThrowIfCancellationRequested();
|
|
if (_hasPerUserCompletionLogged)
|
|
{
|
|
return;
|
|
}
|
|
|
|
string summary = BuildCompletionSummary(elapsed);
|
|
activitySink(summary);
|
|
}
|
|
|
|
public void OnMessage(string message)
|
|
{
|
|
ThrowIfCancellationRequested();
|
|
progressStatusUpdate(message);
|
|
}
|
|
|
|
private void ThrowIfCancellationRequested()
|
|
{
|
|
if (isCancellationRequested())
|
|
{
|
|
throw new OperationCanceledException("Operation canceled by user.");
|
|
}
|
|
}
|
|
|
|
private string BuildCompletionSummary(TimeSpan elapsed)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(_lastProgressDescription))
|
|
{
|
|
return $"Download completed in {elapsed.TotalMinutes:0.0} minutes.";
|
|
}
|
|
|
|
string normalized = _lastProgressDescription.Trim().TrimEnd('.');
|
|
if (normalized.StartsWith("Downloading ", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
string remainder = normalized["Downloading ".Length..].ToLowerInvariant();
|
|
return $"Downloaded {remainder} in {elapsed.TotalMinutes:0.0} minutes.";
|
|
}
|
|
|
|
return $"{normalized} in {elapsed.TotalMinutes:0.0} minutes.";
|
|
}
|
|
}
|