Fixed: Transmission seeding idle time handling
This commit is contained in:
parent
13907d6711
commit
af5166e95d
|
@ -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]
|
||||
|
@ -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)]
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,7 +109,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests
|
|||
|
||||
Mocker.GetMock<ITransmissionProxy>()
|
||||
.Setup(v => v.GetConfig(It.IsAny<TransmissionSettings>()))
|
||||
.Returns(_transmissionConfigItems);
|
||||
.Returns(() => Json.Deserialize<TransmissionConfig>(_transmissionConfigItems.ToJson()));
|
||||
|
||||
}
|
||||
|
||||
|
@ -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<TransmissionTorrent>
|
||||
{
|
||||
_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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -47,7 +47,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
|
|||
[Test]
|
||||
public void completed_download_should_have_required_properties()
|
||||
{
|
||||
PrepareClientToReturnCompletedItem();
|
||||
PrepareClientToReturnCompletedItem(true, ratioLimit: 0.5);
|
||||
var item = Subject.GetItems().Single();
|
||||
VerifyCompleted(item);
|
||||
|
||||
|
@ -184,7 +184,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
|
|||
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.Queued, false)]
|
||||
|
|
|
@ -32,6 +32,7 @@ namespace NzbDrone.Core.Download.Clients.Transmission
|
|||
|
||||
public override IEnumerable<DownloadClientItem> GetItems()
|
||||
{
|
||||
var configFunc = new Lazy<TransmissionConfig>(() => _proxy.GetConfig(Settings));
|
||||
var torrents = _proxy.GetTorrents(Settings);
|
||||
|
||||
var items = new List<DownloadClientItem>();
|
||||
|
@ -98,9 +99,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);
|
||||
}
|
||||
|
@ -108,6 +108,46 @@ namespace NzbDrone.Core.Download.Clients.Transmission
|
|||
return items;
|
||||
}
|
||||
|
||||
protected bool HasReachedSeedLimit(TransmissionTorrent torrent, double? ratio, Lazy<TransmissionConfig> 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);
|
||||
|
@ -116,7 +156,7 @@ 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.TvCategory.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
|
@ -184,7 +224,7 @@ namespace NzbDrone.Core.Download.Clients.Transmission
|
|||
if (!Settings.TvCategory.IsNotNullOrWhiteSpace()) return null;
|
||||
|
||||
var config = _proxy.GetConfig(Settings);
|
||||
var destDir = (string)config.GetValueOrDefault("download-dir");
|
||||
var destDir = config.DownloadDir;
|
||||
|
||||
return $"{destDir.TrimEnd('/')}/{Settings.TvCategory}";
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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<string, object> 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<string, object> GetConfig(TransmissionSettings settings)
|
||||
public TransmissionConfig GetConfig(TransmissionSettings settings)
|
||||
{
|
||||
// Gets the transmission version.
|
||||
var result = GetSessionVariables(settings);
|
||||
|
||||
return result.Arguments;
|
||||
return Json.Deserialize<TransmissionConfig>(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"
|
||||
};
|
||||
|
||||
|
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -151,6 +151,7 @@
|
|||
<Compile Include="DecisionEngine\Specifications\RepackSpecification.cs" />
|
||||
<Compile Include="DecisionEngine\Specifications\MultiSeasonSpecification.cs" />
|
||||
<Compile Include="DecisionEngine\Specifications\UpgradeAllowedSpecification.cs" />
|
||||
<Compile Include="Download\Clients\Transmission\TransmissionConfig.cs" />
|
||||
<Compile Include="Exceptions\DownloadClientRejectedReleaseException.cs" />
|
||||
<Compile Include="Exceptions\SearchFailedException.cs" />
|
||||
<Compile Include="Download\Clients\QBittorrent\QBittorrentLabel.cs" />
|
||||
|
|
Loading…
Reference in New Issue