renamed vars; added root dir; fixed root dir file counts; fixed qbit flow

This commit is contained in:
Flaminel
2025-03-24 18:05:50 +02:00
parent a83809eef7
commit 4a1e0f6896
15 changed files with 306 additions and 306 deletions
@@ -193,4 +193,10 @@ public sealed class DelugeClient
return webResponse.Result;
}
public async Task SetTorrentLabel(string hash, string newLabel)
{
// TODO
throw new NotImplementedException();
}
}
@@ -301,110 +301,112 @@ public class DelugeService : DownloadService, IDelugeService
throw new NotImplementedException();
}
public override async Task ChangeCategoryForNoHardLinksAsync(List<object>? downloads, HashSet<string> excludedHashes)
public override async Task ChangeCategoryForNoHardLinksAsync(List<object>? downloads, HashSet<string> excludedHashes, IReadOnlyList<string> ignoredDownloads)
{
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;
}
throw new NotImplementedException();
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);
}
// 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/>
@@ -88,7 +88,7 @@ public abstract class DownloadService : IDownloadService
public abstract Task CleanDownloadsAsync(List<object>? downloads, List<CleanCategory> categoriesToClean, HashSet<string> excludedHashes, IReadOnlyList<string> ignoredDownloads);
/// <inheritdoc/>
public abstract Task ChangeCategoryForNoHardLinksAsync(List<object>? downloads, HashSet<string> excludedHashes);
public abstract Task ChangeCategoryForNoHardLinksAsync(List<object>? downloads, HashSet<string> excludedHashes, IReadOnlyList<string> ignoredDownloads);
/// <inheritdoc/>
public abstract Task CreateCategoryAsync(string name);
@@ -74,7 +74,7 @@ public class DummyDownloadService : DownloadService
throw new NotImplementedException();
}
public override Task ChangeCategoryForNoHardLinksAsync(List<object>? downloads, HashSet<string> excludedHashes)
public override Task ChangeCategoryForNoHardLinksAsync(List<object>? downloads, HashSet<string> excludedHashes, IReadOnlyList<string> ignoredDownloads)
{
throw new NotImplementedException();
}
@@ -61,14 +61,16 @@ public interface IDownloadService : IDisposable
/// <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>
/// <param name="ignoredDownloads">The downloads to ignore from processing.</param>
Task CleanDownloadsAsync(List<object>? downloads, List<CleanCategory> categoriesToClean, HashSet<string> excludedHashes, IReadOnlyList<string> ignoredDownloads);
/// <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);
/// <param name="excludedHashes">The hashes that should not be cleaned.</param>
/// <param name="ignoredDownloads">The downloads to ignore from processing.</param>
Task ChangeCategoryForNoHardLinksAsync(List<object>? downloads, HashSet<string> excludedHashes, IReadOnlyList<string> ignoredDownloads);
/// <summary>
/// Deletes a download item.
@@ -271,10 +271,16 @@ public class QBitService : DownloadService, IQBitService
continue;
}
if (excludedHashes.Any(x => x.Equals(download.Hash, StringComparison.InvariantCultureIgnoreCase)))
{
_logger.LogDebug("skip | download is used by an arr | {name}", download.Name);
continue;
}
IReadOnlyList<TorrentTracker> trackers = await GetTrackersAsync(download.Hash);
if (ignoredDownloads.Count > 0 &&
(download.ShouldIgnore(ignoredDownloads) || trackers.Any(x => x.ShouldIgnore(ignoredDownloads)) is true))
(download.ShouldIgnore(ignoredDownloads) || trackers.Any(x => x.ShouldIgnore(ignoredDownloads))))
{
_logger.LogInformation("skip | download is ignored | {name}", download.Name);
continue;
@@ -288,12 +294,6 @@ public class QBitService : DownloadService, IQBitService
continue;
}
if (excludedHashes.Any(x => x.Equals(download.Hash, StringComparison.InvariantCultureIgnoreCase)))
{
_logger.LogDebug("skip | download is used by an arr | {name}", download.Name);
continue;
}
if (!_downloadCleanerConfig.DeletePrivate)
{
TorrentProperties? torrentProperties = await _client.GetTorrentPropertiesAsync(download.Hash);
@@ -351,57 +351,23 @@ public class QBitService : DownloadService, IQBitService
await _client.AddCategoryAsync(name);
}
public override async Task ChangeCategoryForNoHardLinksAsync(List<object>? downloads, HashSet<string> excludedHashes)
public override async Task ChangeCategoryForNoHardLinksAsync(List<object>? downloads, HashSet<string> excludedHashes, IReadOnlyList<string> ignoredDownloads)
{
if (downloads?.Count is null or 0)
{
return;
}
if (_downloadCleanerConfig.NoHardLinksIgnoreRootDir)
if (!string.IsNullOrEmpty(_downloadCleanerConfig.UnlinkedIgnoredRootDir))
{
downloads
.Cast<TorrentInfo>()
.Select(x =>
{
string? firstDir = GetRootWithFirstDirectory(x.SavePath);
if (string.IsNullOrEmpty(firstDir))
{
return string.Empty;
}
if (firstDir == Path.GetPathRoot(x.SavePath))
{
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);
});
_hardLinkFileService.PopulateFileCounts(_downloadCleanerConfig.UnlinkedIgnoredRootDir);
}
foreach (TorrentInfo download in downloads)
{
IReadOnlyList<TorrentContent>? files = await _client.GetTorrentContentsAsync(download.Hash);
if (files is null)
if (string.IsNullOrEmpty(download.Hash))
{
_logger.LogDebug("failed to find files for {name}", download.Name);
return;
continue;
}
if (excludedHashes.Any(x => x.Equals(download.Hash, StringComparison.InvariantCultureIgnoreCase)))
@@ -409,6 +375,23 @@ public class QBitService : DownloadService, IQBitService
_logger.LogDebug("skip | download is used by an arr | {name}", download.Name);
continue;
}
IReadOnlyList<TorrentTracker> trackers = await GetTrackersAsync(download.Hash);
if (ignoredDownloads.Count > 0 &&
(download.ShouldIgnore(ignoredDownloads) || trackers.Any(x => x.ShouldIgnore(ignoredDownloads))))
{
_logger.LogInformation("skip | download is ignored | {name}", download.Name);
continue;
}
IReadOnlyList<TorrentContent>? files = await _client.GetTorrentContentsAsync(download.Hash);
if (files is null)
{
_logger.LogDebug("failed to find files for {name}", download.Name);
continue;
}
ContextProvider.Set("downloadName", download.Name);
ContextProvider.Set("hash", download.Hash);
@@ -419,20 +402,17 @@ public class QBitService : DownloadService, IQBitService
if (!file.Index.HasValue)
{
_logger.LogDebug("skip | file index is null for {name}", download.Name);
return;
hasHardlinks = true;
break;
}
var ceva = Path.Combine(download.ContentPath, file.Name);
var ceva2 = Path.Combine(download.SavePath, file.Name);
// string filePath = Path.Combine(Directory.Exists(download.ContentPath)
// ? download.ContentPath
// : download.SavePath, file.Name
// );
string filePath = string.Join(Path.DirectorySeparatorChar, Path.Combine(download.SavePath, file.Name).Split(['\\', '/'])); // TODO
string filePath = Path.Combine(Directory.Exists(download.ContentPath)
? download.ContentPath
: download.SavePath, file.Name
);
filePath = string.Join(Path.DirectorySeparatorChar, Path.Combine(download.SavePath, file.Name).Split(['\\', '/'])); // TODO
// TODO add config for root directory
long hardlinkCount = _hardLinkFileService.GetHardLinkCount(filePath, _downloadCleanerConfig.NoHardLinksIgnoreRootDir);
long hardlinkCount = _hardLinkFileService.GetHardLinkCount(filePath, !string.IsNullOrEmpty(_downloadCleanerConfig.UnlinkedIgnoredRootDir));
if (hardlinkCount < 0)
{
@@ -444,6 +424,7 @@ public class QBitService : DownloadService, IQBitService
if (hardlinkCount > 0)
{
hasHardlinks = true;
break;
}
}
@@ -453,13 +434,13 @@ public class QBitService : DownloadService, IQBitService
continue;
}
await _dryRunInterceptor.InterceptAsync(ChangeCategory, download.Hash, _downloadCleanerConfig.NoHardLinksCategory);
await _dryRunInterceptor.InterceptAsync(ChangeCategory, download.Hash, _downloadCleanerConfig.UnlinkedTargetCategory);
_logger.LogInformation("category changed for {name}", download.Name);
await _notifier.NotifyCategoryChanged(download.Category, _downloadCleanerConfig.NoHardLinksCategory);
await _notifier.NotifyCategoryChanged(download.Category, _downloadCleanerConfig.UnlinkedTargetCategory);
download.Category = _downloadCleanerConfig.NoHardLinksCategory;
download.Category = _downloadCleanerConfig.UnlinkedTargetCategory;
}
}
@@ -269,6 +269,11 @@ public class TransmissionService : DownloadService, ITransmissionService
public override async Task CleanDownloadsAsync(List<object>? downloads, List<CleanCategory> categoriesToClean,
HashSet<string> excludedHashes, IReadOnlyList<string> ignoredDownloads)
{
if (downloads?.Count is null or 0)
{
return;
}
foreach (TorrentInfo download in downloads)
{
if (string.IsNullOrEmpty(download.HashString))
@@ -341,117 +346,120 @@ public class TransmissionService : DownloadService, ITransmissionService
throw new NotImplementedException();
}
public override async Task ChangeCategoryForNoHardLinksAsync(List<object>? downloads, HashSet<string> excludedHashes)
public override async Task ChangeCategoryForNoHardLinksAsync(List<object>? downloads, HashSet<string> excludedHashes, IReadOnlyList<string> ignoredDownloads)
{
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);
});
}
// TODO ignored downloads
throw new NotImplementedException();
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);
}
// 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]