Fix rTorrent path evaluation for items without a subdirectory (#548)

This commit is contained in:
Flaminel
2026-04-05 22:11:22 +03:00
committed by GitHub
parent ef280ec398
commit 7b80e038cc
4 changed files with 103 additions and 5 deletions

View File

@@ -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>

View File

@@ -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()
{

View File

@@ -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()
};
}

View File

@@ -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);