Merge pull request #947 from AlexandreVassard/main

fix(symlink): strip duplicate torrent name from subpath
This commit is contained in:
Roger Far 2026-03-14 13:26:56 -06:00 committed by GitHub
commit 095a25f04a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 129 additions and 2 deletions

View file

@ -259,6 +259,95 @@ public class DownloadHelperTest
Assert.Equal(expectedPath, path);
}
[Fact]
public void GetDownloadPath_WithPath_WhenFilePathStartsWithTorrentName_StripsPrefix()
{
// Arrange
var download = new Download
{
Link = "https://fake.url/file.txt",
FileName = "file.txt"
};
String fileRelativePath;
if (OSHelper.IsLinux)
{
fileRelativePath = "Torrent Name/Saison 1/file.txt";
}
else
{
fileRelativePath = @"Torrent Name\Saison 1\file.txt";
}
IList<DebridClientFile> files =
[
new()
{
Path = fileRelativePath
}
];
var torrent = new Torrent
{
RdName = "Torrent Name",
RdFiles = JsonSerializer.Serialize(files)
};
var fileSystem = new MockFileSystem();
// Act
var path = DownloadHelper.GetDownloadPath("/data/downloads", torrent, download, fileSystem);
// Assert
// The torrent name prefix in the file path should not duplicate the torrent name in the base dir
var expectedPath = Path.Combine("/data/downloads", "Torrent Name", "Saison 1", "file.txt");
Assert.Equal(expectedPath, path);
}
[Fact]
public void GetDownloadPath_WithoutPath_WhenFilePathStartsWithTorrentName_StripsPrefix()
{
// Arrange
var download = new Download
{
Link = "https://fake.url/file.txt",
FileName = "file.txt"
};
String fileRelativePath;
if (OSHelper.IsLinux)
{
fileRelativePath = "Torrent Name/Saison 1/file.txt";
}
else
{
fileRelativePath = @"Torrent Name\Saison 1\file.txt";
}
IList<DebridClientFile> files =
[
new()
{
Path = fileRelativePath
}
];
var torrent = new Torrent
{
RdName = "Torrent Name",
RdFiles = JsonSerializer.Serialize(files)
};
// Act
var path = DownloadHelper.GetDownloadPath(torrent, download);
// Assert
var expectedPath = Path.Combine("Torrent Name", "Saison 1", "file.txt");
Assert.Equal(expectedPath, path);
}
// This is probably a bug
[Fact]
public void GetDownloadPath_WithPath_WhenNoUriSegmentsOrFileName_ReturnsTorrentDirectory()

View file

@ -38,7 +38,12 @@ public static class DownloadHelper
{
subPath = subPath.Trim('/').Trim('\\');
torrentPath = Path.Combine(torrentPath, subPath);
subPath = StripTorrentNamePrefix(subPath, directory);
if (!String.IsNullOrWhiteSpace(subPath))
{
torrentPath = Path.Combine(torrentPath, subPath);
}
}
}
@ -87,7 +92,12 @@ public static class DownloadHelper
{
subPath = subPath.Trim('/').Trim('\\');
torrentPath = Path.Combine(torrentPath, subPath);
subPath = StripTorrentNamePrefix(subPath, torrentPath);
if (!String.IsNullOrWhiteSpace(subPath))
{
torrentPath = Path.Combine(torrentPath, subPath);
}
}
}
@ -117,4 +127,32 @@ public static class DownloadHelper
{
return String.Concat(path.Split(Path.GetInvalidPathChars()));
}
private static String StripTorrentNamePrefix(String subPath, String torrentName)
{
var separatorIndex = subPath.IndexOfAny(['/', '\\']);
String firstComponent;
String remainder;
if (separatorIndex < 0)
{
firstComponent = subPath;
remainder = String.Empty;
}
else
{
firstComponent = subPath[..separatorIndex];
remainder = subPath[(separatorIndex + 1)..];
}
var normalizedFirst = RemoveInvalidPathChars(firstComponent);
if (normalizedFirst.Equals(torrentName.TrimEnd('/', '\\'), StringComparison.OrdinalIgnoreCase))
{
return remainder;
}
return subPath;
}
}