diff --git a/server/RdtClient.Service.Test/Helpers/DownloadHelperTest.cs b/server/RdtClient.Service.Test/Helpers/DownloadHelperTest.cs index 1fac48f..90ba064 100644 --- a/server/RdtClient.Service.Test/Helpers/DownloadHelperTest.cs +++ b/server/RdtClient.Service.Test/Helpers/DownloadHelperTest.cs @@ -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 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 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() diff --git a/server/RdtClient.Service/Helpers/DownloadHelper.cs b/server/RdtClient.Service/Helpers/DownloadHelper.cs index 36c20cd..bc5b578 100644 --- a/server/RdtClient.Service/Helpers/DownloadHelper.cs +++ b/server/RdtClient.Service/Helpers/DownloadHelper.cs @@ -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; + } }