added implementation for Deluge and Transmission
This commit is contained in:
@@ -280,9 +280,110 @@ public class DelugeService : DownloadService, IDelugeService
|
|||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Task ChangeCategoryForNoHardLinksAsync(List<object>? downloads, HashSet<string> excludedHashes)
|
public override async Task ChangeCategoryForNoHardLinksAsync(List<object>? downloads, HashSet<string> excludedHashes)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
if (downloads?.Count is null or 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_downloadCleanerConfig.NoHardLinksIgnoreRootDir)
|
||||||
|
{
|
||||||
|
downloads
|
||||||
|
.Cast<TorrentStatus>()
|
||||||
|
.Select(x =>
|
||||||
|
{
|
||||||
|
string? firstDir = GetRootWithFirstDirectory(x.DownloadPath);
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(firstDir))
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (firstDir == Path.GetPathRoot(x.DownloadPath))
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
return firstDir;
|
||||||
|
})
|
||||||
|
.Where(x => !string.IsNullOrEmpty(x))
|
||||||
|
.Distinct()
|
||||||
|
.ToList()
|
||||||
|
.ForEach(x =>
|
||||||
|
{
|
||||||
|
_logger.LogTrace("populating file counts from {dir}", x);
|
||||||
|
|
||||||
|
if (!Directory.Exists(x))
|
||||||
|
{
|
||||||
|
throw new ValidationException($"directory \"{x}\" does not exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
_hardLinkFileService.PopulateFileCounts(x);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (TorrentStatus download in downloads.Cast<TorrentStatus>())
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(download.Hash))
|
||||||
|
{
|
||||||
|
_logger.LogDebug("skip | download hash is null for {name}", download.Name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (excludedHashes.Any(x => x.Equals(download.Hash, StringComparison.InvariantCultureIgnoreCase)))
|
||||||
|
{
|
||||||
|
_logger.LogDebug("skip | download is used by an arr | {name}", download.Name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ContextProvider.Set("downloadName", download.Name);
|
||||||
|
ContextProvider.Set("hash", download.Hash);
|
||||||
|
|
||||||
|
DelugeContents? contents = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
contents = await _client.GetTorrentFiles(download.Hash);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
_logger.LogDebug(exception, "failed to find torrent files for {name}", download.Name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasHardlinks = false;
|
||||||
|
|
||||||
|
ProcessFiles(contents?.Contents, (name, file) =>
|
||||||
|
{
|
||||||
|
string filePath = Path.Combine(download.DownloadPath, file.Path);
|
||||||
|
|
||||||
|
long hardlinkCount = _hardLinkFileService.GetHardLinkCount(filePath, _downloadCleanerConfig.NoHardLinksIgnoreRootDir);
|
||||||
|
|
||||||
|
if (hardlinkCount < 0)
|
||||||
|
{
|
||||||
|
_logger.LogDebug("skip | could not get file properties | {name}", download.Name);
|
||||||
|
hasHardlinks = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hardlinkCount > 0)
|
||||||
|
{
|
||||||
|
hasHardlinks = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hasHardlinks)
|
||||||
|
{
|
||||||
|
_logger.LogDebug("skip | download has hardlinks | {name}", download.Name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _dryRunInterceptor.InterceptAsync(ChangeLabel, download.Hash, _downloadCleanerConfig.NoHardLinksCategory);
|
||||||
|
|
||||||
|
_logger.LogInformation("category changed for {name}", download.Name);
|
||||||
|
|
||||||
|
await _notifier.NotifyCategoryChanged(download.Label, _downloadCleanerConfig.NoHardLinksCategory);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -300,6 +401,12 @@ public class DelugeService : DownloadService, IDelugeService
|
|||||||
await _client.ChangeFilesPriority(hash, sortedPriorities);
|
await _client.ChangeFilesPriority(hash, sortedPriorities);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[DryRunSafeguard]
|
||||||
|
protected virtual async Task ChangeLabel(string hash, string newLabel)
|
||||||
|
{
|
||||||
|
await _client.SetTorrentLabel(hash, newLabel);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<bool> IsItemStuckAndShouldRemove(TorrentStatus status)
|
private async Task<bool> IsItemStuckAndShouldRemove(TorrentStatus status)
|
||||||
{
|
{
|
||||||
if (_queueCleanerConfig.StalledMaxStrikes is 0)
|
if (_queueCleanerConfig.StalledMaxStrikes is 0)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using Common.Configuration.ContentBlocker;
|
|||||||
using Common.Configuration.DownloadCleaner;
|
using Common.Configuration.DownloadCleaner;
|
||||||
using Common.Configuration.DownloadClient;
|
using Common.Configuration.DownloadClient;
|
||||||
using Common.Configuration.QueueCleaner;
|
using Common.Configuration.QueueCleaner;
|
||||||
|
using Common.Exceptions;
|
||||||
using Common.Helpers;
|
using Common.Helpers;
|
||||||
using Domain.Enums;
|
using Domain.Enums;
|
||||||
using Infrastructure.Interceptors;
|
using Infrastructure.Interceptors;
|
||||||
@@ -304,9 +305,128 @@ public class TransmissionService : DownloadService, ITransmissionService
|
|||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Task ChangeCategoryForNoHardLinksAsync(List<object>? downloads, HashSet<string> excludedHashes)
|
public override async Task ChangeCategoryForNoHardLinksAsync(List<object>? downloads, HashSet<string> excludedHashes)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
if (downloads?.Count is null or 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_downloadCleanerConfig.NoHardLinksIgnoreRootDir)
|
||||||
|
{
|
||||||
|
downloads
|
||||||
|
.Cast<TorrentInfo>()
|
||||||
|
.Select(x =>
|
||||||
|
{
|
||||||
|
if (x.DownloadDir == null)
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
string? firstDir = GetRootWithFirstDirectory(x.DownloadDir);
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(firstDir))
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (firstDir == Path.GetPathRoot(x.DownloadDir))
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
return firstDir;
|
||||||
|
})
|
||||||
|
.Where(x => !string.IsNullOrEmpty(x))
|
||||||
|
.Distinct()
|
||||||
|
.ToList()
|
||||||
|
.ForEach(x =>
|
||||||
|
{
|
||||||
|
_logger.LogTrace("populating file counts from {dir}", x);
|
||||||
|
|
||||||
|
if (!Directory.Exists(x))
|
||||||
|
{
|
||||||
|
throw new ValidationException($"directory \"{x}\" does not exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
_hardLinkFileService.PopulateFileCounts(x);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (TorrentInfo download in downloads.Cast<TorrentInfo>())
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(download.HashString) || download.DownloadDir == null)
|
||||||
|
{
|
||||||
|
_logger.LogDebug("skip | download hash or download directory is null for {name}", download.Name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (excludedHashes.Any(x => x.Equals(download.HashString, StringComparison.InvariantCultureIgnoreCase)))
|
||||||
|
{
|
||||||
|
_logger.LogDebug("skip | download is used by an arr | {name}", download.Name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ContextProvider.Set("downloadName", download.Name);
|
||||||
|
ContextProvider.Set("hash", download.HashString);
|
||||||
|
|
||||||
|
bool hasHardlinks = false;
|
||||||
|
|
||||||
|
if (download.Files != null)
|
||||||
|
{
|
||||||
|
foreach (TransmissionTorrentFiles file in download.Files)
|
||||||
|
{
|
||||||
|
string filePath = Path.Combine(download.DownloadDir, file.Name);
|
||||||
|
|
||||||
|
long hardlinkCount = _hardLinkFileService.GetHardLinkCount(filePath, _downloadCleanerConfig.NoHardLinksIgnoreRootDir);
|
||||||
|
|
||||||
|
if (hardlinkCount < 0)
|
||||||
|
{
|
||||||
|
_logger.LogDebug("skip | could not get file properties | {name}", download.Name);
|
||||||
|
hasHardlinks = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hardlinkCount > 0)
|
||||||
|
{
|
||||||
|
hasHardlinks = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasHardlinks)
|
||||||
|
{
|
||||||
|
_logger.LogDebug("skip | download has hardlinks | {name}", download.Name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the current category (directory name)
|
||||||
|
string currentCategory = Path.GetFileName(Path.TrimEndingDirectorySeparator(download.DownloadDir));
|
||||||
|
|
||||||
|
// Create the new location path
|
||||||
|
string newLocation = Path.Combine(
|
||||||
|
Path.GetDirectoryName(Path.TrimEndingDirectorySeparator(download.DownloadDir)) ?? string.Empty,
|
||||||
|
_downloadCleanerConfig.NoHardLinksCategory
|
||||||
|
);
|
||||||
|
|
||||||
|
await _dryRunInterceptor.InterceptAsync(MoveDownload, download.Id, newLocation);
|
||||||
|
|
||||||
|
_logger.LogInformation("category changed for {name}", download.Name);
|
||||||
|
|
||||||
|
await _notifier.NotifyCategoryChanged(currentCategory, _downloadCleanerConfig.NoHardLinksCategory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[DryRunSafeguard]
|
||||||
|
protected virtual async Task MoveDownload(long downloadId, string newLocation)
|
||||||
|
{
|
||||||
|
await _client.TorrentSetAsync(new TorrentSettings
|
||||||
|
{
|
||||||
|
Ids = [downloadId],
|
||||||
|
Location = newLocation,
|
||||||
|
// Move = true
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task DeleteDownload(string hash)
|
public override async Task DeleteDownload(string hash)
|
||||||
|
|||||||
Reference in New Issue
Block a user