added category change for downloads with no additional hardlinks
This commit is contained in:
@@ -10,6 +10,7 @@ using Domain.Models.Deluge.Response;
|
||||
using Infrastructure.Interceptors;
|
||||
using Infrastructure.Verticals.ContentBlocker;
|
||||
using Infrastructure.Verticals.Context;
|
||||
using Infrastructure.Verticals.Files;
|
||||
using Infrastructure.Verticals.ItemStriker;
|
||||
using Infrastructure.Verticals.Notifications;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
@@ -33,10 +34,11 @@ public class DelugeService : DownloadService, IDelugeService
|
||||
IFilenameEvaluator filenameEvaluator,
|
||||
IStriker striker,
|
||||
INotificationPublisher notifier,
|
||||
IDryRunInterceptor dryRunInterceptor
|
||||
IDryRunInterceptor dryRunInterceptor,
|
||||
IHardlinkFileService hardlinkFileService
|
||||
) : base(
|
||||
logger, queueCleanerConfig, contentBlockerConfig, downloadCleanerConfig, cache,
|
||||
filenameEvaluator, striker, notifier, dryRunInterceptor
|
||||
filenameEvaluator, striker, notifier, dryRunInterceptor, hardlinkFileService
|
||||
)
|
||||
{
|
||||
config.Value.Validate();
|
||||
@@ -194,7 +196,7 @@ public class DelugeService : DownloadService, IDelugeService
|
||||
return result;
|
||||
}
|
||||
|
||||
public override async Task<List<object>?> GetAllDownloadsToBeCleaned(List<Category> categories)
|
||||
public override async Task<List<object>?> GetDownloadsToBeCleaned(List<CleanCategory> categories)
|
||||
{
|
||||
return (await _client.GetStatusForAllTorrents())
|
||||
?.Where(x => !string.IsNullOrEmpty(x.Hash))
|
||||
@@ -204,8 +206,13 @@ public class DelugeService : DownloadService, IDelugeService
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public override Task<List<object>?> GetDownloadsToChangeCategory(List<string> categories)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task CleanDownloads(List<object> downloads, List<Category> categoriesToClean, HashSet<string> excludedHashes)
|
||||
public override async Task CleanDownloads(List<object> downloads, List<CleanCategory> categoriesToClean, HashSet<string> excludedHashes)
|
||||
{
|
||||
foreach (TorrentStatus download in downloads)
|
||||
{
|
||||
@@ -214,7 +221,7 @@ public class DelugeService : DownloadService, IDelugeService
|
||||
continue;
|
||||
}
|
||||
|
||||
Category? category = categoriesToClean
|
||||
CleanCategory? category = categoriesToClean
|
||||
.FirstOrDefault(x => x.Name.Equals(download.Label, StringComparison.InvariantCultureIgnoreCase));
|
||||
|
||||
if (category is null)
|
||||
@@ -258,7 +265,12 @@ public class DelugeService : DownloadService, IDelugeService
|
||||
await _notifier.NotifyDownloadCleaned(download.Ratio, seedingTime, category.Name, result.Reason);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override Task ChangeCategoryForNoHardlinksAsync(List<object> downloads, HashSet<string> excludedHashes)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[DryRunSafeguard]
|
||||
public override async Task DeleteDownload(string hash)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Text.RegularExpressions;
|
||||
using Common.Configuration.ContentBlocker;
|
||||
using Common.Configuration.DownloadCleaner;
|
||||
@@ -10,6 +10,7 @@ using Infrastructure.Helpers;
|
||||
using Infrastructure.Interceptors;
|
||||
using Infrastructure.Verticals.ContentBlocker;
|
||||
using Infrastructure.Verticals.Context;
|
||||
using Infrastructure.Verticals.Files;
|
||||
using Infrastructure.Verticals.ItemStriker;
|
||||
using Infrastructure.Verticals.Notifications;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
@@ -30,6 +31,7 @@ public abstract class DownloadService : IDownloadService
|
||||
protected readonly MemoryCacheEntryOptions _cacheOptions;
|
||||
protected readonly INotificationPublisher _notifier;
|
||||
protected readonly IDryRunInterceptor _dryRunInterceptor;
|
||||
protected readonly IHardlinkFileService _hardlinkFileService;
|
||||
|
||||
protected DownloadService(
|
||||
ILogger<DownloadService> logger,
|
||||
@@ -40,7 +42,8 @@ public abstract class DownloadService : IDownloadService
|
||||
IFilenameEvaluator filenameEvaluator,
|
||||
IStriker striker,
|
||||
INotificationPublisher notifier,
|
||||
IDryRunInterceptor dryRunInterceptor
|
||||
IDryRunInterceptor dryRunInterceptor,
|
||||
IHardlinkFileService hardlinkFileService
|
||||
)
|
||||
{
|
||||
_logger = logger;
|
||||
@@ -52,6 +55,7 @@ public abstract class DownloadService : IDownloadService
|
||||
_striker = striker;
|
||||
_notifier = notifier;
|
||||
_dryRunInterceptor = dryRunInterceptor;
|
||||
_hardlinkFileService = hardlinkFileService;
|
||||
_cacheOptions = new MemoryCacheEntryOptions()
|
||||
.SetSlidingExpiration(StaticConfiguration.TriggerValue + Constants.CacheLimitBuffer);
|
||||
}
|
||||
@@ -74,11 +78,17 @@ public abstract class DownloadService : IDownloadService
|
||||
public abstract Task DeleteDownload(string hash);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public abstract Task<List<object>?> GetAllDownloadsToBeCleaned(List<Category> categories);
|
||||
public abstract Task<List<object>?> GetDownloadsToBeCleaned(List<CleanCategory> categories);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public abstract Task CleanDownloads(List<object> downloads, List<Category> categoriesToClean, HashSet<string> excludedHashes);
|
||||
public abstract Task<List<object>?> GetDownloadsToChangeCategory(List<string> categories);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public abstract Task CleanDownloads(List<object> downloads, List<CleanCategory> categoriesToClean, HashSet<string> excludedHashes);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public abstract Task ChangeCategoryForNoHardlinksAsync(List<object> downloads, HashSet<string> excludedHashes);
|
||||
|
||||
protected void ResetStrikesOnProgress(string hash, long downloaded)
|
||||
{
|
||||
if (!_queueCleanerConfig.StalledResetStrikesOnProgress)
|
||||
@@ -107,7 +117,7 @@ public abstract class DownloadService : IDownloadService
|
||||
return await _striker.StrikeAndCheckLimit(hash, itemName, _queueCleanerConfig.StalledMaxStrikes, StrikeType.Stalled);
|
||||
}
|
||||
|
||||
protected SeedingCheckResult ShouldCleanDownload(double ratio, TimeSpan seedingTime, Category category)
|
||||
protected SeedingCheckResult ShouldCleanDownload(double ratio, TimeSpan seedingTime, CleanCategory category)
|
||||
{
|
||||
// check ratio
|
||||
if (DownloadReachedRatio(ratio, seedingTime, category))
|
||||
@@ -132,7 +142,7 @@ public abstract class DownloadService : IDownloadService
|
||||
return new();
|
||||
}
|
||||
|
||||
private bool DownloadReachedRatio(double ratio, TimeSpan seedingTime, Category category)
|
||||
private bool DownloadReachedRatio(double ratio, TimeSpan seedingTime, CleanCategory category)
|
||||
{
|
||||
if (category.MaxRatio < 0)
|
||||
{
|
||||
@@ -158,7 +168,7 @@ public abstract class DownloadService : IDownloadService
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool DownloadReachedMaxSeedTime(TimeSpan seedingTime, Category category)
|
||||
private bool DownloadReachedMaxSeedTime(TimeSpan seedingTime, CleanCategory category)
|
||||
{
|
||||
if (category.MaxSeedTime < 0)
|
||||
{
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Text.RegularExpressions;
|
||||
using Common.Configuration.ContentBlocker;
|
||||
using Common.Configuration.DownloadCleaner;
|
||||
using Common.Configuration.QueueCleaner;
|
||||
using Infrastructure.Interceptors;
|
||||
using Infrastructure.Verticals.ContentBlocker;
|
||||
using Infrastructure.Verticals.Files;
|
||||
using Infrastructure.Verticals.ItemStriker;
|
||||
using Infrastructure.Verticals.Notifications;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
@@ -15,7 +16,21 @@ namespace Infrastructure.Verticals.DownloadClient;
|
||||
|
||||
public class DummyDownloadService : DownloadService
|
||||
{
|
||||
public DummyDownloadService(ILogger<DownloadService> logger, IOptions<QueueCleanerConfig> queueCleanerConfig, IOptions<ContentBlockerConfig> contentBlockerConfig, IOptions<DownloadCleanerConfig> downloadCleanerConfig, IMemoryCache cache, IFilenameEvaluator filenameEvaluator, IStriker striker, INotificationPublisher notifier, IDryRunInterceptor dryRunInterceptor) : base(logger, queueCleanerConfig, contentBlockerConfig, downloadCleanerConfig, cache, filenameEvaluator, striker, notifier, dryRunInterceptor)
|
||||
public DummyDownloadService(
|
||||
ILogger<DownloadService> logger,
|
||||
IOptions<QueueCleanerConfig> queueCleanerConfig,
|
||||
IOptions<ContentBlockerConfig> contentBlockerConfig,
|
||||
IOptions<DownloadCleanerConfig> downloadCleanerConfig,
|
||||
IMemoryCache cache,
|
||||
IFilenameEvaluator filenameEvaluator,
|
||||
IStriker striker,
|
||||
NotificationPublisher notifier,
|
||||
IDryRunInterceptor dryRunInterceptor,
|
||||
IHardlinkFileService hardlinkFileService
|
||||
) : base(
|
||||
logger, queueCleanerConfig, contentBlockerConfig, downloadCleanerConfig,
|
||||
cache, filenameEvaluator, striker, notifier, dryRunInterceptor, hardlinkFileService
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -38,12 +53,22 @@ public class DummyDownloadService : DownloadService
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override Task<List<object>?> GetAllDownloadsToBeCleaned(List<Category> categories)
|
||||
public override Task<List<object>?> GetDownloadsToBeCleaned(List<CleanCategory> categories)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override Task CleanDownloads(List<object> downloads, List<Category> categoriesToClean, HashSet<string> excludedHashes)
|
||||
public override Task<List<object>?> GetDownloadsToChangeCategory(List<string> categories)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override Task CleanDownloads(List<object> downloads, List<CleanCategory> categoriesToClean, HashSet<string> excludedHashes)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override Task ChangeCategoryForNoHardlinksAsync(List<object> downloads, HashSet<string> excludedHashes)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
@@ -36,16 +36,25 @@ public interface IDownloadService : IDisposable
|
||||
/// </summary>
|
||||
/// <param name="categories">The categories by which to filter the downloads.</param>
|
||||
/// <returns>A list of downloads for the provided categories.</returns>
|
||||
Task<List<object>?> GetAllDownloadsToBeCleaned(List<Category> categories);
|
||||
Task<List<object>?> GetDownloadsToBeCleaned(List<CleanCategory> categories);
|
||||
|
||||
Task<List<object>?> GetDownloadsToChangeCategory(List<string> categories);
|
||||
|
||||
/// <summary>
|
||||
/// Cleans the downloads.
|
||||
/// </summary>
|
||||
/// <param name="downloads"></param>
|
||||
/// <param name="downloads">The downloads to clean.</param>
|
||||
/// <param name="categoriesToClean">The categories that should be cleaned.</param>
|
||||
/// <param name="excludedHashes">The hashes that should not be cleaned.</param>
|
||||
public abstract Task CleanDownloads(List<object> downloads, List<Category> categoriesToClean, HashSet<string> excludedHashes);
|
||||
Task CleanDownloads(List<object> downloads, List<CleanCategory> categoriesToClean, HashSet<string> excludedHashes);
|
||||
|
||||
/// <summary>
|
||||
/// Changes the category for downloads that have no hardlinks.
|
||||
/// </summary>
|
||||
/// <param name="downloads">The downloads to change.</param>
|
||||
/// <param name="excludedHashes"></param>
|
||||
Task ChangeCategoryForNoHardlinksAsync(List<object> downloads, HashSet<string> excludedHashes);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a download item.
|
||||
/// </summary>
|
||||
|
||||
@@ -10,13 +10,13 @@ using Domain.Enums;
|
||||
using Infrastructure.Interceptors;
|
||||
using Infrastructure.Verticals.ContentBlocker;
|
||||
using Infrastructure.Verticals.Context;
|
||||
using Infrastructure.Verticals.Files;
|
||||
using Infrastructure.Verticals.ItemStriker;
|
||||
using Infrastructure.Verticals.Notifications;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using QBittorrent.Client;
|
||||
using Category = Common.Configuration.DownloadCleaner.Category;
|
||||
|
||||
namespace Infrastructure.Verticals.DownloadClient.QBittorrent;
|
||||
|
||||
@@ -36,10 +36,11 @@ public class QBitService : DownloadService, IQBitService
|
||||
IFilenameEvaluator filenameEvaluator,
|
||||
IStriker striker,
|
||||
INotificationPublisher notifier,
|
||||
IDryRunInterceptor dryRunInterceptor
|
||||
IDryRunInterceptor dryRunInterceptor,
|
||||
IHardlinkFileService hardlinkFileService
|
||||
) : base(
|
||||
logger, queueCleanerConfig, contentBlockerConfig, downloadCleanerConfig, cache,
|
||||
filenameEvaluator, striker, notifier, dryRunInterceptor
|
||||
filenameEvaluator, striker, notifier, dryRunInterceptor, hardlinkFileService
|
||||
)
|
||||
{
|
||||
_config = config.Value;
|
||||
@@ -207,7 +208,7 @@ public class QBitService : DownloadService, IQBitService
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<List<object>?> GetAllDownloadsToBeCleaned(List<Category> categories) =>
|
||||
public override async Task<List<object>?> GetDownloadsToBeCleaned(List<CleanCategory> categories) =>
|
||||
(await _client.GetTorrentListAsync(new()
|
||||
{
|
||||
Filter = TorrentListFilter.Seeding
|
||||
@@ -217,8 +218,19 @@ public class QBitService : DownloadService, IQBitService
|
||||
.Cast<object>()
|
||||
.ToList();
|
||||
|
||||
public override async Task<List<object>?> GetDownloadsToChangeCategory(List<string> categories)
|
||||
{
|
||||
return (await _client.GetTorrentListAsync(new()
|
||||
{
|
||||
Filter = TorrentListFilter.Seeding
|
||||
}))
|
||||
?.Where(x => !string.IsNullOrEmpty(x.Hash))
|
||||
.Cast<object>()
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task CleanDownloads(List<object> downloads, List<Category> categoriesToClean, HashSet<string> excludedHashes)
|
||||
public override async Task CleanDownloads(List<object> downloads, List<CleanCategory> categoriesToClean, HashSet<string> excludedHashes)
|
||||
{
|
||||
foreach (TorrentInfo download in downloads)
|
||||
{
|
||||
@@ -227,7 +239,7 @@ public class QBitService : DownloadService, IQBitService
|
||||
continue;
|
||||
}
|
||||
|
||||
Category? category = categoriesToClean
|
||||
CleanCategory? category = categoriesToClean
|
||||
.FirstOrDefault(x => download.Category.Equals(x.Name, StringComparison.InvariantCultureIgnoreCase));
|
||||
|
||||
if (category is null)
|
||||
@@ -286,6 +298,52 @@ public class QBitService : DownloadService, IQBitService
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task ChangeCategoryForNoHardlinksAsync(List<object> downloads, HashSet<string> excludedHashes)
|
||||
{
|
||||
// TODO account for cross-seed
|
||||
foreach (TorrentInfo download in downloads)
|
||||
{
|
||||
IReadOnlyList<TorrentContent>? files = await _client.GetTorrentContentsAsync(download.Hash);
|
||||
|
||||
if (files is null)
|
||||
{
|
||||
_logger.LogDebug("failed to find files for {name}", download.Name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (excludedHashes.Any(x => x.Equals(download.Hash, StringComparison.InvariantCultureIgnoreCase)))
|
||||
{
|
||||
_logger.LogDebug("skip | download is used by an arr | {name}", download.Name);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool hasHardlinks = false;
|
||||
|
||||
foreach (TorrentContent file in files)
|
||||
{
|
||||
if (!file.Index.HasValue)
|
||||
{
|
||||
_logger.LogDebug("skip | file index is null for {name}", download.Name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_hardlinkFileService.GetHardLinkCount(file.Name) > 1)
|
||||
{
|
||||
hasHardlinks = true;
|
||||
};
|
||||
}
|
||||
|
||||
if (hasHardlinks)
|
||||
{
|
||||
_logger.LogDebug("skip | download has hardlinks | {name}", download.Name);
|
||||
continue;
|
||||
}
|
||||
|
||||
await ((QBitService)Proxy).ChangeCategory(download.Hash, _downloadCleanerConfig.NoHardlinksCategory);
|
||||
await _notifier.NotifyCategoryChanged(download.Category, _downloadCleanerConfig.NoHardlinksCategory);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[DryRunSafeguard]
|
||||
public override async Task DeleteDownload(string hash)
|
||||
@@ -299,6 +357,12 @@ public class QBitService : DownloadService, IQBitService
|
||||
await _client.SetFilePriorityAsync(hash, fileIndex, TorrentContentPriority.Skip);
|
||||
}
|
||||
|
||||
[DryRunSafeguard]
|
||||
protected virtual async Task ChangeCategory(string hash, string newCategory)
|
||||
{
|
||||
await _client.SetTorrentCategoryAsync([hash], newCategory);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
_client.Dispose();
|
||||
|
||||
@@ -10,6 +10,7 @@ using Domain.Enums;
|
||||
using Infrastructure.Interceptors;
|
||||
using Infrastructure.Verticals.ContentBlocker;
|
||||
using Infrastructure.Verticals.Context;
|
||||
using Infrastructure.Verticals.Files;
|
||||
using Infrastructure.Verticals.ItemStriker;
|
||||
using Infrastructure.Verticals.Notifications;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
@@ -38,10 +39,11 @@ public class TransmissionService : DownloadService, ITransmissionService
|
||||
IFilenameEvaluator filenameEvaluator,
|
||||
IStriker striker,
|
||||
INotificationPublisher notifier,
|
||||
IDryRunInterceptor dryRunInterceptor
|
||||
IDryRunInterceptor dryRunInterceptor,
|
||||
IHardlinkFileService hardlinkFileService
|
||||
) : base(
|
||||
logger, queueCleanerConfig, contentBlockerConfig, downloadCleanerConfig, cache,
|
||||
filenameEvaluator, striker, notifier, dryRunInterceptor
|
||||
filenameEvaluator, striker, notifier, dryRunInterceptor, hardlinkFileService
|
||||
)
|
||||
{
|
||||
_config = config.Value;
|
||||
@@ -181,7 +183,7 @@ public class TransmissionService : DownloadService, ITransmissionService
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<List<object>?> GetAllDownloadsToBeCleaned(List<Category> categories)
|
||||
public override async Task<List<object>?> GetDownloadsToBeCleaned(List<CleanCategory> categories)
|
||||
{
|
||||
string[] fields = [
|
||||
TorrentFields.FILES,
|
||||
@@ -218,8 +220,13 @@ public class TransmissionService : DownloadService, ITransmissionService
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public override Task<List<object>?> GetDownloadsToChangeCategory(List<string> categories)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task CleanDownloads(List<object> downloads, List<Category> categoriesToClean, HashSet<string> excludedHashes)
|
||||
public override async Task CleanDownloads(List<object> downloads, List<CleanCategory> categoriesToClean, HashSet<string> excludedHashes)
|
||||
{
|
||||
foreach (TorrentInfo download in downloads)
|
||||
{
|
||||
@@ -228,7 +235,7 @@ public class TransmissionService : DownloadService, ITransmissionService
|
||||
continue;
|
||||
}
|
||||
|
||||
Category? category = categoriesToClean
|
||||
CleanCategory? category = categoriesToClean
|
||||
.FirstOrDefault(x =>
|
||||
{
|
||||
if (download.DownloadDir is null)
|
||||
@@ -281,7 +288,12 @@ public class TransmissionService : DownloadService, ITransmissionService
|
||||
await _notifier.NotifyDownloadCleaned(download.uploadRatio ?? 0, seedingTime, category.Name, result.Reason);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override Task ChangeCategoryForNoHardlinksAsync(List<object> downloads, HashSet<string> excludedHashes)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override async Task DeleteDownload(string hash)
|
||||
{
|
||||
TorrentInfo? torrent = await GetTorrentAsync(hash);
|
||||
|
||||
Reference in New Issue
Block a user