diff --git a/src/NzbDrone.Core/Configuration/ISeedConfigProvider.cs b/src/NzbDrone.Core/Configuration/ISeedConfigProvider.cs new file mode 100644 index 0000000000..ec91ff21a8 --- /dev/null +++ b/src/NzbDrone.Core/Configuration/ISeedConfigProvider.cs @@ -0,0 +1,10 @@ +using NzbDrone.Core.Download.Clients; +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.Configuration +{ + public interface ISeedConfigProvider + { + TorrentSeedConfiguration GetSeedConfiguration(ReleaseInfo release); + } +} diff --git a/src/NzbDrone.Core/Configuration/SeedConfigProvider.cs b/src/NzbDrone.Core/Configuration/SeedConfigProvider.cs new file mode 100644 index 0000000000..2398c82014 --- /dev/null +++ b/src/NzbDrone.Core/Configuration/SeedConfigProvider.cs @@ -0,0 +1,33 @@ +using NzbDrone.Core.Download.Clients; +using NzbDrone.Core.Indexers; +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.Configuration +{ + public class SeedConfigProvider: ISeedConfigProvider + { + private readonly IIndexerFactory _indexerFactory; + + public SeedConfigProvider(IIndexerFactory indexerFactory) + { + _indexerFactory = indexerFactory; + } + + public TorrentSeedConfiguration GetSeedConfiguration(ReleaseInfo release) + { + if (release.DownloadProtocol != DownloadProtocol.Torrent) return null; + + var indexer = _indexerFactory.Get(release.IndexerId); + + if (indexer.Settings is ITorrentIndexerSettings torrentIndexerSettings) + { + return new TorrentSeedConfiguration + { + Ratio = torrentIndexerSettings.SeedRatio + }; + } + + return null; + } + } +} diff --git a/src/NzbDrone.Core/Download/Clients/Deluge/Deluge.cs b/src/NzbDrone.Core/Download/Clients/Deluge/Deluge.cs index 3b52f95700..54848872bc 100644 --- a/src/NzbDrone.Core/Download/Clients/Deluge/Deluge.cs +++ b/src/NzbDrone.Core/Download/Clients/Deluge/Deluge.cs @@ -45,6 +45,8 @@ protected override string AddFromMagnetLink(RemoteEpisode remoteEpisode, string _proxy.SetLabel(actualHash, Settings.TvCategory, Settings); } + _proxy.SetTorrentSeedingConfiguration(actualHash, remoteEpisode.SeedConfiguration, Settings); + var isRecentEpisode = remoteEpisode.IsRecentEpisode(); if (isRecentEpisode && Settings.RecentTvPriority == (int)DelugePriority.First || @@ -65,6 +67,8 @@ protected override string AddFromTorrentFile(RemoteEpisode remoteEpisode, string throw new DownloadClientException("Deluge failed to add torrent " + filename); } + _proxy.SetTorrentSeedingConfiguration(actualHash, remoteEpisode.SeedConfiguration, Settings); + if (!Settings.TvCategory.IsNullOrWhiteSpace()) { _proxy.SetLabel(actualHash, Settings.TvCategory, Settings); @@ -110,6 +114,7 @@ public override IEnumerable GetItems() var outputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(torrent.DownloadPath)); item.OutputPath = outputPath + torrent.Name; item.RemainingSize = torrent.Size - torrent.BytesDownloaded; + item.SeedRatio = torrent.Ratio; try { @@ -145,8 +150,13 @@ public override IEnumerable GetItems() item.Status = DownloadItemStatus.Downloading; } - // Here we detect if Deluge is managing the torrent and whether the seed criteria has been met. This allows drone to delete the torrent as appropriate. - item.CanMoveFiles = item.CanBeRemoved = (torrent.IsAutoManaged && torrent.StopAtRatio && torrent.Ratio >= torrent.StopRatio && torrent.State == DelugeTorrentStatus.Paused); + // Here we detect if Deluge is managing the torrent and whether the seed criteria has been met. + // This allows drone to delete the torrent as appropriate. + item.CanMoveFiles = item.CanBeRemoved = + torrent.IsAutoManaged && + torrent.StopAtRatio && + torrent.Ratio >= torrent.StopRatio && + torrent.State == DelugeTorrentStatus.Paused; items.Add(item); } diff --git a/src/NzbDrone.Core/Download/Clients/Deluge/DelugeProxy.cs b/src/NzbDrone.Core/Download/Clients/Deluge/DelugeProxy.cs index 860a8e8ea5..e1b4f28454 100644 --- a/src/NzbDrone.Core/Download/Clients/Deluge/DelugeProxy.cs +++ b/src/NzbDrone.Core/Download/Clients/Deluge/DelugeProxy.cs @@ -151,13 +151,16 @@ public void SetTorrentConfiguration(string hash, string key, object value, Delug public void SetTorrentSeedingConfiguration(string hash, TorrentSeedConfiguration seedConfiguration, DelugeSettings settings) { + if (seedConfiguration == null) return; + + var ratioArguments = new Dictionary(); + if (seedConfiguration.Ratio != null) { - var ratioArguments = new Dictionary(); ratioArguments.Add("stop_ratio", seedConfiguration.Ratio.Value); - - ProcessRequest(settings, "core.set_torrent_options", new string[] { hash }, ratioArguments); } + + ProcessRequest(settings, "core.set_torrent_options", new[] { hash }, ratioArguments); } public void AddLabel(string label, DelugeSettings settings) @@ -176,7 +179,7 @@ private JsonRpcRequestBuilder BuildRequest(DelugeSettings settings) var requestBuilder = new JsonRpcRequestBuilder(url); requestBuilder.LogResponseContent = true; - + requestBuilder.Resource("json"); requestBuilder.PostProcess += r => r.RequestTimeout = TimeSpan.FromSeconds(15); diff --git a/src/NzbDrone.Core/Download/Clients/Deluge/DelugeTorrent.cs b/src/NzbDrone.Core/Download/Clients/Deluge/DelugeTorrent.cs index 5dcdc75496..898c425b4c 100644 --- a/src/NzbDrone.Core/Download/Clients/Deluge/DelugeTorrent.cs +++ b/src/NzbDrone.Core/Download/Clients/Deluge/DelugeTorrent.cs @@ -13,7 +13,7 @@ public class DelugeTorrent [JsonProperty(PropertyName = "is_finished")] public bool IsFinished { get; set; } - + // Other paths: What is the difference between 'move_completed_path' and 'move_on_completed_path'? /* [JsonProperty(PropertyName = "move_completed_path")] @@ -22,7 +22,7 @@ public class DelugeTorrent public String DownloadPathMoveOnCompleted { get; set; } */ - [JsonProperty(PropertyName = "save_path")] + [JsonProperty(PropertyName = "save_path")] public string DownloadPath { get; set; } [JsonProperty(PropertyName = "total_size")] diff --git a/src/NzbDrone.Core/Download/Clients/DownloadStation/TorrentDownloadStation.cs b/src/NzbDrone.Core/Download/Clients/DownloadStation/TorrentDownloadStation.cs index 10c24a10ae..9e265165a0 100644 --- a/src/NzbDrone.Core/Download/Clients/DownloadStation/TorrentDownloadStation.cs +++ b/src/NzbDrone.Core/Download/Clients/DownloadStation/TorrentDownloadStation.cs @@ -88,6 +88,7 @@ public override IEnumerable GetItems() TotalSize = torrent.Size, RemainingSize = GetRemainingSize(torrent), RemainingTime = GetRemainingTime(torrent), + SeedRatio = GetSeedRatio(torrent), Status = GetStatus(torrent), Message = GetMessage(torrent), CanMoveFiles = IsCompleted(torrent), @@ -121,7 +122,7 @@ public override DownloadClientInfo GetStatus() { _logger.Debug(e, "Failed to get config from Download Station"); - throw e; + throw; } } @@ -278,6 +279,19 @@ protected long GetRemainingSize(DownloadStationTask torrent) return TimeSpan.FromSeconds(remainingSize / downloadSpeed); } + protected double? GetSeedRatio(DownloadStationTask torrent) + { + var couldConvertDownloaded = long.TryParse(torrent.Additional.Transfer["size_downloaded"], out var downloaded); + var couldConvertUploaded = long.TryParse(torrent.Additional.Transfer["size_uploaded"], out var uploaded); + + if (!couldConvertDownloaded || !couldConvertUploaded) + { + return new Nullable(); + } + + return downloaded <= 0 ? 0 : (double) uploaded / downloaded; + } + protected ValidationFailure TestOutputPath() { try diff --git a/src/NzbDrone.Core/Download/Clients/DownloadStation/UsenetDownloadStation.cs b/src/NzbDrone.Core/Download/Clients/DownloadStation/UsenetDownloadStation.cs index f870d4567e..632eedcb0f 100644 --- a/src/NzbDrone.Core/Download/Clients/DownloadStation/UsenetDownloadStation.cs +++ b/src/NzbDrone.Core/Download/Clients/DownloadStation/UsenetDownloadStation.cs @@ -147,7 +147,7 @@ public override DownloadClientInfo GetStatus() { _logger.Debug(e, "Failed to get config from Download Station"); - throw e; + throw; } } diff --git a/src/NzbDrone.Core/Download/Clients/Hadouken/Hadouken.cs b/src/NzbDrone.Core/Download/Clients/Hadouken/Hadouken.cs index e652cd9e97..1c2d660dad 100644 --- a/src/NzbDrone.Core/Download/Clients/Hadouken/Hadouken.cs +++ b/src/NzbDrone.Core/Download/Clients/Hadouken/Hadouken.cs @@ -62,7 +62,9 @@ public override IEnumerable GetItems() RemainingSize = torrent.TotalSize - torrent.DownloadedBytes, RemainingTime = eta, Title = torrent.Name, - TotalSize = torrent.TotalSize + TotalSize = torrent.TotalSize, + SeedRatio = torrent.DownloadedBytes <= 0 ? 0 : + (double) torrent.UploadedBytes / torrent.DownloadedBytes }; if (!string.IsNullOrEmpty(torrent.Error)) diff --git a/src/NzbDrone.Core/Download/Clients/Hadouken/HadoukenProxy.cs b/src/NzbDrone.Core/Download/Clients/Hadouken/HadoukenProxy.cs index 20e60f1fb8..e9eb8e651c 100644 --- a/src/NzbDrone.Core/Download/Clients/Hadouken/HadoukenProxy.cs +++ b/src/NzbDrone.Core/Download/Clients/Hadouken/HadoukenProxy.cs @@ -138,6 +138,7 @@ private HadoukenTorrent MapTorrent(object[] item) TotalSize = Convert.ToInt64(item[3]), Progress = Convert.ToDouble(item[4]), DownloadedBytes = Convert.ToInt64(item[5]), + UploadedBytes = Convert.ToInt64(item[6]), DownloadRate = Convert.ToInt64(item[9]), Label = Convert.ToString(item[11]), Error = Convert.ToString(item[21]), diff --git a/src/NzbDrone.Core/Download/Clients/Hadouken/Models/HadoukenTorrent.cs b/src/NzbDrone.Core/Download/Clients/Hadouken/Models/HadoukenTorrent.cs index a52180ca27..b84c2b3f5f 100644 --- a/src/NzbDrone.Core/Download/Clients/Hadouken/Models/HadoukenTorrent.cs +++ b/src/NzbDrone.Core/Download/Clients/Hadouken/Models/HadoukenTorrent.cs @@ -13,6 +13,7 @@ public sealed class HadoukenTorrent public bool IsSeeding { get; set; } public long TotalSize { get; set; } public long DownloadedBytes { get; set; } + public long UploadedBytes { get; set; } public long DownloadRate { get; set; } public string Error { get; set; } } diff --git a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs index 9fb218d751..2e58c9cd93 100644 --- a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs +++ b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs @@ -108,6 +108,7 @@ public override IEnumerable GetItems() item.DownloadClient = Definition.Name; item.RemainingSize = (long)(torrent.Size * (1.0 - torrent.Progress)); item.RemainingTime = GetRemainingTime(torrent); + item.SeedRatio = torrent.Ratio; item.OutputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(torrent.SavePath)); diff --git a/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionBase.cs b/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionBase.cs index 8533f71dce..dd4d8eea4c 100644 --- a/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionBase.cs +++ b/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionBase.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Net; using FluentValidation.Results; using NLog; using NzbDrone.Common.Disk; @@ -66,6 +65,9 @@ public override IEnumerable GetItems() item.OutputPath = GetOutputPath(outputPath, torrent); item.TotalSize = torrent.TotalSize; item.RemainingSize = torrent.LeftUntilDone; + item.SeedRatio = torrent.DownloadedEver <= 0 ? 0 : + (double) torrent.UploadedEver / torrent.DownloadedEver; + if (torrent.Eta >= 0) { item.RemainingTime = TimeSpan.FromSeconds(torrent.Eta); @@ -96,7 +98,9 @@ public override IEnumerable GetItems() item.Status = DownloadItemStatus.Downloading; } - item.CanMoveFiles = item.CanBeRemoved = torrent.Status == TransmissionTorrentStatus.Stopped; + item.CanMoveFiles = item.CanBeRemoved = + torrent.Status == TransmissionTorrentStatus.Stopped && + item.SeedRatio >= torrent.SeedRatioLimit; items.Add(item); } @@ -129,6 +133,7 @@ public override DownloadClientInfo GetStatus() protected override string AddFromMagnetLink(RemoteEpisode remoteEpisode, string hash, string magnetLink) { _proxy.AddTorrentFromUrl(magnetLink, GetDownloadDirectory(), Settings); + _proxy.SetTorrentSeedingConfiguration(hash, remoteEpisode.SeedConfiguration, Settings); var isRecentEpisode = remoteEpisode.IsRecentEpisode(); @@ -144,6 +149,7 @@ protected override string AddFromMagnetLink(RemoteEpisode remoteEpisode, string protected override string AddFromTorrentFile(RemoteEpisode remoteEpisode, string hash, string filename, byte[] fileContent) { _proxy.AddTorrentFromData(fileContent, GetDownloadDirectory(), Settings); + _proxy.SetTorrentSeedingConfiguration(hash, remoteEpisode.SeedConfiguration, Settings); var isRecentEpisode = remoteEpisode.IsRecentEpisode(); @@ -174,17 +180,13 @@ protected string GetDownloadDirectory() { return Settings.TvDirectory; } - else if (Settings.TvCategory.IsNotNullOrWhiteSpace()) - { - var config = _proxy.GetConfig(Settings); - var destDir = (string)config.GetValueOrDefault("download-dir"); - return string.Format("{0}/{1}", destDir.TrimEnd('/'), Settings.TvCategory); - } - else - { - return null; - } + if (!Settings.TvCategory.IsNotNullOrWhiteSpace()) return null; + + var config = _proxy.GetConfig(Settings); + var destDir = (string)config.GetValueOrDefault("download-dir"); + + return $"{destDir.TrimEnd('/')}/{Settings.TvCategory}"; } protected ValidationFailure TestConnection() diff --git a/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionProxy.cs b/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionProxy.cs index 10fc8345a5..567001e065 100644 --- a/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionProxy.cs +++ b/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionProxy.cs @@ -37,7 +37,7 @@ public TransmissionProxy(ICacheManager cacheManager, IHttpClient httpClient, Log _authSessionIDCache = cacheManager.GetCache(GetType(), "authSessionID"); } - + public List GetTorrents(TransmissionSettings settings) { var result = GetTorrentStatus(settings); @@ -77,8 +77,10 @@ public void AddTorrentFromData(byte[] torrentData, string downloadDirectory, Tra public void SetTorrentSeedingConfiguration(string hash, TorrentSeedConfiguration seedConfiguration, TransmissionSettings settings) { + if (seedConfiguration == null) return; + var arguments = new Dictionary(); - arguments.Add("ids", new string[] { hash }); + arguments.Add("ids", new[] { hash }); if (seedConfiguration.Ratio != null) { @@ -167,9 +169,12 @@ private TransmissionResponse GetTorrentStatus(IEnumerable hashStrings, T "leftUntilDone", "isFinished", "eta", - "errorString" + "errorString", + "uploadedEver", + "downloadedEver", + "seedRatioLimit" }; - + var arguments = new Dictionary(); arguments.Add("fields", fields); @@ -237,7 +242,7 @@ private void AuthenticateClient(HttpRequestBuilder requestBuilder, TransmissionS requestBuilder.SetHeader("X-Transmission-Session-Id", sessionId); } - + public TransmissionResponse ProcessRequest(string action, object arguments, TransmissionSettings settings) { try diff --git a/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionTorrent.cs b/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionTorrent.cs index 3845ce0b0f..c3369e3379 100644 --- a/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionTorrent.cs +++ b/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionTorrent.cs @@ -23,5 +23,11 @@ public class TransmissionTorrent public int SecondsDownloading { get; set; } public string ErrorString { get; set; } + + public long DownloadedEver { get; set; } + + public long UploadedEver { get; set; } + + public long SeedRatioLimit { get; set; } } } diff --git a/src/NzbDrone.Core/Download/Clients/rTorrent/RTorrent.cs b/src/NzbDrone.Core/Download/Clients/rTorrent/RTorrent.cs index 95fb2db198..535bcd5884 100644 --- a/src/NzbDrone.Core/Download/Clients/rTorrent/RTorrent.cs +++ b/src/NzbDrone.Core/Download/Clients/rTorrent/RTorrent.cs @@ -104,6 +104,7 @@ public override IEnumerable GetItems() item.TotalSize = torrent.TotalSize; item.RemainingSize = torrent.RemainingSize; item.Category = torrent.Category; + item.SeedRatio = torrent.Ratio; if (torrent.DownRate > 0) { diff --git a/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrent.cs b/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrent.cs index e02bf646ce..40da8f4838 100644 --- a/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrent.cs +++ b/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrent.cs @@ -40,6 +40,7 @@ protected override string AddFromMagnetLink(RemoteEpisode remoteEpisode, string { _proxy.AddTorrentFromUrl(magnetLink, Settings); _proxy.SetTorrentLabel(hash, Settings.TvCategory, Settings); + _proxy.SetTorrentSeedingConfiguration(hash, remoteEpisode.SeedConfiguration, Settings); var isRecentEpisode = remoteEpisode.IsRecentEpisode(); @@ -58,6 +59,7 @@ protected override string AddFromTorrentFile(RemoteEpisode remoteEpisode, string { _proxy.AddTorrentFromFile(filename, fileContent, Settings); _proxy.SetTorrentLabel(hash, Settings.TvCategory, Settings); + _proxy.SetTorrentSeedingConfiguration(hash, remoteEpisode.SeedConfiguration, Settings); var isRecentEpisode = remoteEpisode.IsRecentEpisode(); @@ -94,6 +96,8 @@ public override IEnumerable GetItems() item.Category = torrent.Label; item.DownloadClient = Definition.Name; item.RemainingSize = torrent.Remaining; + item.SeedRatio = torrent.Ratio; + if (torrent.Eta != -1) { item.RemainingTime = TimeSpan.FromSeconds(torrent.Eta); @@ -101,7 +105,7 @@ public override IEnumerable GetItems() var outputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(torrent.RootDownloadPath)); - if (outputPath == null || outputPath.FileName == torrent.Name) + if (outputPath.FileName == torrent.Name) { item.OutputPath = outputPath; } @@ -134,7 +138,9 @@ public override IEnumerable GetItems() } // 'Started' without 'Queued' is when the torrent is 'forced seeding' - item.CanMoveFiles = item.CanBeRemoved = (!torrent.Status.HasFlag(UTorrentTorrentStatus.Queued) && !torrent.Status.HasFlag(UTorrentTorrentStatus.Started)); + item.CanMoveFiles = item.CanBeRemoved = + !torrent.Status.HasFlag(UTorrentTorrentStatus.Queued) && + !torrent.Status.HasFlag(UTorrentTorrentStatus.Started); queueItems.Add(item); } diff --git a/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrentProxy.cs b/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrentProxy.cs index c65e37c28b..557ae0e6e1 100644 --- a/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrentProxy.cs +++ b/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrentProxy.cs @@ -69,14 +69,14 @@ public Dictionary GetConfig(UTorrentSettings settings) return configuration; } - public UTorrentResponse GetTorrents(string cacheID, UTorrentSettings settings) + public UTorrentResponse GetTorrents(string cacheId, UTorrentSettings settings) { var requestBuilder = BuildRequest(settings) .AddQueryParam("list", 1); - if (cacheID.IsNotNullOrWhiteSpace()) + if (cacheId.IsNotNullOrWhiteSpace()) { - requestBuilder.AddQueryParam("cid", cacheID); + requestBuilder.AddQueryParam("cid", cacheId); } var result = ProcessRequest(requestBuilder, settings); @@ -99,17 +99,19 @@ public void AddTorrentFromFile(string fileName, byte[] fileContent, UTorrentSett .Post() .AddQueryParam("action", "add-file") .AddQueryParam("path", string.Empty) - .AddFormUpload("torrent_file", fileName, fileContent, @"application/octet-stream"); + .AddFormUpload("torrent_file", fileName, fileContent); ProcessRequest(requestBuilder, settings); } public void SetTorrentSeedingConfiguration(string hash, TorrentSeedConfiguration seedConfiguration, UTorrentSettings settings) { + if (seedConfiguration == null) return; + var requestBuilder = BuildRequest(settings) .AddQueryParam("action", "setprops") .AddQueryParam("hash", hash); - + requestBuilder.AddQueryParam("s", "seed_override") .AddQueryParam("v", 1); diff --git a/src/NzbDrone.Core/Download/DownloadClientItem.cs b/src/NzbDrone.Core/Download/DownloadClientItem.cs index acd0b05799..3b5922c6af 100644 --- a/src/NzbDrone.Core/Download/DownloadClientItem.cs +++ b/src/NzbDrone.Core/Download/DownloadClientItem.cs @@ -15,6 +15,7 @@ public class DownloadClientItem public long TotalSize { get; set; } public long RemainingSize { get; set; } public TimeSpan? RemainingTime { get; set; } + public double? SeedRatio { get; set; } public OsPath OutputPath { get; set; } public string Message { get; set; } diff --git a/src/NzbDrone.Core/Download/DownloadService.cs b/src/NzbDrone.Core/Download/DownloadService.cs index 02f0a8d4ec..86d3c324b5 100644 --- a/src/NzbDrone.Core/Download/DownloadService.cs +++ b/src/NzbDrone.Core/Download/DownloadService.cs @@ -5,6 +5,7 @@ using NzbDrone.Common.Http; using NzbDrone.Common.Instrumentation.Extensions; using NzbDrone.Common.TPL; +using NzbDrone.Core.Configuration; using NzbDrone.Core.Download.Clients; using NzbDrone.Core.Exceptions; using NzbDrone.Core.Indexers; @@ -26,6 +27,7 @@ public class DownloadService : IDownloadService private readonly IIndexerStatusService _indexerStatusService; private readonly IRateLimitService _rateLimitService; private readonly IEventAggregator _eventAggregator; + private readonly ISeedConfigProvider _seedConfigProvider; private readonly Logger _logger; public DownloadService(IProvideDownloadClient downloadClientProvider, @@ -33,6 +35,7 @@ public DownloadService(IProvideDownloadClient downloadClientProvider, IIndexerStatusService indexerStatusService, IRateLimitService rateLimitService, IEventAggregator eventAggregator, + ISeedConfigProvider seedConfigProvider, Logger logger) { _downloadClientProvider = downloadClientProvider; @@ -40,6 +43,7 @@ public DownloadService(IProvideDownloadClient downloadClientProvider, _indexerStatusService = indexerStatusService; _rateLimitService = rateLimitService; _eventAggregator = eventAggregator; + _seedConfigProvider = seedConfigProvider; _logger = logger; } @@ -56,6 +60,9 @@ public void DownloadReport(RemoteEpisode remoteEpisode) throw new DownloadClientUnavailableException($"{remoteEpisode.Release.DownloadProtocol} Download client isn't configured yet"); } + // Get the seed configuration for this release. + remoteEpisode.SeedConfiguration = _seedConfigProvider.GetSeedConfiguration(remoteEpisode.Release); + // Limit grabs to 2 per second. if (remoteEpisode.Release.DownloadUrl.IsNotNullOrWhiteSpace() && !remoteEpisode.Release.DownloadUrl.StartsWith("magnet:")) { diff --git a/src/NzbDrone.Core/Indexers/BitMeTv/BitMeTvSettings.cs b/src/NzbDrone.Core/Indexers/BitMeTv/BitMeTvSettings.cs index a858192d57..e7d066da8c 100644 --- a/src/NzbDrone.Core/Indexers/BitMeTv/BitMeTvSettings.cs +++ b/src/NzbDrone.Core/Indexers/BitMeTv/BitMeTvSettings.cs @@ -47,6 +47,9 @@ public BitMeTvSettings() [FieldDefinition(4, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)] public int MinimumSeeders { get; set; } + [FieldDefinition(5, Type = FieldType.Textbox, Label = "Seed Ratio", HelpText = "The ratio a torrent should reach before stopping, empty is download client's default", Advanced = true)] + public double? SeedRatio { get; set; } + public NzbDroneValidationResult Validate() { return new NzbDroneValidationResult(Validator.Validate(this)); diff --git a/src/NzbDrone.Core/Indexers/BroadcastheNet/BroadcastheNetSettings.cs b/src/NzbDrone.Core/Indexers/BroadcastheNet/BroadcastheNetSettings.cs index 3890fef1fe..d259b9273c 100644 --- a/src/NzbDrone.Core/Indexers/BroadcastheNet/BroadcastheNetSettings.cs +++ b/src/NzbDrone.Core/Indexers/BroadcastheNet/BroadcastheNetSettings.cs @@ -32,6 +32,9 @@ public BroadcastheNetSettings() [FieldDefinition(2, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)] public int MinimumSeeders { get; set; } + [FieldDefinition(3, Type = FieldType.Textbox, Label = "Seed Ratio", HelpText = "The ratio a torrent should reach before stopping, empty is download client's default", Advanced = true)] + public double? SeedRatio { get; set; } + public NzbDroneValidationResult Validate() { return new NzbDroneValidationResult(Validator.Validate(this)); diff --git a/src/NzbDrone.Core/Indexers/HDBits/HDBitsSettings.cs b/src/NzbDrone.Core/Indexers/HDBits/HDBitsSettings.cs index ed0a513666..1a44e76fce 100644 --- a/src/NzbDrone.Core/Indexers/HDBits/HDBitsSettings.cs +++ b/src/NzbDrone.Core/Indexers/HDBits/HDBitsSettings.cs @@ -35,6 +35,9 @@ public HDBitsSettings() [FieldDefinition(3, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)] public int MinimumSeeders { get; set; } + [FieldDefinition(4, Type = FieldType.Textbox, Label = "Seed Ratio", HelpText = "The ratio a torrent should reach before stopping, empty is download client's default", Advanced = true)] + public double? SeedRatio { get; set; } + public NzbDroneValidationResult Validate() { return new NzbDroneValidationResult(Validator.Validate(this)); diff --git a/src/NzbDrone.Core/Indexers/IPTorrents/IPTorrentsSettings.cs b/src/NzbDrone.Core/Indexers/IPTorrents/IPTorrentsSettings.cs index 16d78430b2..8fd4f55a61 100644 --- a/src/NzbDrone.Core/Indexers/IPTorrents/IPTorrentsSettings.cs +++ b/src/NzbDrone.Core/Indexers/IPTorrents/IPTorrentsSettings.cs @@ -2,7 +2,6 @@ using FluentValidation; using NzbDrone.Common.Extensions; using NzbDrone.Core.Annotations; -using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.Validation; namespace NzbDrone.Core.Indexers.IPTorrents @@ -36,6 +35,9 @@ public IPTorrentsSettings() [FieldDefinition(1, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)] public int MinimumSeeders { get; set; } + [FieldDefinition(2, Type = FieldType.Textbox, Label = "Seed Ratio", HelpText = "The ratio a torrent should reach before stopping, empty is download client's default", Advanced = true)] + public double? SeedRatio { get; set; } + public NzbDroneValidationResult Validate() { return new NzbDroneValidationResult(Validator.Validate(this)); diff --git a/src/NzbDrone.Core/Indexers/ITorrentIndexerSettings.cs b/src/NzbDrone.Core/Indexers/ITorrentIndexerSettings.cs index 9ac4fafcb2..761a56b5f8 100644 --- a/src/NzbDrone.Core/Indexers/ITorrentIndexerSettings.cs +++ b/src/NzbDrone.Core/Indexers/ITorrentIndexerSettings.cs @@ -3,5 +3,7 @@ public interface ITorrentIndexerSettings : IIndexerSettings { int MinimumSeeders { get; set; } + + double? SeedRatio { get; set; } } } diff --git a/src/NzbDrone.Core/Indexers/Nyaa/NyaaSettings.cs b/src/NzbDrone.Core/Indexers/Nyaa/NyaaSettings.cs index 33661c6d46..587b4d15bd 100644 --- a/src/NzbDrone.Core/Indexers/Nyaa/NyaaSettings.cs +++ b/src/NzbDrone.Core/Indexers/Nyaa/NyaaSettings.cs @@ -2,6 +2,7 @@ using NzbDrone.Core.Annotations; using NzbDrone.Core.Validation; using System.Text.RegularExpressions; + namespace NzbDrone.Core.Indexers.Nyaa { public class NyaaSettingsValidator : AbstractValidator @@ -33,6 +34,9 @@ public NyaaSettings() [FieldDefinition(2, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)] public int MinimumSeeders { get; set; } + [FieldDefinition(3, Type = FieldType.Textbox, Label = "Seed Ratio", HelpText = "The ratio a torrent should reach before stopping, empty is download client's default", Advanced = true)] + public double? SeedRatio { get; set; } + public NzbDroneValidationResult Validate() { return new NzbDroneValidationResult(Validator.Validate(this)); diff --git a/src/NzbDrone.Core/Indexers/Rarbg/RarbgSettings.cs b/src/NzbDrone.Core/Indexers/Rarbg/RarbgSettings.cs index 2b4d76020f..50f1aa45d4 100644 --- a/src/NzbDrone.Core/Indexers/Rarbg/RarbgSettings.cs +++ b/src/NzbDrone.Core/Indexers/Rarbg/RarbgSettings.cs @@ -35,6 +35,9 @@ public RarbgSettings() [FieldDefinition(3, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)] public int MinimumSeeders { get; set; } + [FieldDefinition(4, Type = FieldType.Textbox, Label = "Seed Ratio", HelpText = "The ratio a torrent should reach before stopping, empty is download client's default", Advanced = true)] + public double? SeedRatio { get; set; } + public NzbDroneValidationResult Validate() { return new NzbDroneValidationResult(Validator.Validate(this)); diff --git a/src/NzbDrone.Core/Indexers/TorrentRss/TorrentRssIndexerSettings.cs b/src/NzbDrone.Core/Indexers/TorrentRss/TorrentRssIndexerSettings.cs index 8c24f9ba47..5ba3282c6c 100644 --- a/src/NzbDrone.Core/Indexers/TorrentRss/TorrentRssIndexerSettings.cs +++ b/src/NzbDrone.Core/Indexers/TorrentRss/TorrentRssIndexerSettings.cs @@ -35,6 +35,9 @@ public TorrentRssIndexerSettings() [FieldDefinition(3, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)] public int MinimumSeeders { get; set; } + [FieldDefinition(4, Type = FieldType.Textbox, Label = "Seed Ratio", HelpText = "The ratio a torrent should reach before stopping, empty is download client's default", Advanced = true)] + public double? SeedRatio { get; set; } + public NzbDroneValidationResult Validate() { return new NzbDroneValidationResult(validator.Validate(this)); diff --git a/src/NzbDrone.Core/Indexers/Torrentleech/TorrentleechSettings.cs b/src/NzbDrone.Core/Indexers/Torrentleech/TorrentleechSettings.cs index f7b06ec59d..8f3c5203e4 100644 --- a/src/NzbDrone.Core/Indexers/Torrentleech/TorrentleechSettings.cs +++ b/src/NzbDrone.Core/Indexers/Torrentleech/TorrentleechSettings.cs @@ -32,6 +32,9 @@ public TorrentleechSettings() [FieldDefinition(2, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)] public int MinimumSeeders { get; set; } + [FieldDefinition(3, Type = FieldType.Textbox, Label = "Seed Ratio", HelpText = "The ratio a torrent should reach before stopping, empty is download client's default", Advanced = true)] + public double? SeedRatio { get; set; } + public NzbDroneValidationResult Validate() { return new NzbDroneValidationResult(Validator.Validate(this)); diff --git a/src/NzbDrone.Core/Indexers/Torznab/TorznabSettings.cs b/src/NzbDrone.Core/Indexers/Torznab/TorznabSettings.cs index bbbfcfce54..d1dc0ed9f7 100644 --- a/src/NzbDrone.Core/Indexers/Torznab/TorznabSettings.cs +++ b/src/NzbDrone.Core/Indexers/Torznab/TorznabSettings.cs @@ -60,6 +60,9 @@ public TorznabSettings() [FieldDefinition(6, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)] public int MinimumSeeders { get; set; } + [FieldDefinition(7, Type = FieldType.Textbox, Label = "Seed Ratio", HelpText = "The ratio a torrent should reach before stopping, empty is download client's default", Advanced = true)] + public double? SeedRatio { get; set; } + public override NzbDroneValidationResult Validate() { return new NzbDroneValidationResult(Validator.Validate(this)); diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index 6f5f1fcf07..c69d1bd274 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -142,7 +142,9 @@ + + diff --git a/src/NzbDrone.Core/Parser/Model/RemoteEpisode.cs b/src/NzbDrone.Core/Parser/Model/RemoteEpisode.cs index 319606781c..3b99f250ce 100644 --- a/src/NzbDrone.Core/Parser/Model/RemoteEpisode.cs +++ b/src/NzbDrone.Core/Parser/Model/RemoteEpisode.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using NzbDrone.Core.Download.Clients; using NzbDrone.Core.Tv; namespace NzbDrone.Core.Parser.Model @@ -12,6 +13,7 @@ public class RemoteEpisode public Series Series { get; set; } public List Episodes { get; set; } public bool DownloadAllowed { get; set; } + public TorrentSeedConfiguration SeedConfiguration { get; set; } public bool IsRecentEpisode() { @@ -23,4 +25,4 @@ public override string ToString() return Release.Title; } } -} \ No newline at end of file +}