mirror of
https://github.com/Cleanuparr/Cleanuparr.git
synced 2026-05-24 22:44:39 -04:00
Fix rTorrent path evaluation for items without a subdirectory (#548)
This commit is contained in:
@@ -61,10 +61,17 @@ public sealed record RTorrentTorrent
|
||||
public string? Label { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Base path where the torrent data is stored
|
||||
/// Base path where the torrent data is stored.
|
||||
/// For multi-file torrents this is the torrent directory; for single-file torrents this is the full file path.
|
||||
/// </summary>
|
||||
public string? BasePath { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Directory containing the torrent data (from d.directory).
|
||||
/// Unlike BasePath, this always points to a directory for both single-file and multi-file torrents.
|
||||
/// </summary>
|
||||
public string? Directory { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// List of tracker URLs for this torrent
|
||||
/// </summary>
|
||||
|
||||
@@ -660,6 +660,95 @@ public class RTorrentServiceDCTests : IClassFixture<RTorrentServiceFixture>
|
||||
Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UsesDirectoryOverBasePathForFilePath()
|
||||
{
|
||||
// Arrange
|
||||
var sut = _fixture.CreateSut();
|
||||
|
||||
var unlinkedConfig = new UnlinkedConfig
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TargetCategory = "unlinked"
|
||||
};
|
||||
|
||||
// Single-file torrent: BasePath is the full file path, Directory is the containing dir
|
||||
var downloads = new List<ITorrentItemWrapper>
|
||||
{
|
||||
new RTorrentItemWrapper(new RTorrentTorrent
|
||||
{
|
||||
Hash = "HASH1", Name = "movie.mkv", Label = "movies",
|
||||
BasePath = "/downloads/movie.mkv",
|
||||
Directory = "/downloads"
|
||||
})
|
||||
};
|
||||
|
||||
_fixture.ClientWrapper
|
||||
.Setup(x => x.GetTorrentFilesAsync("HASH1"))
|
||||
.ReturnsAsync(new List<RTorrentFile>
|
||||
{
|
||||
new RTorrentFile { Index = 0, Path = "movie.mkv", Priority = 1 }
|
||||
});
|
||||
|
||||
_fixture.HardLinkFileService
|
||||
.Setup(x => x.GetHardLinkCount(It.IsAny<string>(), It.IsAny<bool>()))
|
||||
.Returns(0);
|
||||
|
||||
// Act
|
||||
await sut.ChangeCategoryForNoHardLinksAsync(downloads, unlinkedConfig);
|
||||
|
||||
// Assert - path should use Directory (/downloads), not BasePath (/downloads/movie.mkv)
|
||||
var expectedPath = string.Join(Path.DirectorySeparatorChar,
|
||||
Path.Combine("/downloads", "movie.mkv").Split('\\', '/'));
|
||||
_fixture.HardLinkFileService.Verify(
|
||||
x => x.GetHardLinkCount(expectedPath, false),
|
||||
Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FallsBackToBasePathWhenDirectoryNull()
|
||||
{
|
||||
// Arrange
|
||||
var sut = _fixture.CreateSut();
|
||||
|
||||
var unlinkedConfig = new UnlinkedConfig
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TargetCategory = "unlinked"
|
||||
};
|
||||
|
||||
var downloads = new List<ITorrentItemWrapper>
|
||||
{
|
||||
new RTorrentItemWrapper(new RTorrentTorrent
|
||||
{
|
||||
Hash = "HASH1", Name = "Test", Label = "movies",
|
||||
BasePath = "/downloads",
|
||||
Directory = null
|
||||
})
|
||||
};
|
||||
|
||||
_fixture.ClientWrapper
|
||||
.Setup(x => x.GetTorrentFilesAsync("HASH1"))
|
||||
.ReturnsAsync(new List<RTorrentFile>
|
||||
{
|
||||
new RTorrentFile { Index = 0, Path = "file1.mkv", Priority = 1 }
|
||||
});
|
||||
|
||||
_fixture.HardLinkFileService
|
||||
.Setup(x => x.GetHardLinkCount(It.IsAny<string>(), It.IsAny<bool>()))
|
||||
.Returns(0);
|
||||
|
||||
// Act
|
||||
await sut.ChangeCategoryForNoHardLinksAsync(downloads, unlinkedConfig);
|
||||
|
||||
// Assert - path should fall back to BasePath
|
||||
var expectedPath = string.Join(Path.DirectorySeparatorChar,
|
||||
Path.Combine("/downloads", "file1.mkv").Split('\\', '/'));
|
||||
_fixture.HardLinkFileService.Verify(
|
||||
x => x.GetHardLinkCount(expectedPath, false),
|
||||
Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdatesCategoryOnWrapper()
|
||||
{
|
||||
|
||||
@@ -29,7 +29,8 @@ public sealed class RTorrentClient
|
||||
"d.complete=",
|
||||
"d.timestamp.finished=",
|
||||
"d.custom1=",
|
||||
"d.base_path="
|
||||
"d.base_path=",
|
||||
"d.directory="
|
||||
];
|
||||
|
||||
// Fields to request when fetching file data via f.multicall
|
||||
@@ -327,7 +328,7 @@ public sealed class RTorrentClient
|
||||
|
||||
private static RTorrentTorrent? CreateTorrentFromValues(object?[] values)
|
||||
{
|
||||
if (values.Length < 12) return null;
|
||||
if (values.Length < 13) return null;
|
||||
|
||||
return new RTorrentTorrent
|
||||
{
|
||||
@@ -342,7 +343,8 @@ public sealed class RTorrentClient
|
||||
Complete = Convert.ToInt32(values[8] ?? 0),
|
||||
TimestampFinished = Convert.ToInt64(values[9] ?? 0),
|
||||
Label = values[10]?.ToString(),
|
||||
BasePath = values[11]?.ToString()
|
||||
BasePath = values[11]?.ToString(),
|
||||
Directory = values[12]?.ToString()
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ public partial class RTorrentService
|
||||
foreach (var file in files)
|
||||
{
|
||||
string filePath = string.Join(Path.DirectorySeparatorChar,
|
||||
Path.Combine(torrent.Info.BasePath ?? "", file.Path).Split(['\\', '/']));
|
||||
Path.Combine(torrent.Info.Directory ?? torrent.Info.BasePath ?? "", file.Path).Split(['\\', '/']));
|
||||
|
||||
filePath = PathHelper.RemapPath(filePath, unlinkedConfig.DownloadDirectorySource, unlinkedConfig.DownloadDirectoryTarget);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user