From 612d948eba3c6c308ff6b699897ba559fdb3581d Mon Sep 17 00:00:00 2001 From: Qstick Date: Tue, 9 Jul 2019 22:05:32 -0400 Subject: [PATCH] Fixed: Some Tests and Added New Ones --- build.sh | 11 - .../DiskTests/DiskTransferServiceFixture.cs | 195 +++++++++++++++++- .../DiskTests/FreeSpaceFixtureBase.cs | 4 +- .../EnvironmentInfo/BuildInfoFixture.cs | 3 +- .../Http/HttpUriFixture.cs | 9 +- src/NzbDrone.Common.Test/WebClientTests.cs | 1 + .../Extensions/PathExtensions.cs | 1 - .../Extensions/TryParseExtensions.cs | 6 +- .../DownloadApprovedFixture.cs | 89 +++++++- .../TransmissionTests/TransmissionFixture.cs | 152 +++++++++++++- .../TransmissionFixtureBase.cs | 62 +++++- .../VuzeTests/VuzeFixture.cs | 10 +- .../CleanupOrphanedMetadataFilesFixture.cs | 3 + .../ParserTests/ReleaseGroupParserFixture.cs | 7 +- .../AcceptableSizeSpecification.cs | 12 +- .../Clients/QBittorrent/QBittorrentLabel.cs | 2 +- .../Clients/Transmission/TransmissionBase.cs | 64 ++++-- .../Transmission/TransmissionConfig.cs | 27 +++ .../Clients/Transmission/TransmissionProxy.cs | 21 +- .../Transmission/TransmissionTorrent.cs | 6 +- src/NzbDrone.Core/NzbDrone.Core.csproj | 1 + src/NzbDrone.Core/Parser/Parser.cs | 2 +- 22 files changed, 604 insertions(+), 84 deletions(-) create mode 100644 src/NzbDrone.Core/Download/Clients/Transmission/TransmissionConfig.cs diff --git a/build.sh b/build.sh index e36d4f440..be8230c40 100755 --- a/build.sh +++ b/build.sh @@ -52,15 +52,6 @@ CleanFolder() find $path -depth -empty -type d -exec rm -r "{}" \; } - - -AddJsonNet() -{ - rm $outputFolder/Newtonsoft.Json.* - cp $sourceFolder/packages/Newtonsoft.Json.*/lib/net35/*.dll $outputFolder - cp $sourceFolder/packages/Newtonsoft.Json.*/lib/net35/*.dll $outputFolder/NzbDrone.Update -} - BuildWithMSBuild() { export PATH=$msBuild:$PATH @@ -115,8 +106,6 @@ Build() CleanFolder $outputFolder false - AddJsonNet - echo "Removing Mono.Posix.dll" rm $outputFolder/Mono.Posix.dll diff --git a/src/NzbDrone.Common.Test/DiskTests/DiskTransferServiceFixture.cs b/src/NzbDrone.Common.Test/DiskTests/DiskTransferServiceFixture.cs index a2c3ca314..dc06b23ce 100644 --- a/src/NzbDrone.Common.Test/DiskTests/DiskTransferServiceFixture.cs +++ b/src/NzbDrone.Common.Test/DiskTests/DiskTransferServiceFixture.cs @@ -1,11 +1,13 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; +using FluentAssertions; using Moq; using NUnit.Framework; using NzbDrone.Common.Disk; +using NzbDrone.Common.Extensions; using NzbDrone.Test.Common; -using FluentAssertions; namespace NzbDrone.Common.Test.DiskTests { @@ -16,6 +18,7 @@ namespace NzbDrone.Common.Test.DiskTests private readonly string _targetPath = @"C:\target\my.video.mkv".AsOsAgnostic(); private readonly string _backupPath = @"C:\source\my.video.mkv.backup~".AsOsAgnostic(); private readonly string _tempTargetPath = @"C:\target\my.video.mkv.partial~".AsOsAgnostic(); + private readonly string _nfsFile = ".nfs01231232"; [SetUp] public void SetUp() @@ -237,7 +240,7 @@ namespace NzbDrone.Common.Test.DiskTests WithExistingFile(_targetPath); - Assert.Throws(() => Subject.TransferFile(_sourcePath, _targetPath, TransferMode.Move, false)); + Assert.Throws(() => Subject.TransferFile(_sourcePath, _targetPath, TransferMode.Move, false)); Mocker.GetMock() .Verify(v => v.DeleteFile(_targetPath), Times.Never()); @@ -642,6 +645,21 @@ namespace NzbDrone.Common.Test.DiskTests VerifyCopyFolder(source.FullName, destination.FullName); } + [Test] + public void CopyFolder_should_ignore_nfs_temp_file() + { + WithRealDiskProvider(); + + var source = GetFilledTempFolder(); + + File.WriteAllText(Path.Combine(source.FullName, _nfsFile), "SubFile1"); + + var destination = new DirectoryInfo(GetTempFilePath()); + + Subject.TransferFolder(source.FullName, destination.FullName, TransferMode.Copy); + + File.Exists(Path.Combine(destination.FullName, _nfsFile)).Should().BeFalse(); + } [Test] public void MoveFolder_should_move_folder() @@ -704,6 +722,26 @@ namespace NzbDrone.Common.Test.DiskTests destination.GetFileSystemInfos().Should().BeEmpty(); } + [Test] + public void MirrorFolder_should_not_remove_nfs_files() + { + WithRealDiskProvider(); + + var original = GetFilledTempFolder(); + var source = new DirectoryInfo(GetTempFilePath()); + var destination = new DirectoryInfo(GetTempFilePath()); + + source.Create(); + Subject.TransferFolder(original.FullName, destination.FullName, TransferMode.Copy); + + File.WriteAllText(Path.Combine(destination.FullName, _nfsFile), "SubFile1"); + + var count = Subject.MirrorFolder(source.FullName, destination.FullName); + + count.Should().Equals(0); + destination.GetFileSystemInfos().Should().HaveCount(1); + } + [Test] public void MirrorFolder_should_add_new_files() { @@ -721,6 +759,24 @@ namespace NzbDrone.Common.Test.DiskTests VerifyCopyFolder(original.FullName, destination.FullName); } + [Test] + public void MirrorFolder_should_ignore_nfs_temp_file() + { + WithRealDiskProvider(); + + var source = GetFilledTempFolder(); + + File.WriteAllText(Path.Combine(source.FullName, _nfsFile), "SubFile1"); + + var destination = new DirectoryInfo(GetTempFilePath()); + + var count = Subject.MirrorFolder(source.FullName, destination.FullName); + + count.Should().Equals(3); + + File.Exists(Path.Combine(destination.FullName, _nfsFile)).Should().BeFalse(); + } + [Test] public void MirrorFolder_should_not_touch_equivalent_files() { @@ -740,6 +796,75 @@ namespace NzbDrone.Common.Test.DiskTests VerifyCopyFolder(original.FullName, destination.FullName); } + [Test] + public void TransferFolder_should_use_movefolder_if_on_same_mount() + { + WithEmulatedDiskProvider(); + + var src = @"C:\Base1\TestDir1".AsOsAgnostic(); + var dst = @"C:\Base1\TestDir2".AsOsAgnostic(); + + WithMockMount(@"C:\Base1".AsOsAgnostic()); + WithExistingFile(@"C:\Base1\TestDir1\test.file.txt".AsOsAgnostic()); + + Subject.TransferFolder(src, dst, TransferMode.Move); + + Mocker.GetMock() + .Verify(v => v.MoveFolder(src, dst, false), Times.Once()); + } + + [Test] + public void TransferFolder_should_not_use_movefolder_if_on_same_mount_but_target_already_exists() + { + WithEmulatedDiskProvider(); + + var src = @"C:\Base1\TestDir1".AsOsAgnostic(); + var dst = @"C:\Base1\TestDir2".AsOsAgnostic(); + + WithMockMount(@"C:\Base1".AsOsAgnostic()); + WithExistingFile(@"C:\Base1\TestDir1\test.file.txt".AsOsAgnostic()); + WithExistingFolder(dst); + + Subject.TransferFolder(src, dst, TransferMode.Move); + + Mocker.GetMock() + .Verify(v => v.MoveFolder(src, dst, false), Times.Never()); + } + + [Test] + public void TransferFolder_should_not_use_movefolder_if_on_same_mount_but_transactional() + { + WithEmulatedDiskProvider(); + + var src = @"C:\Base1\TestDir1".AsOsAgnostic(); + var dst = @"C:\Base1\TestDir2".AsOsAgnostic(); + + WithMockMount(@"C:\Base1".AsOsAgnostic()); + WithExistingFile(@"C:\Base1\TestDir1\test.file.txt".AsOsAgnostic()); + + Subject.TransferFolder(src, dst, TransferMode.Move, DiskTransferVerificationMode.Transactional); + + Mocker.GetMock() + .Verify(v => v.MoveFolder(src, dst, false), Times.Never()); + } + + [Test] + public void TransferFolder_should_not_use_movefolder_if_on_different_mount() + { + WithEmulatedDiskProvider(); + + var src = @"C:\Base1\TestDir1".AsOsAgnostic(); + var dst = @"C:\Base2\TestDir2".AsOsAgnostic(); + + WithMockMount(@"C:\Base1".AsOsAgnostic()); + WithMockMount(@"C:\Base2".AsOsAgnostic()); + + Subject.TransferFolder(src, dst, TransferMode.Move); + + Mocker.GetMock() + .Verify(v => v.MoveFolder(src, dst, false), Times.Never()); + } + public DirectoryInfo GetFilledTempFolder() { var tempFolder = GetTempFilePath(); @@ -756,8 +881,23 @@ namespace NzbDrone.Common.Test.DiskTests return new DirectoryInfo(tempFolder); } + private void WithExistingFolder(string path, bool exists = true) + { + var dir = Path.GetDirectoryName(path); + if (exists && dir.IsNotNullOrWhiteSpace()) + WithExistingFolder(dir); + + Mocker.GetMock() + .Setup(v => v.FolderExists(path)) + .Returns(exists); + } + private void WithExistingFile(string path, bool exists = true, int size = 1000) { + var dir = Path.GetDirectoryName(path); + if (exists && dir.IsNotNullOrWhiteSpace()) + WithExistingFolder(dir); + Mocker.GetMock() .Setup(v => v.FileExists(path)) .Returns(exists); @@ -809,6 +949,45 @@ namespace NzbDrone.Common.Test.DiskTests { WithExistingFile(v, false); }); + + + Mocker.GetMock() + .Setup(v => v.FolderExists(It.IsAny())) + .Returns(false); + + Mocker.GetMock() + .Setup(v => v.CreateFolder(It.IsAny())) + .Callback((f) => + { + WithExistingFolder(f); + }); + + Mocker.GetMock() + .Setup(v => v.MoveFolder(It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((s, d, b) => + { + WithExistingFolder(s, false); + WithExistingFolder(d); + // Note: Should also deal with the files. + }); + + Mocker.GetMock() + .Setup(v => v.DeleteFolder(It.IsAny(), It.IsAny())) + .Callback((f, r) => + { + WithExistingFolder(f, false); + // Note: Should also deal with the files. + }); + + // Note: never returns anything. + Mocker.GetMock() + .Setup(v => v.GetDirectoryInfos(It.IsAny())) + .Returns(new List()); + + // Note: never returns anything. + Mocker.GetMock() + .Setup(v => v.GetFileInfos(It.IsAny())) + .Returns(new List()); } private void WithRealDiskProvider() @@ -865,6 +1044,18 @@ namespace NzbDrone.Common.Test.DiskTests .Returns(s => new FileStream(s, FileMode.Open, FileAccess.Read)); } + private void WithMockMount(string root) + { + var rootDir = root; + var mock = new Mock(); + mock.SetupGet(v => v.RootDirectory) + .Returns(rootDir); + + Mocker.GetMock() + .Setup(v => v.GetMount(It.Is(s => s.StartsWith(rootDir)))) + .Returns(mock.Object); + } + private void VerifyCopyFolder(string source, string destination) { var sourceFiles = Directory.GetFileSystemEntries(source, "*", SearchOption.AllDirectories).Select(v => v.Substring(source.Length + 1)).ToArray(); diff --git a/src/NzbDrone.Common.Test/DiskTests/FreeSpaceFixtureBase.cs b/src/NzbDrone.Common.Test/DiskTests/FreeSpaceFixtureBase.cs index 7d2d7a88f..532075f91 100644 --- a/src/NzbDrone.Common.Test/DiskTests/FreeSpaceFixtureBase.cs +++ b/src/NzbDrone.Common.Test/DiskTests/FreeSpaceFixtureBase.cs @@ -32,7 +32,7 @@ namespace NzbDrone.Common.Test.DiskTests public void should_be_able_to_check_space_on_ramdrive() { MonoOnly(); - Subject.GetAvailableSpace("/").Should().NotBe(0); + Subject.GetAvailableSpace("/run/").Should().NotBe(0); } [Ignore("Docker")] @@ -62,7 +62,7 @@ namespace NzbDrone.Common.Test.DiskTests { if (new DriveInfo(driveletter.ToString()).IsReady) continue; - + Assert.Throws(() => Subject.GetAvailableSpace(driveletter + @":\NOT_A_REAL_PATH\DOES_NOT_EXIST".AsOsAgnostic())); return; } diff --git a/src/NzbDrone.Common.Test/EnvironmentInfo/BuildInfoFixture.cs b/src/NzbDrone.Common.Test/EnvironmentInfo/BuildInfoFixture.cs index 8f931fcd5..6e6f664fd 100644 --- a/src/NzbDrone.Common.Test/EnvironmentInfo/BuildInfoFixture.cs +++ b/src/NzbDrone.Common.Test/EnvironmentInfo/BuildInfoFixture.cs @@ -10,8 +10,9 @@ namespace NzbDrone.Common.Test.EnvironmentInfo [Test] public void should_return_version() { - BuildInfo.Version.Major.Should().BeOneOf(0, 10); + BuildInfo.Version.Major.Should().BeOneOf(2, 10); } + [Test] public void should_get_branch() { diff --git a/src/NzbDrone.Common.Test/Http/HttpUriFixture.cs b/src/NzbDrone.Common.Test/Http/HttpUriFixture.cs index 099ab990f..dea6f64cd 100644 --- a/src/NzbDrone.Common.Test/Http/HttpUriFixture.cs +++ b/src/NzbDrone.Common.Test/Http/HttpUriFixture.cs @@ -7,6 +7,13 @@ namespace NzbDrone.Common.Test.Http { public class HttpUriFixture : TestBase { + [TestCase("abc://my_host.com:8080/root/api/")] + public void should_parse(string uri) + { + var newUri = new HttpUri(uri); + newUri.FullUri.Should().Be(uri); + } + [TestCase("", "", "")] [TestCase("/", "", "/")] [TestCase("base", "", "base")] @@ -77,7 +84,7 @@ namespace NzbDrone.Common.Test.Http public void should_combine_relative_path(string basePath, string relativePath, string expected) { var newUri = new HttpUri(basePath).CombinePath(relativePath); - + newUri.FullUri.Should().Be(expected); } } diff --git a/src/NzbDrone.Common.Test/WebClientTests.cs b/src/NzbDrone.Common.Test/WebClientTests.cs index f0cceff73..899fbadbd 100644 --- a/src/NzbDrone.Common.Test/WebClientTests.cs +++ b/src/NzbDrone.Common.Test/WebClientTests.cs @@ -20,6 +20,7 @@ namespace NzbDrone.Common.Test } [TestCase("")] + [TestCase("http://")] public void DownloadString_should_throw_on_error(string url) { Assert.Throws(() => Subject.DownloadString(url)); diff --git a/src/NzbDrone.Common/Extensions/PathExtensions.cs b/src/NzbDrone.Common/Extensions/PathExtensions.cs index 7038c9bac..1a11fb7c4 100644 --- a/src/NzbDrone.Common/Extensions/PathExtensions.cs +++ b/src/NzbDrone.Common/Extensions/PathExtensions.cs @@ -16,7 +16,6 @@ namespace NzbDrone.Common.Extensions private const string LOG_DB = "logs.db"; private const string NLOG_CONFIG_FILE = "nlog.config"; private const string UPDATE_CLIENT_EXE = "Radarr.Update.exe"; - private const string BACKUP_FOLDER = "Backups"; private static readonly string UPDATE_SANDBOX_FOLDER_NAME = "radarr_update" + Path.DirectorySeparatorChar; private static readonly string UPDATE_PACKAGE_FOLDER_NAME = "Radarr" + Path.DirectorySeparatorChar; diff --git a/src/NzbDrone.Common/Extensions/TryParseExtensions.cs b/src/NzbDrone.Common/Extensions/TryParseExtensions.cs index 8c0a62298..21255f514 100644 --- a/src/NzbDrone.Common/Extensions/TryParseExtensions.cs +++ b/src/NzbDrone.Common/Extensions/TryParseExtensions.cs @@ -7,7 +7,7 @@ namespace NzbDrone.Common.Extensions { public static int? ParseInt32(this string source) { - int result = 0; + int result; if (int.TryParse(source, out result)) { @@ -17,9 +17,9 @@ namespace NzbDrone.Common.Extensions return null; } - public static Nullable ParseInt64(this string source) + public static long? ParseInt64(this string source) { - long result = 0; + long result; if (long.TryParse(source, out result)) { diff --git a/src/NzbDrone.Core.Test/Download/DownloadApprovedReportsTests/DownloadApprovedFixture.cs b/src/NzbDrone.Core.Test/Download/DownloadApprovedReportsTests/DownloadApprovedFixture.cs index 50dec85ea..2828286d7 100644 --- a/src/NzbDrone.Core.Test/Download/DownloadApprovedReportsTests/DownloadApprovedFixture.cs +++ b/src/NzbDrone.Core.Test/Download/DownloadApprovedReportsTests/DownloadApprovedFixture.cs @@ -13,6 +13,9 @@ using NzbDrone.Core.Qualities; using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Movies; using NzbDrone.Test.Common; +using NzbDrone.Core.Exceptions; +using NzbDrone.Core.Indexers; +using NzbDrone.Core.Download.Clients; namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests { @@ -36,7 +39,7 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests .Build(); } - private RemoteMovie GetRemoteMovie(QualityModel quality, Movie movie = null) + private RemoteMovie GetRemoteMovie(QualityModel quality, Movie movie = null, DownloadProtocol downloadProtocol = DownloadProtocol.Usenet) { if (movie == null) { @@ -54,12 +57,13 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests MovieTitle = "A Movie", }, Movie = movie, - + Release = new ReleaseInfo() { PublishDate = DateTime.UtcNow, Title = "A.Movie.1998", - Size = 200 + Size = 200, + DownloadProtocol = downloadProtocol } }; @@ -91,6 +95,25 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests Mocker.GetMock().Verify(v => v.DownloadReport(It.IsAny()), Times.Once()); } + [Test] + public void should_not_download_if_any_movie_was_already_downloaded() + { + var remoteMovie1 = GetRemoteMovie( + new QualityModel(Quality.HDTV720p) + ); + + var remoteMovie2 = GetRemoteMovie( + new QualityModel(Quality.HDTV720p) + ); + + var decisions = new List(); + decisions.Add(new DownloadDecision(remoteMovie1)); + decisions.Add(new DownloadDecision(remoteMovie2)); + + Subject.ProcessDecisions(decisions); + Mocker.GetMock().Verify(v => v.DownloadReport(It.IsAny()), Times.Once()); + } + [Test] public void should_return_downloaded_reports() { @@ -179,7 +202,6 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests var decisions = new List(); decisions.Add(new DownloadDecision(remoteMovie, new Rejection("Failure!", RejectionType.Temporary))); - decisions.Add(new DownloadDecision(remoteMovie)); Subject.ProcessDecisions(decisions); Mocker.GetMock().Verify(v => v.DownloadReport(It.IsAny()), Times.Never()); @@ -201,15 +223,68 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests [Test] public void should_add_to_pending_even_if_already_added_to_pending() { + var remoteEpisode = GetRemoteMovie(new QualityModel(Quality.HDTV720p)); + var decisions = new List(); + decisions.Add(new DownloadDecision(remoteEpisode, new Rejection("Failure!", RejectionType.Temporary))); + decisions.Add(new DownloadDecision(remoteEpisode, new Rejection("Failure!", RejectionType.Temporary))); + + Subject.ProcessDecisions(decisions); + Mocker.GetMock().Verify(v => v.AddMany(It.IsAny>>()), Times.Once()); + } + + [Test] + public void should_add_to_failed_if_already_failed_for_that_protocol() + { var remoteMovie = GetRemoteMovie(new QualityModel(Quality.HDTV720p)); var decisions = new List(); - decisions.Add(new DownloadDecision(remoteMovie, new Rejection("Failure!", RejectionType.Temporary))); - decisions.Add(new DownloadDecision(remoteMovie, new Rejection("Failure!", RejectionType.Temporary))); + decisions.Add(new DownloadDecision(remoteMovie)); + decisions.Add(new DownloadDecision(remoteMovie)); + + Mocker.GetMock().Setup(s => s.DownloadReport(It.IsAny())) + .Throws(new DownloadClientUnavailableException("Download client failed")); Subject.ProcessDecisions(decisions); - Mocker.GetMock().Verify(v => v.AddMany(It.IsAny>>()), Times.Exactly(2)); + Mocker.GetMock().Verify(v => v.DownloadReport(It.IsAny()), Times.Once()); + } + + [Test] + public void should_not_add_to_failed_if_failed_for_a_different_protocol() + { + var remoteMovie = GetRemoteMovie(new QualityModel(Quality.HDTV720p), null, DownloadProtocol.Usenet); + var remoteMovie2 = GetRemoteMovie(new QualityModel(Quality.HDTV720p), null, DownloadProtocol.Torrent); + + var decisions = new List(); + decisions.Add(new DownloadDecision(remoteMovie)); + decisions.Add(new DownloadDecision(remoteMovie2)); + + Mocker.GetMock().Setup(s => s.DownloadReport(It.Is(r => r.Release.DownloadProtocol == DownloadProtocol.Usenet))) + .Throws(new DownloadClientUnavailableException("Download client failed")); + + Subject.ProcessDecisions(decisions); + Mocker.GetMock().Verify(v => v.DownloadReport(It.Is(r => r.Release.DownloadProtocol == DownloadProtocol.Usenet)), Times.Once()); + Mocker.GetMock().Verify(v => v.DownloadReport(It.Is(r => r.Release.DownloadProtocol == DownloadProtocol.Torrent)), Times.Once()); + } + + [Test] + public void should_add_to_rejected_if_release_unavailable_on_indexer() + { + var remoteMovie = GetRemoteMovie(new QualityModel(Quality.HDTV720p)); + + var decisions = new List(); + decisions.Add(new DownloadDecision(remoteMovie)); + + Mocker.GetMock() + .Setup(s => s.DownloadReport(It.IsAny())) + .Throws(new ReleaseUnavailableException(remoteMovie.Release, "That 404 Error is not just a Quirk")); + + var result = Subject.ProcessDecisions(decisions); + + result.Grabbed.Should().BeEmpty(); + result.Rejected.Should().NotBeEmpty(); + + ExceptionVerification.ExpectedWarns(1); } } } diff --git a/src/NzbDrone.Core.Test/Download/DownloadClientTests/TransmissionTests/TransmissionFixture.cs b/src/NzbDrone.Core.Test/Download/DownloadClientTests/TransmissionTests/TransmissionFixture.cs index 65acb57f7..f16b050a9 100644 --- a/src/NzbDrone.Core.Test/Download/DownloadClientTests/TransmissionTests/TransmissionFixture.cs +++ b/src/NzbDrone.Core.Test/Download/DownloadClientTests/TransmissionTests/TransmissionFixture.cs @@ -42,8 +42,8 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests var item = Subject.GetItems().Single(); VerifyCompleted(item); - item.CanBeRemoved.Should().BeTrue(); - item.CanMoveFiles.Should().BeTrue(); + item.CanBeRemoved.Should().BeFalse(); + item.CanMoveFiles.Should().BeFalse(); } [Test] @@ -66,9 +66,9 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests } [Test] - public void Download_with_TvDirectory_should_force_directory() + public void Download_with_MovieDirectory_should_force_directory() { - GivenTvDirectory(); + GivenMovieDirectory(); GivenSuccessfulDownload(); var remoteMovie = CreateRemoteMovie(); @@ -84,7 +84,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests [Test] public void Download_with_category_should_force_directory() { - GivenTvCategory(); + GivenMovieCategory(); GivenSuccessfulDownload(); var remoteMovie = CreateRemoteMovie(); @@ -100,7 +100,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests [Test] public void Download_with_category_should_not_have_double_slashes() { - GivenTvCategory(); + GivenMovieCategory(); GivenSuccessfulDownload(); _transmissionConfigItems["download-dir"] += "/"; @@ -175,7 +175,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests item.Status.Should().Be(expectedItemStatus); } - [TestCase(TransmissionTorrentStatus.Stopped, DownloadItemStatus.Completed, true)] + [TestCase(TransmissionTorrentStatus.Stopped, DownloadItemStatus.Completed, false)] [TestCase(TransmissionTorrentStatus.CheckWait, DownloadItemStatus.Downloading, false)] [TestCase(TransmissionTorrentStatus.Check, DownloadItemStatus.Downloading, false)] [TestCase(TransmissionTorrentStatus.Queued, DownloadItemStatus.Completed, false)] @@ -207,7 +207,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests [Test] public void should_exclude_items_not_in_category() { - GivenTvCategory(); + GivenMovieCategory(); _downloading.DownloadDir = @"C:/Downloads/Finished/transmission/radarr"; @@ -226,7 +226,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests [Test] public void should_exclude_items_not_in_TvDirectory() { - GivenTvDirectory(); + GivenMovieDirectory(); _downloading.DownloadDir = @"C:/Downloads/Finished/radarr/subdir"; @@ -283,5 +283,139 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests var item = Subject.GetItems().Single(); item.RemainingTime.Should().NotHaveValue(); } + + + [Test] + public void should_not_be_removable_and_should_not_allow_move_files_if_max_ratio_reached_and_not_stopped() + { + GivenGlobalSeedLimits(1.0); + PrepareClientToReturnCompletedItem(false, ratio: 1.0); + + var item = Subject.GetItems().Single(); + item.CanBeRemoved.Should().BeFalse(); + item.CanMoveFiles.Should().BeFalse(); + } + + [Test] + public void should_not_be_removable_and_should_not_allow_move_files_if_max_ratio_is_not_set() + { + GivenGlobalSeedLimits(); + PrepareClientToReturnCompletedItem(true, ratio: 1.0); + + var item = Subject.GetItems().Single(); + item.CanBeRemoved.Should().BeFalse(); + item.CanMoveFiles.Should().BeFalse(); + } + + [Test] + public void should_be_removable_and_should_allow_move_files_if_max_ratio_reached_and_paused() + { + GivenGlobalSeedLimits(1.0); + PrepareClientToReturnCompletedItem(true, ratio: 1.0); + + var item = Subject.GetItems().Single(); + item.CanBeRemoved.Should().BeTrue(); + item.CanMoveFiles.Should().BeTrue(); + } + + [Test] + public void should_be_removable_and_should_allow_move_files_if_overridden_max_ratio_reached_and_paused() + { + GivenGlobalSeedLimits(2.0); + PrepareClientToReturnCompletedItem(true, ratio: 1.0, ratioLimit: 0.8); + + var item = Subject.GetItems().Single(); + item.CanBeRemoved.Should().BeTrue(); + item.CanMoveFiles.Should().BeTrue(); + } + + [Test] + public void should_not_be_removable_if_overridden_max_ratio_not_reached_and_paused() + { + GivenGlobalSeedLimits(0.2); + PrepareClientToReturnCompletedItem(true, ratio: 0.5, ratioLimit: 0.8); + + var item = Subject.GetItems().Single(); + item.CanBeRemoved.Should().BeFalse(); + item.CanMoveFiles.Should().BeFalse(); + } + + + [Test] + public void should_not_be_removable_and_should_not_allow_move_files_if_max_idletime_reached_and_not_paused() + { + GivenGlobalSeedLimits(null, 20); + PrepareClientToReturnCompletedItem(false, ratio: 2.0, seedingTime: 30); + + var item = Subject.GetItems().Single(); + item.CanBeRemoved.Should().BeFalse(); + item.CanMoveFiles.Should().BeFalse(); + } + + [Test] + public void should_be_removable_and_should_allow_move_files_if_max_idletime_reached_and_paused() + { + GivenGlobalSeedLimits(null, 20); + PrepareClientToReturnCompletedItem(true, ratio: 2.0, seedingTime: 20); + + var item = Subject.GetItems().Single(); + item.CanBeRemoved.Should().BeTrue(); + item.CanMoveFiles.Should().BeTrue(); + } + + [Test] + public void should_be_removable_and_should_allow_move_files_if_overridden_max_idletime_reached_and_paused() + { + GivenGlobalSeedLimits(null, 40); + PrepareClientToReturnCompletedItem(true, ratio: 2.0, seedingTime: 20, idleLimit: 10); + + var item = Subject.GetItems().Single(); + item.CanBeRemoved.Should().BeTrue(); + item.CanMoveFiles.Should().BeTrue(); + } + + [Test] + public void should_be_removable_and_should_not_allow_move_files_if_overridden_max_idletime_reached_and_not_paused() + { + GivenGlobalSeedLimits(null, 40); + PrepareClientToReturnCompletedItem(false, ratio: 2.0, seedingTime: 20, idleLimit: 10); + + var item = Subject.GetItems().Single(); + item.CanBeRemoved.Should().BeTrue(); + item.CanMoveFiles.Should().BeFalse(); + } + + [Test] + public void should_not_be_removable_if_overridden_max_idletime_not_reached_and_paused() + { + GivenGlobalSeedLimits(null, 20); + PrepareClientToReturnCompletedItem(true, ratio: 2.0, seedingTime: 30, idleLimit: 40); + + var item = Subject.GetItems().Single(); + item.CanBeRemoved.Should().BeFalse(); + item.CanMoveFiles.Should().BeFalse(); + } + + [Test] + public void should_not_be_removable_if_max_idletime_reached_but_ratio_not_and_not_paused() + { + GivenGlobalSeedLimits(2.0, 20); + PrepareClientToReturnCompletedItem(false, ratio: 1.0, seedingTime: 30); + + var item = Subject.GetItems().Single(); + item.CanBeRemoved.Should().BeFalse(); + item.CanMoveFiles.Should().BeFalse(); + } + + [Test] + public void should_be_removable_and_should_allow_move_files_if_max_idletime_configured_and_paused() + { + GivenGlobalSeedLimits(2.0, 20); + PrepareClientToReturnCompletedItem(true, ratio: 1.0, seedingTime: 30); + + var item = Subject.GetItems().Single(); + item.CanBeRemoved.Should().BeTrue(); + item.CanMoveFiles.Should().BeTrue(); + } } } diff --git a/src/NzbDrone.Core.Test/Download/DownloadClientTests/TransmissionTests/TransmissionFixtureBase.cs b/src/NzbDrone.Core.Test/Download/DownloadClientTests/TransmissionTests/TransmissionFixtureBase.cs index 5fd4136b6..d6bc18c2e 100644 --- a/src/NzbDrone.Core.Test/Download/DownloadClientTests/TransmissionTests/TransmissionFixtureBase.cs +++ b/src/NzbDrone.Core.Test/Download/DownloadClientTests/TransmissionTests/TransmissionFixtureBase.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using Moq; using NUnit.Framework; using NzbDrone.Common.Http; +using NzbDrone.Common.Serializer; using NzbDrone.Core.Download; using NzbDrone.Core.Download.Clients.Transmission; using NzbDrone.Core.MediaFiles.TorrentInfo; @@ -72,11 +73,13 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests { HashString = "HASH", IsFinished = true, - Status = TransmissionTorrentStatus.Stopped, + Status = TransmissionTorrentStatus.Seeding, Name = _title, TotalSize = 1000, LeftUntilDone = 0, - DownloadDir = "somepath" + DownloadDir = "somepath", + DownloadedEver = 1000, + UploadedEver = 900 }; _magnet = new TransmissionTorrent @@ -106,16 +109,16 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests Mocker.GetMock() .Setup(v => v.GetConfig(It.IsAny())) - .Returns(_transmissionConfigItems); + .Returns(() => Json.Deserialize(_transmissionConfigItems.ToJson())); } - protected void GivenTvCategory() + protected void GivenMovieCategory() { _settings.MovieCategory = "radarr"; } - protected void GivenTvDirectory() + protected void GivenMovieDirectory() { _settings.MovieDirectory = @"C:/Downloads/Finished/radarr"; } @@ -178,8 +181,40 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests }); } - protected void PrepareClientToReturnCompletedItem() + protected void PrepareClientToReturnCompletedItem(bool stopped = false, double ratio = 0.9, int seedingTime = 60, double? ratioLimit = null, int? idleLimit = null) { + if (stopped) + _completed.Status = TransmissionTorrentStatus.Stopped; + _completed.UploadedEver = (int)(_completed.DownloadedEver * ratio); + _completed.SecondsSeeding = seedingTime * 60; + + if (ratioLimit.HasValue) + { + if (double.IsPositiveInfinity(ratioLimit.Value)) + { + _completed.SeedRatioMode = 2; + } + else + { + _completed.SeedRatioMode = 1; + _completed.SeedRatioLimit = ratioLimit.Value; + } + } + + if (idleLimit.HasValue) + { + if (double.IsPositiveInfinity(idleLimit.Value)) + { + _completed.SeedIdleMode = 2; + } + else + { + _completed.SeedIdleMode = 1; + _completed.SeedIdleLimit = idleLimit.Value; + } + } + + GivenTorrents(new List { _completed @@ -193,5 +228,20 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests _magnet }); } + + protected void GivenGlobalSeedLimits(double? ratioLimit = null, int? idleLimit = null) + { + _transmissionConfigItems["seedRatioLimited"] = ratioLimit.HasValue; + if (ratioLimit.HasValue) + { + _transmissionConfigItems["seedRatioLimit"] = ratioLimit.Value; + } + + _transmissionConfigItems["idle-seeding-limit-enabled"] = idleLimit.HasValue; + if (idleLimit.HasValue) + { + _transmissionConfigItems["idle-seeding-limit"] = idleLimit.Value; + } + } } } \ No newline at end of file diff --git a/src/NzbDrone.Core.Test/Download/DownloadClientTests/VuzeTests/VuzeFixture.cs b/src/NzbDrone.Core.Test/Download/DownloadClientTests/VuzeTests/VuzeFixture.cs index 2884581c6..1abf92193 100644 --- a/src/NzbDrone.Core.Test/Download/DownloadClientTests/VuzeTests/VuzeFixture.cs +++ b/src/NzbDrone.Core.Test/Download/DownloadClientTests/VuzeTests/VuzeFixture.cs @@ -74,7 +74,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests [Test] public void Download_with_TvDirectory_should_force_directory() { - GivenTvDirectory(); + GivenMovieDirectory(); GivenSuccessfulDownload(); var remoteMovie = CreateRemoteMovie(); @@ -90,7 +90,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests [Test] public void Download_with_category_should_force_directory() { - GivenTvCategory(); + GivenMovieCategory(); GivenSuccessfulDownload(); var remoteMovie = CreateRemoteMovie(); @@ -106,7 +106,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests [Test] public void Download_with_category_should_not_have_double_slashes() { - GivenTvCategory(); + GivenMovieCategory(); GivenSuccessfulDownload(); _transmissionConfigItems["download-dir"] += "/"; @@ -213,7 +213,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests [Test] public void should_exclude_items_not_in_category() { - GivenTvCategory(); + GivenMovieCategory(); _downloading.DownloadDir = @"C:/Downloads/Finished/transmission/radarr"; @@ -232,7 +232,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests [Test] public void should_exclude_items_not_in_TvDirectory() { - GivenTvDirectory(); + GivenMovieDirectory(); _downloading.DownloadDir = @"C:/Downloads/Finished/radarr/subdir"; diff --git a/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupOrphanedMetadataFilesFixture.cs b/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupOrphanedMetadataFilesFixture.cs index 7bb0dd880..fa5727ea7 100644 --- a/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupOrphanedMetadataFilesFixture.cs +++ b/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupOrphanedMetadataFilesFixture.cs @@ -8,6 +8,8 @@ using NzbDrone.Core.MediaFiles; using NzbDrone.Core.Qualities; using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Movies; +using NzbDrone.Core.Languages; +using System.Collections.Generic; namespace NzbDrone.Core.Test.Housekeeping.Housekeepers { @@ -70,6 +72,7 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers var movieFile = Builder.CreateNew() .With(h => h.Quality = new QualityModel()) + .With(h => h.Languages = new List()) .BuildNew(); Db.Insert(movie); diff --git a/src/NzbDrone.Core.Test/ParserTests/ReleaseGroupParserFixture.cs b/src/NzbDrone.Core.Test/ParserTests/ReleaseGroupParserFixture.cs index 5e3399eb5..3ee5319c7 100644 --- a/src/NzbDrone.Core.Test/ParserTests/ReleaseGroupParserFixture.cs +++ b/src/NzbDrone.Core.Test/ParserTests/ReleaseGroupParserFixture.cs @@ -22,7 +22,7 @@ namespace NzbDrone.Core.Test.ParserTests [TestCase("The Colbert Report - 2014-06-02 - Thomas Piketty.mkv", null)] [TestCase("Real Time with Bill Maher S12E17 May 23, 2014.mp4", null)] [TestCase("Reizen Waes - S01E08 - Transistri\u00EB, Zuid-Osseti\u00EB en Abchazi\u00EB SDTV.avi", null)] - [TestCase("Simpsons 10x11 - Wild Barts Cant Be Broken [rl].avi", null)] + [TestCase("Simpsons 10x11 - Wild Barts Cant Be Broken [rl].avi", "rl")] [TestCase("[ www.Torrenting.com ] - Revenge.S03E14.720p.HDTV.X264-DIMENSION", "DIMENSION")] [TestCase("Seed S02E09 HDTV x264-2HD [eztv]-[rarbg.com]", "2HD")] [TestCase("7s-atlantis-s02e01-720p.mkv", null)] @@ -32,6 +32,11 @@ namespace NzbDrone.Core.Test.ParserTests [TestCase("Who.is.America.S01E01.INTERNAL.720p.HDTV.x264-aAF-RakuvUS-Obfuscated", "aAF")] [TestCase("Haunted.Hayride.2018.720p.WEBRip.DDP5.1.x264-NTb-postbot", "NTb")] [TestCase("Haunted.Hayride.2018.720p.WEBRip.DDP5.1.x264-NTb-xpost", "NTb")] + [TestCase("2.Broke.Girls.S02E24.1080p.AMZN.WEBRip.DD5.1.x264-CasStudio-AsRequested", "CasStudio")] + [TestCase("Billions.S04E11.Lamster.1080p.AMZN.WEB-DL.DDP5.1.H.264-NTb-AlternativeToRequested", "NTb")] + [TestCase("NCIS.S16E04.Third.Wheel.1080p.AMZN.WEB-DL.DDP5.1.H.264-NTb-GEROV", "NTb")] + [TestCase("Will.and.Grace.S10E06.Kid.n.Play.1080p.AMZN.WEB-DL.DDP5.1.H.264-NTb-Z0iDS3N", "NTb")] + [TestCase("Absolute.Power.S02E06.The.House.of.Lords.DVDRip.x264-MaG-Chamele0n", "MaG")] //[TestCase("", "")] public void should_parse_release_group(string title, string expected) { diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/AcceptableSizeSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/AcceptableSizeSpecification.cs index b03ee62aa..ab9fd3db5 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/AcceptableSizeSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/AcceptableSizeSpecification.cs @@ -36,11 +36,13 @@ namespace NzbDrone.Core.DecisionEngine.Specifications } var qualityDefinition = _qualityDefinitionService.Get(quality); - if (subject.Movie.Runtime == 0) - { - _logger.Warn("{0} has no runtime information using median movie runtime of 110 minutes.", subject.Movie); - subject.Movie.Runtime = 110; - } + + if (subject.Movie.Runtime == 0) + { + _logger.Warn("{0} has no runtime information using median movie runtime of 110 minutes.", subject.Movie); + subject.Movie.Runtime = 110; + } + if (qualityDefinition.MinSize.HasValue) { var minSize = qualityDefinition.MinSize.Value.Megabytes(); diff --git a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentLabel.cs b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentLabel.cs index 7be7d97e7..153e8e009 100644 --- a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentLabel.cs +++ b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentLabel.cs @@ -7,4 +7,4 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent public string Name { get; set; } public string SavePath { get; set; } } -} \ No newline at end of file +} diff --git a/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionBase.cs b/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionBase.cs index 434d57531..187d4c717 100644 --- a/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionBase.cs +++ b/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionBase.cs @@ -35,17 +35,8 @@ namespace NzbDrone.Core.Download.Clients.Transmission public override IEnumerable GetItems() { - List torrents; - - try - { - torrents = _proxy.GetTorrents(Settings); - } - catch (DownloadClientException ex) - { - _logger.Error(ex, ex.Message); - return Enumerable.Empty(); - } + var configFunc = new Lazy(() => _proxy.GetConfig(Settings)); + var torrents = _proxy.GetTorrents(Settings); var items = new List(); @@ -111,9 +102,8 @@ namespace NzbDrone.Core.Download.Clients.Transmission item.Status = DownloadItemStatus.Downloading; } - item.CanMoveFiles = item.CanBeRemoved = - torrent.Status == TransmissionTorrentStatus.Stopped && - item.SeedRatio >= torrent.SeedRatioLimit; + item.CanBeRemoved = HasReachedSeedLimit(torrent, item.SeedRatio, configFunc); + item.CanMoveFiles = item.CanBeRemoved && torrent.Status == TransmissionTorrentStatus.Stopped; items.Add(item); } @@ -121,6 +111,46 @@ namespace NzbDrone.Core.Download.Clients.Transmission return items; } + protected bool HasReachedSeedLimit(TransmissionTorrent torrent, double? ratio, Lazy config) + { + var isStopped = torrent.Status == TransmissionTorrentStatus.Stopped; + var isSeeding = torrent.Status == TransmissionTorrentStatus.Seeding; + + if (torrent.SeedRatioMode == 1) + { + if (isStopped && ratio.HasValue && ratio >= torrent.SeedRatioLimit) + { + return true; + } + } + else if (torrent.SeedRatioMode == 0) + { + if (isStopped && config.Value.SeedRatioLimited && ratio >= config.Value.SeedRatioLimit) + { + return true; + } + } + + // Transmission doesn't support SeedTimeLimit, use/abuse seed idle limit, but only if it was set per-torrent. + if (torrent.SeedIdleMode == 1) + { + if ((isStopped || isSeeding) && torrent.SecondsSeeding > torrent.SeedIdleLimit * 60) + { + return true; + } + } + else if (torrent.SeedIdleMode == 0) + { + // The global idle limit is a real idle limit, if it's configured then 'Stopped' is enough. + if (isStopped && config.Value.IdleSeedingLimitEnabled) + { + return true; + } + } + + return false; + } + public override void RemoveItem(string downloadId, bool deleteData) { _proxy.RemoveTorrent(downloadId.ToLower(), deleteData, Settings); @@ -129,8 +159,8 @@ namespace NzbDrone.Core.Download.Clients.Transmission public override DownloadClientInfo GetStatus() { var config = _proxy.GetConfig(Settings); - var destDir = config.GetValueOrDefault("download-dir") as string; - + var destDir = config.DownloadDir; + if (Settings.MovieCategory.IsNotNullOrWhiteSpace()) { destDir = string.Format("{0}/.{1}", destDir, Settings.MovieCategory); @@ -197,7 +227,7 @@ namespace NzbDrone.Core.Download.Clients.Transmission if (!Settings.MovieCategory.IsNotNullOrWhiteSpace()) return null; var config = _proxy.GetConfig(Settings); - var destDir = (string)config.GetValueOrDefault("download-dir"); + var destDir = config.DownloadDir; return $"{destDir.TrimEnd('/')}/{Settings.MovieCategory}"; } diff --git a/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionConfig.cs b/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionConfig.cs new file mode 100644 index 000000000..194df1e5e --- /dev/null +++ b/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionConfig.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace NzbDrone.Core.Download.Clients.Transmission +{ + public class TransmissionConfig + { + [JsonProperty("rpc-version")] + public string RpcVersion { get; set; } + public string Version { get; set; } + + [JsonProperty("download-dir")] + public string DownloadDir { get; set; } + + public double SeedRatioLimit { get; set; } + public bool SeedRatioLimited { get; set; } + + [JsonProperty("idle-seeding-limit")] + public long IdleSeedingLimit { get; set; } + [JsonProperty("idle-seeding-limit-enabled")] + public bool IdleSeedingLimitEnabled { get; set; } + } +} diff --git a/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionProxy.cs b/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionProxy.cs index 84ba1be5d..dd0b0a0cf 100644 --- a/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionProxy.cs +++ b/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionProxy.cs @@ -16,7 +16,7 @@ namespace NzbDrone.Core.Download.Clients.Transmission void AddTorrentFromUrl(string torrentUrl, string downloadDirectory, TransmissionSettings settings); void AddTorrentFromData(byte[] torrentData, string downloadDirectory, TransmissionSettings settings); void SetTorrentSeedingConfiguration(string hash, TorrentSeedConfiguration seedConfiguration, TransmissionSettings settings); - Dictionary GetConfig(TransmissionSettings settings); + TransmissionConfig GetConfig(TransmissionSettings settings); string GetProtocolVersion(TransmissionSettings settings); string GetClientVersion(TransmissionSettings settings); void RemoveTorrent(string hash, bool removeData, TransmissionSettings settings); @@ -101,26 +101,22 @@ namespace NzbDrone.Core.Download.Clients.Transmission { var config = GetConfig(settings); - var version = config["rpc-version"]; - - return version.ToString(); + return config.RpcVersion; } public string GetClientVersion(TransmissionSettings settings) { var config = GetConfig(settings); - var version = config["version"]; - - return version.ToString(); + return config.Version; } - public Dictionary GetConfig(TransmissionSettings settings) + public TransmissionConfig GetConfig(TransmissionSettings settings) { // Gets the transmission version. var result = GetSessionVariables(settings); - return result.Arguments; + return Json.Deserialize(result.Arguments.ToJson()); } public void RemoveTorrent(string hashString, bool removeData, TransmissionSettings settings) @@ -164,15 +160,20 @@ namespace NzbDrone.Core.Download.Clients.Transmission "hashString", // Unique torrent ID. Use this instead of the client id? "name", "downloadDir", - "status", "totalSize", "leftUntilDone", "isFinished", "eta", + "status", + "secondsDownloading", + "secondsSeeding", "errorString", "uploadedEver", "downloadedEver", "seedRatioLimit", + "seedRatioMode", + "seedIdleLimit", + "seedIdleMode", "fileCount" }; diff --git a/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionTorrent.cs b/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionTorrent.cs index 377cc01f2..3abb5d4e8 100644 --- a/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionTorrent.cs +++ b/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionTorrent.cs @@ -12,10 +12,14 @@ public int Eta { get; set; } public TransmissionTorrentStatus Status { get; set; } public int SecondsDownloading { get; set; } + public int SecondsSeeding { get; set; } public string ErrorString { get; set; } public long DownloadedEver { get; set; } public long UploadedEver { get; set; } - public long SeedRatioLimit { get; set; } + public double SeedRatioLimit { get; set; } + public int SeedRatioMode { get; set; } + public long SeedIdleLimit { get; set; } + public int SeedIdleMode { get; set; } public int FileCount { get; set; } } } diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index 09ebfdd58..b693ddaec 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -171,6 +171,7 @@ + diff --git a/src/NzbDrone.Core/Parser/Parser.cs b/src/NzbDrone.Core/Parser/Parser.cs index c67b2a067..64491a662 100644 --- a/src/NzbDrone.Core/Parser/Parser.cs +++ b/src/NzbDrone.Core/Parser/Parser.cs @@ -113,7 +113,7 @@ namespace NzbDrone.Core.Parser private static readonly Regex SixDigitAirDateRegex = new Regex(@"(?<=[_.-])(?(?[1-9]\d{1})(?[0-1][0-9])(?[0-3][0-9]))(?=[_.-])", RegexOptions.IgnoreCase | RegexOptions.Compiled); - private static readonly Regex CleanReleaseGroupRegex = new Regex(@"^(.*?[-._ ](S\d+E\d+)[-._ ])|(-(RP|1|NZBGeek|Obfuscated|sample|Pre|postbot|xpost|Rakuv[a-z]*|WhiteRev|BUYMORE|AsRequested))+$", + private static readonly Regex CleanReleaseGroupRegex = new Regex(@"^(.*?[-._ ](S\d+E\d+)[-._ ])|(-(RP|1|NZBGeek|Obfuscated|sample|Pre|postbot|xpost|Rakuv[a-z]*|WhiteRev|BUYMORE|AsRequested|AlternativeToRequested|GEROV|Z0iDS3N|Chamele0n))+$", RegexOptions.IgnoreCase | RegexOptions.Compiled); private static readonly Regex CleanTorrentSuffixRegex = new Regex(@"\[(?:ettv|rartv|rarbg|cttv)\]$",