using System.Collections.Concurrent; using System.Text.RegularExpressions; using Common.Configuration.ContentBlocker; using Common.Configuration.DownloadCleaner; using Common.Configuration.QueueCleaner; using Common.Helpers; using Domain.Enums; using Domain.Models.Cache; using Infrastructure.Helpers; using Infrastructure.Interceptors; using Infrastructure.Verticals.ContentBlocker; using Infrastructure.Verticals.Context; using Infrastructure.Verticals.ItemStriker; using Infrastructure.Verticals.Notifications; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace Infrastructure.Verticals.DownloadClient; public abstract class DownloadService : InterceptedService, IDownloadService { protected readonly ILogger _logger; protected readonly QueueCleanerConfig _queueCleanerConfig; protected readonly ContentBlockerConfig _contentBlockerConfig; protected readonly DownloadCleanerConfig _downloadCleanerConfig; protected readonly IMemoryCache _cache; protected readonly IFilenameEvaluator _filenameEvaluator; protected readonly IStriker _striker; protected readonly MemoryCacheEntryOptions _cacheOptions; protected readonly NotificationPublisher _notifier; /// /// Constructor to be used by interceptors. /// protected DownloadService() { } protected DownloadService( ILogger logger, IOptions queueCleanerConfig, IOptions contentBlockerConfig, IOptions downloadCleanerConfig, IMemoryCache cache, IFilenameEvaluator filenameEvaluator, IStriker striker, NotificationPublisher notifier) { _logger = logger; _queueCleanerConfig = queueCleanerConfig.Value; _contentBlockerConfig = contentBlockerConfig.Value; _downloadCleanerConfig = downloadCleanerConfig.Value; _cache = cache; _filenameEvaluator = filenameEvaluator; _striker = striker; _notifier = notifier; _cacheOptions = new MemoryCacheEntryOptions() .SetSlidingExpiration(StaticConfiguration.TriggerValue + Constants.CacheLimitBuffer); } public abstract void Dispose(); public abstract Task LoginAsync(); public abstract Task ShouldRemoveFromArrQueueAsync(string hash); /// public abstract Task BlockUnwantedFilesAsync( string hash, BlocklistType blocklistType, ConcurrentBag patterns, ConcurrentBag regexes ); /// public abstract Task DeleteDownload(string hash); /// public abstract Task?> GetAllDownloadsToBeCleaned(List categories); /// public abstract Task CleanDownloads(List downloads, List categoriesToClean, HashSet excludedHashes); protected void ResetStrikesOnProgress(string hash, long downloaded) { if (!_queueCleanerConfig.StalledResetStrikesOnProgress) { return; } if (_cache.TryGetValue(CacheKeys.Item(hash), out CacheItem? cachedItem) && cachedItem is not null && downloaded > cachedItem.Downloaded) { // cache item found _cache.Remove(CacheKeys.Strike(StrikeType.Stalled, hash)); _logger.LogDebug("resetting strikes for {hash} due to progress", hash); } _cache.Set(CacheKeys.Item(hash), new CacheItem { Downloaded = downloaded }, _cacheOptions); } /// /// Strikes an item and checks if the limit has been reached. /// /// The torrent hash. /// The name or title of the item. /// True if the limit has been reached; otherwise, false. protected async Task StrikeAndCheckLimit(string hash, string itemName) { return await _striker.StrikeAndCheckLimit(hash, itemName, _queueCleanerConfig.StalledMaxStrikes, StrikeType.Stalled); } protected SeedingCheckResult ShouldCleanDownload(double ratio, TimeSpan seedingTime, Category category) { // check ratio if (DownloadReachedRatio(ratio, seedingTime, category)) { return new() { ShouldClean = true, Reason = CleanReason.MaxRatioReached }; } // check max seed time if (DownloadReachedMaxSeedTime(seedingTime, category)) { return new() { ShouldClean = true, Reason = CleanReason.MaxSeedTimeReached }; } return new(); } private bool DownloadReachedRatio(double ratio, TimeSpan seedingTime, Category category) { if (category.MaxRatio < 0) { return false; } string downloadName = ContextProvider.Get("downloadName"); TimeSpan minSeedingTime = TimeSpan.FromHours(category.MinSeedTime); if (category.MinSeedTime > 0 && seedingTime < minSeedingTime) { _logger.LogDebug("skip | download has not reached MIN_SEED_TIME | {name}", downloadName); return false; } if (ratio < category.MaxRatio) { _logger.LogDebug("skip | download has not reached MAX_RATIO | {name}", downloadName); return false; } // max ration is 0 or reached return true; } private bool DownloadReachedMaxSeedTime(TimeSpan seedingTime, Category category) { if (category.MaxSeedTime < 0) { return false; } string downloadName = ContextProvider.Get("downloadName"); TimeSpan maxSeedingTime = TimeSpan.FromHours(category.MaxSeedTime); if (category.MaxSeedTime > 0 && seedingTime < maxSeedingTime) { _logger.LogDebug("skip | download has not reached MAX_SEED_TIME | {name}", downloadName); return false; } // max seed time is 0 or reached return true; } }