New: Ability to set a post-import label in Deluge, rTorrent, qBittorrent, and uTorrent

This commit is contained in:
jtpavlock 2019-06-09 12:54:53 -05:00 committed by Taloth
parent 1d77c40d0e
commit 39ea2dd32f
20 changed files with 304 additions and 67 deletions

View File

@ -31,6 +31,24 @@ namespace NzbDrone.Core.Download.Clients.Deluge
_proxy = proxy; _proxy = proxy;
} }
public override void MarkItemAsImported(DownloadClientItem downloadClientItem)
{
// set post-import category
if (Settings.TvImportedCategory.IsNotNullOrWhiteSpace() &&
Settings.TvImportedCategory != Settings.TvCategory)
{
try
{
_proxy.SetTorrentLabel(downloadClientItem.DownloadId.ToLower(), Settings.TvImportedCategory, Settings);
}
catch (DownloadClientUnavailableException)
{
_logger.Warn("Failed to set torrent post-import label \"{0}\" for {1} in Deluge. Does the label exist?",
Settings.TvImportedCategory, downloadClientItem.Title);
}
}
}
protected override string AddFromMagnetLink(RemoteEpisode remoteEpisode, string hash, string magnetLink) protected override string AddFromMagnetLink(RemoteEpisode remoteEpisode, string hash, string magnetLink)
{ {
var actualHash = _proxy.AddTorrentFromMagnet(magnetLink, Settings); var actualHash = _proxy.AddTorrentFromMagnet(magnetLink, Settings);
@ -40,13 +58,13 @@ namespace NzbDrone.Core.Download.Clients.Deluge
throw new DownloadClientException("Deluge failed to add magnet " + magnetLink); throw new DownloadClientException("Deluge failed to add magnet " + magnetLink);
} }
if (!Settings.TvCategory.IsNullOrWhiteSpace())
{
_proxy.SetLabel(actualHash, Settings.TvCategory, Settings);
}
_proxy.SetTorrentSeedingConfiguration(actualHash, remoteEpisode.SeedConfiguration, Settings); _proxy.SetTorrentSeedingConfiguration(actualHash, remoteEpisode.SeedConfiguration, Settings);
if (Settings.TvCategory.IsNotNullOrWhiteSpace())
{
_proxy.SetTorrentLabel(actualHash, Settings.TvCategory, Settings);
}
var isRecentEpisode = remoteEpisode.IsRecentEpisode(); var isRecentEpisode = remoteEpisode.IsRecentEpisode();
if (isRecentEpisode && Settings.RecentTvPriority == (int)DelugePriority.First || if (isRecentEpisode && Settings.RecentTvPriority == (int)DelugePriority.First ||
@ -69,9 +87,9 @@ namespace NzbDrone.Core.Download.Clients.Deluge
_proxy.SetTorrentSeedingConfiguration(actualHash, remoteEpisode.SeedConfiguration, Settings); _proxy.SetTorrentSeedingConfiguration(actualHash, remoteEpisode.SeedConfiguration, Settings);
if (!Settings.TvCategory.IsNullOrWhiteSpace()) if (Settings.TvCategory.IsNotNullOrWhiteSpace())
{ {
_proxy.SetLabel(actualHash, Settings.TvCategory, Settings); _proxy.SetTorrentLabel(actualHash, Settings.TvCategory, Settings);
} }
var isRecentEpisode = remoteEpisode.IsRecentEpisode(); var isRecentEpisode = remoteEpisode.IsRecentEpisode();
@ -91,7 +109,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge
{ {
IEnumerable<DelugeTorrent> torrents; IEnumerable<DelugeTorrent> torrents;
if (!Settings.TvCategory.IsNullOrWhiteSpace()) if (Settings.TvCategory.IsNotNullOrWhiteSpace())
{ {
torrents = _proxy.GetTorrentsByLabel(Settings.TvCategory, Settings); torrents = _proxy.GetTorrentsByLabel(Settings.TvCategory, Settings);
} }
@ -250,7 +268,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge
private ValidationFailure TestCategory() private ValidationFailure TestCategory()
{ {
if (Settings.TvCategory.IsNullOrWhiteSpace()) if (Settings.TvCategory.IsNullOrWhiteSpace() && Settings.TvImportedCategory.IsNullOrWhiteSpace())
{ {
return null; return null;
} }
@ -267,7 +285,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge
var labels = _proxy.GetAvailableLabels(Settings); var labels = _proxy.GetAvailableLabels(Settings);
if (!labels.Contains(Settings.TvCategory)) if (Settings.TvCategory.IsNotNullOrWhiteSpace() && !labels.Contains(Settings.TvCategory))
{ {
_proxy.AddLabel(Settings.TvCategory, Settings); _proxy.AddLabel(Settings.TvCategory, Settings);
labels = _proxy.GetAvailableLabels(Settings); labels = _proxy.GetAvailableLabels(Settings);
@ -276,7 +294,21 @@ namespace NzbDrone.Core.Download.Clients.Deluge
{ {
return new NzbDroneValidationFailure("TvCategory", "Configuration of label failed") return new NzbDroneValidationFailure("TvCategory", "Configuration of label failed")
{ {
DetailedDescription = "Sonarr as unable to add the label to Deluge." DetailedDescription = "Sonarr was unable to add the label to Deluge."
};
}
}
if (Settings.TvImportedCategory.IsNotNullOrWhiteSpace() && !labels.Contains(Settings.TvImportedCategory))
{
_proxy.AddLabel(Settings.TvImportedCategory, Settings);
labels = _proxy.GetAvailableLabels(Settings);
if (!labels.Contains(Settings.TvImportedCategory))
{
return new NzbDroneValidationFailure("TvImportedCategory", "Configuration of label failed")
{
DetailedDescription = "Sonarr was unable to add the label to Deluge."
}; };
} }
} }

View File

@ -19,7 +19,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge
string[] GetAvailablePlugins(DelugeSettings settings); string[] GetAvailablePlugins(DelugeSettings settings);
string[] GetEnabledPlugins(DelugeSettings settings); string[] GetEnabledPlugins(DelugeSettings settings);
string[] GetAvailableLabels(DelugeSettings settings); string[] GetAvailableLabels(DelugeSettings settings);
void SetLabel(string hash, string label, DelugeSettings settings); void SetTorrentLabel(string hash, string label, DelugeSettings settings);
void SetTorrentConfiguration(string hash, string key, object value, DelugeSettings settings); void SetTorrentConfiguration(string hash, string key, object value, DelugeSettings settings);
void SetTorrentSeedingConfiguration(string hash, TorrentSeedConfiguration seedConfiguration, DelugeSettings settings); void SetTorrentSeedingConfiguration(string hash, TorrentSeedConfiguration seedConfiguration, DelugeSettings settings);
void AddLabel(string label, DelugeSettings settings); void AddLabel(string label, DelugeSettings settings);
@ -185,7 +185,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge
ProcessRequest<object>(settings, "label.add", label); ProcessRequest<object>(settings, "label.add", label);
} }
public void SetLabel(string hash, string label, DelugeSettings settings) public void SetTorrentLabel(string hash, string label, DelugeSettings settings)
{ {
ProcessRequest<object>(settings, "label.set_torrent", hash, label); ProcessRequest<object>(settings, "label.set_torrent", hash, label);
} }

View File

@ -13,6 +13,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge
RuleFor(c => c.Port).InclusiveBetween(1, 65535); RuleFor(c => c.Port).InclusiveBetween(1, 65535);
RuleFor(c => c.TvCategory).Matches("^[-a-z]*$").WithMessage("Allowed characters a-z and -"); RuleFor(c => c.TvCategory).Matches("^[-a-z]*$").WithMessage("Allowed characters a-z and -");
RuleFor(c => c.TvImportedCategory).Matches("^[-a-z]*$").WithMessage("Allowed characters a-z and -");
} }
} }
@ -43,16 +44,19 @@ namespace NzbDrone.Core.Download.Clients.Deluge
[FieldDefinition(4, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated downloads, but it's optional")] [FieldDefinition(4, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated downloads, but it's optional")]
public string TvCategory { get; set; } public string TvCategory { get; set; }
[FieldDefinition(5, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(DelugePriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")] [FieldDefinition(5, Label = "Post-Import Category", Type = FieldType.Textbox, Advanced = true, HelpText = "Category for Sonarr to set after it has imported the download. Leave blank to disable this feature.")]
public string TvImportedCategory { get; set; }
[FieldDefinition(6, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(DelugePriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")]
public int RecentTvPriority { get; set; } public int RecentTvPriority { get; set; }
[FieldDefinition(6, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(DelugePriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")] [FieldDefinition(7, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(DelugePriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")]
public int OlderTvPriority { get; set; } public int OlderTvPriority { get; set; }
[FieldDefinition(7, Label = "Add Paused", Type = FieldType.Checkbox)] [FieldDefinition(8, Label = "Add Paused", Type = FieldType.Checkbox)]
public bool AddPaused { get; set; } public bool AddPaused { get; set; }
[FieldDefinition(8, Label = "Use SSL", Type = FieldType.Checkbox)] [FieldDefinition(9, Label = "Use SSL", Type = FieldType.Checkbox)]
public bool UseSsl { get; set; } public bool UseSsl { get; set; }
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()

View File

@ -33,6 +33,24 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
private IQBittorrentProxy Proxy => _proxySelector.GetProxy(Settings); private IQBittorrentProxy Proxy => _proxySelector.GetProxy(Settings);
public override void MarkItemAsImported(DownloadClientItem downloadClientItem)
{
// set post-import category
if (Settings.TvImportedCategory.IsNotNullOrWhiteSpace() &&
Settings.TvImportedCategory != Settings.TvCategory)
{
try
{
Proxy.SetTorrentLabel(downloadClientItem.DownloadId.ToLower(), Settings.TvImportedCategory, Settings);
}
catch (DownloadClientException)
{
_logger.Warn("Failed to set post-import torrent label \"{0}\" for {1} in qBittorrent. Does the label exist?",
Settings.TvImportedCategory, downloadClientItem.Title);
}
}
}
protected override string AddFromMagnetLink(RemoteEpisode remoteEpisode, string hash, string magnetLink) protected override string AddFromMagnetLink(RemoteEpisode remoteEpisode, string hash, string magnetLink)
{ {
if (!Proxy.GetConfig(Settings).DhtEnabled && !magnetLink.Contains("&tr=")) if (!Proxy.GetConfig(Settings).DhtEnabled && !magnetLink.Contains("&tr="))
@ -42,11 +60,6 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
Proxy.AddTorrentFromUrl(magnetLink, Settings); Proxy.AddTorrentFromUrl(magnetLink, Settings);
if (Settings.TvCategory.IsNotNullOrWhiteSpace())
{
Proxy.SetTorrentLabel(hash.ToLower(), Settings.TvCategory, Settings);
}
var isRecentEpisode = remoteEpisode.IsRecentEpisode(); var isRecentEpisode = remoteEpisode.IsRecentEpisode();
if (isRecentEpisode && Settings.RecentTvPriority == (int)QBittorrentPriority.First || if (isRecentEpisode && Settings.RecentTvPriority == (int)QBittorrentPriority.First ||
@ -69,18 +82,6 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
{ {
Proxy.AddTorrentFromFile(filename, fileContent, Settings); Proxy.AddTorrentFromFile(filename, fileContent, Settings);
try
{
if (Settings.TvCategory.IsNotNullOrWhiteSpace())
{
Proxy.SetTorrentLabel(hash.ToLower(), Settings.TvCategory, Settings);
}
}
catch (Exception ex)
{
_logger.Warn(ex, "Failed to set the torrent label for {0}.", filename);
}
try try
{ {
var isRecentEpisode = remoteEpisode.IsRecentEpisode(); var isRecentEpisode = remoteEpisode.IsRecentEpisode();
@ -216,6 +217,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
{ {
failures.AddIfNotNull(TestConnection()); failures.AddIfNotNull(TestConnection());
if (failures.HasErrors()) return; if (failures.HasErrors()) return;
failures.AddIfNotNull(TestCategory());
failures.AddIfNotNull(TestPrioritySupport()); failures.AddIfNotNull(TestPrioritySupport());
failures.AddIfNotNull(TestGetTorrents()); failures.AddIfNotNull(TestGetTorrents());
} }
@ -293,6 +295,53 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
return null; return null;
} }
private ValidationFailure TestCategory()
{
if (Settings.TvCategory.IsNullOrWhiteSpace() && Settings.TvImportedCategory.IsNullOrWhiteSpace())
{
return null;
}
// api v1 doesn't need to check/add categories as it's done on set
var version = _proxySelector.GetProxy(Settings, true).GetApiVersion(Settings);
if (version < Version.Parse("2.0"))
{
return null;
}
Dictionary<string, QBittorrentLabel> labels = Proxy.GetLabels(Settings);
if (Settings.TvCategory.IsNotNullOrWhiteSpace() && !labels.ContainsKey(Settings.TvCategory))
{
Proxy.AddLabel(Settings.TvCategory, Settings);
labels = Proxy.GetLabels(Settings);
if (!labels.ContainsKey(Settings.TvCategory))
{
return new NzbDroneValidationFailure("TvCategory", "Configuration of label failed")
{
DetailedDescription = "Sonarr was unable to add the label to qBittorrent."
};
}
}
if (Settings.TvImportedCategory.IsNotNullOrWhiteSpace() && !labels.ContainsKey(Settings.TvImportedCategory))
{
Proxy.AddLabel(Settings.TvImportedCategory, Settings);
labels = Proxy.GetLabels(Settings);
if (!labels.ContainsKey(Settings.TvImportedCategory))
{
return new NzbDroneValidationFailure("TvImportedCategory", "Configuration of label failed")
{
DetailedDescription = "Sonarr was unable to add the label to qBittorrent."
};
}
}
return null;
}
private ValidationFailure TestPrioritySupport() private ValidationFailure TestPrioritySupport()
{ {
var recentPriorityDefault = Settings.RecentTvPriority == (int)QBittorrentPriority.Last; var recentPriorityDefault = Settings.RecentTvPriority == (int)QBittorrentPriority.Last;

View File

@ -0,0 +1,10 @@
using Newtonsoft.Json;
namespace NzbDrone.Core.Download.Clients.QBittorrent
{
public class QBittorrentLabel
{
public string Name { get; set; }
public string SavePath { get; set; }
}
}

View File

@ -23,6 +23,8 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
void RemoveTorrent(string hash, Boolean removeData, QBittorrentSettings settings); void RemoveTorrent(string hash, Boolean removeData, QBittorrentSettings settings);
void SetTorrentLabel(string hash, string label, QBittorrentSettings settings); void SetTorrentLabel(string hash, string label, QBittorrentSettings settings);
void AddLabel(string label, QBittorrentSettings settings);
Dictionary<string, QBittorrentLabel> GetLabels(QBittorrentSettings settings);
void SetTorrentSeedingConfiguration(string hash, TorrentSeedConfiguration seedConfiguration, QBittorrentSettings settings); void SetTorrentSeedingConfiguration(string hash, TorrentSeedConfiguration seedConfiguration, QBittorrentSettings settings);
void MoveTorrentToTopInQueue(string hash, QBittorrentSettings settings); void MoveTorrentToTopInQueue(string hash, QBittorrentSettings settings);
void PauseTorrent(string hash, QBittorrentSettings settings); void PauseTorrent(string hash, QBittorrentSettings settings);

View File

@ -190,6 +190,19 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
} }
} }
public void AddLabel(string label, QBittorrentSettings settings)
{
var request = BuildRequest(settings).Resource("/command/addCategory")
.Post()
.AddFormParameter("category", label);
ProcessRequest(request, settings);
}
public Dictionary<string, QBittorrentLabel> GetLabels(QBittorrentSettings settings)
{
throw new NotSupportedException("qBittorrent api v1 does not support getting all torrent categories");
}
public void SetTorrentSeedingConfiguration(string hash, TorrentSeedConfiguration seedConfiguration, QBittorrentSettings settings) public void SetTorrentSeedingConfiguration(string hash, TorrentSeedConfiguration seedConfiguration, QBittorrentSettings settings)
{ {
// Not supported on api v1 // Not supported on api v1

View File

@ -110,6 +110,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
var request = BuildRequest(settings).Resource("/api/v2/torrents/add") var request = BuildRequest(settings).Resource("/api/v2/torrents/add")
.Post() .Post()
.AddFormParameter("urls", torrentUrl); .AddFormParameter("urls", torrentUrl);
if (settings.TvCategory.IsNotNullOrWhiteSpace()) if (settings.TvCategory.IsNotNullOrWhiteSpace())
{ {
request.AddFormParameter("category", settings.TvCategory); request.AddFormParameter("category", settings.TvCategory);
@ -177,6 +178,20 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
ProcessRequest(request, settings); ProcessRequest(request, settings);
} }
public void AddLabel(string label, QBittorrentSettings settings)
{
var request = BuildRequest(settings).Resource("/api/v2/torrents/createCategory")
.Post()
.AddFormParameter("category", label);
ProcessRequest(request, settings);
}
public Dictionary<string, QBittorrentLabel> GetLabels(QBittorrentSettings settings)
{
var request = BuildRequest(settings).Resource("/api/v2/torrents/categories");
return Json.Deserialize<Dictionary<string, QBittorrentLabel>>(ProcessRequest(request, settings));
}
public void SetTorrentSeedingConfiguration(string hash, TorrentSeedConfiguration seedConfiguration, QBittorrentSettings settings) public void SetTorrentSeedingConfiguration(string hash, TorrentSeedConfiguration seedConfiguration, QBittorrentSettings settings)
{ {
var ratioLimit = seedConfiguration.Ratio.HasValue ? seedConfiguration.Ratio : -2; var ratioLimit = seedConfiguration.Ratio.HasValue ? seedConfiguration.Ratio : -2;

View File

@ -11,6 +11,9 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
{ {
RuleFor(c => c.Host).ValidHost(); RuleFor(c => c.Host).ValidHost();
RuleFor(c => c.Port).InclusiveBetween(1, 65535); RuleFor(c => c.Port).InclusiveBetween(1, 65535);
RuleFor(c => c.TvCategory).Matches("^[^/\\\\](((?!//)[^\\\\])*[^/\\\\])?$").WithMessage("Can not contain '\\', '//', or start/end with '/'");
RuleFor(c => c.TvImportedCategory).Matches("^[^/\\\\](((?!//)[^\\\\])*[^/\\\\])?$").WithMessage("Can not contain '\\', '//', or start/end with '/'");
} }
} }
@ -40,16 +43,19 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
[FieldDefinition(4, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated downloads, but it's optional")] [FieldDefinition(4, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated downloads, but it's optional")]
public string TvCategory { get; set; } public string TvCategory { get; set; }
[FieldDefinition(5, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(QBittorrentPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")] [FieldDefinition(5, Label = "Post-Import Category", Type = FieldType.Textbox, Advanced = true, HelpText = "Category for Sonarr to set after it has imported the download. Leave blank to disable this feature.")]
public string TvImportedCategory { get; set; }
[FieldDefinition(6, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(QBittorrentPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")]
public int RecentTvPriority { get; set; } public int RecentTvPriority { get; set; }
[FieldDefinition(6, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(QBittorrentPriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")] [FieldDefinition(7, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(QBittorrentPriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")]
public int OlderTvPriority { get; set; } public int OlderTvPriority { get; set; }
[FieldDefinition(7, Label = "Initial State", Type = FieldType.Select, SelectOptions = typeof(QBittorrentState), HelpText = "Initial state for torrents added to qBittorrent")] [FieldDefinition(8, Label = "Initial State", Type = FieldType.Select, SelectOptions = typeof(QBittorrentState), HelpText = "Initial state for torrents added to qBittorrent")]
public int InitialState { get; set; } public int InitialState { get; set; }
[FieldDefinition(8, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use a secure connection. See Options -> Web UI -> 'Use HTTPS instead of HTTP' in qBittorrent.")] [FieldDefinition(9, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use a secure connection. See Options -> Web UI -> 'Use HTTPS instead of HTTP' in qBittorrent.")]
public bool UseSsl { get; set; } public bool UseSsl { get; set; }
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()

View File

@ -37,6 +37,24 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
_rTorrentDirectoryValidator = rTorrentDirectoryValidator; _rTorrentDirectoryValidator = rTorrentDirectoryValidator;
} }
public override void MarkItemAsImported(DownloadClientItem downloadClientItem)
{
// set post-import category
if (Settings.TvImportedCategory.IsNotNullOrWhiteSpace() &&
Settings.TvImportedCategory != Settings.TvCategory)
{
try
{
_proxy.SetTorrentLabel(downloadClientItem.DownloadId.ToLower(), Settings.TvImportedCategory, Settings);
}
catch (Exception ex)
{
_logger.Warn(ex, "Failed to set torrent post-import label \"{0}\" for {1} in rTorrent. Does the label exist?",
Settings.TvImportedCategory, downloadClientItem.Title);
}
}
}
protected override string AddFromMagnetLink(RemoteEpisode remoteEpisode, string hash, string magnetLink) protected override string AddFromMagnetLink(RemoteEpisode remoteEpisode, string hash, string magnetLink)
{ {
var priority = (RTorrentPriority)(remoteEpisode.IsRecentEpisode() ? Settings.RecentTvPriority : Settings.OlderTvPriority); var priority = (RTorrentPriority)(remoteEpisode.IsRecentEpisode() ? Settings.RecentTvPriority : Settings.OlderTvPriority);

View File

@ -18,6 +18,7 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
void AddTorrentFromUrl(string torrentUrl, string label, RTorrentPriority priority, string directory, RTorrentSettings settings); void AddTorrentFromUrl(string torrentUrl, string label, RTorrentPriority priority, string directory, RTorrentSettings settings);
void AddTorrentFromFile(string fileName, byte[] fileContent, string label, RTorrentPriority priority, string directory, RTorrentSettings settings); void AddTorrentFromFile(string fileName, byte[] fileContent, string label, RTorrentPriority priority, string directory, RTorrentSettings settings);
void RemoveTorrent(string hash, RTorrentSettings settings); void RemoveTorrent(string hash, RTorrentSettings settings);
void SetTorrentLabel(string hash, string label, RTorrentSettings settings);
bool HasHashTorrent(string hash, RTorrentSettings settings); bool HasHashTorrent(string hash, RTorrentSettings settings);
} }
@ -44,6 +45,9 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
[XmlRpcMethod("d.name")] [XmlRpcMethod("d.name")]
string GetName(string hash); string GetName(string hash);
[XmlRpcMethod("d.custom1.set")]
string SetLabel(string hash, string label);
[XmlRpcMethod("system.client_version")] [XmlRpcMethod("system.client_version")]
string GetVersion(); string GetVersion();
} }
@ -90,20 +94,20 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
foreach (object[] torrent in ret) foreach (object[] torrent in ret)
{ {
var labelDecoded = System.Web.HttpUtility.UrlDecode((string) torrent[3]); var labelDecoded = System.Web.HttpUtility.UrlDecode((string)torrent[3]);
var item = new RTorrentTorrent(); var item = new RTorrentTorrent();
item.Name = (string) torrent[0]; item.Name = (string)torrent[0];
item.Hash = (string) torrent[1]; item.Hash = (string)torrent[1];
item.Path = (string) torrent[2]; item.Path = (string)torrent[2];
item.Category = labelDecoded; item.Category = labelDecoded;
item.TotalSize = (long) torrent[4]; item.TotalSize = (long)torrent[4];
item.RemainingSize = (long) torrent[5]; item.RemainingSize = (long)torrent[5];
item.DownRate = (long) torrent[6]; item.DownRate = (long)torrent[6];
item.Ratio = (long) torrent[7]; item.Ratio = (long)torrent[7];
item.IsOpen = Convert.ToBoolean((long) torrent[8]); item.IsOpen = Convert.ToBoolean((long)torrent[8]);
item.IsActive = Convert.ToBoolean((long) torrent[9]); item.IsActive = Convert.ToBoolean((long)torrent[9]);
item.IsFinished = Convert.ToBoolean((long) torrent[10]); item.IsFinished = Convert.ToBoolean((long)torrent[10]);
items.Add(item); items.Add(item);
} }
@ -157,6 +161,19 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
} }
} }
public void SetTorrentLabel(string hash, string label, RTorrentSettings settings)
{
_logger.Debug("Executing remote method: d.custom1.set");
var client = BuildClient(settings);
var response = ExecuteRequest(() => client.SetLabel(hash, label));
if (response != label)
{
throw new DownloadClientException("Could not set label to {1} for torrent: {0}.", hash, label);
}
}
public void RemoveTorrent(string hash, RTorrentSettings settings) public void RemoveTorrent(string hash, RTorrentSettings settings)
{ {
_logger.Debug("Executing remote method: d.erase"); _logger.Debug("Executing remote method: d.erase");

View File

@ -52,16 +52,19 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
[FieldDefinition(6, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated downloads, but it's optional.")] [FieldDefinition(6, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated downloads, but it's optional.")]
public string TvCategory { get; set; } public string TvCategory { get; set; }
[FieldDefinition(7, Label = "Directory", Type = FieldType.Textbox, Advanced = true, HelpText = "Optional location to put downloads in, leave blank to use the default rTorrent location")] [FieldDefinition(7, Label = "Post-Import Category", Type = FieldType.Textbox, Advanced = true, HelpText = "Category for Sonarr to set after it has imported the download. Leave blank to disable this feature.")]
public string TvImportedCategory { get; set; }
[FieldDefinition(8, Label = "Directory", Type = FieldType.Textbox, Advanced = true, HelpText = "Optional location to put downloads in, leave blank to use the default rTorrent location")]
public string TvDirectory { get; set; } public string TvDirectory { get; set; }
[FieldDefinition(8, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(RTorrentPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")] [FieldDefinition(9, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(RTorrentPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")]
public int RecentTvPriority { get; set; } public int RecentTvPriority { get; set; }
[FieldDefinition(9, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(RTorrentPriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")] [FieldDefinition(10, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(RTorrentPriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")]
public int OlderTvPriority { get; set; } public int OlderTvPriority { get; set; }
[FieldDefinition(10, Label = "Add Stopped", Type = FieldType.Checkbox, HelpText = "Enabling will prevent magnets from downloading before downloading")] [FieldDefinition(11, Label = "Add Stopped", Type = FieldType.Checkbox, HelpText = "Enabling will prevent magnets from downloading before downloading")]
public bool AddStopped { get; set; } public bool AddStopped { get; set; }
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()

View File

@ -36,12 +36,32 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
_torrentCache = cacheManager.GetCache<UTorrentTorrentCache>(GetType(), "differentialTorrents"); _torrentCache = cacheManager.GetCache<UTorrentTorrentCache>(GetType(), "differentialTorrents");
} }
public override void MarkItemAsImported(DownloadClientItem downloadClientItem)
{
// set post-import category
if (Settings.TvImportedCategory.IsNotNullOrWhiteSpace() &&
Settings.TvImportedCategory != Settings.TvCategory)
{
_proxy.SetTorrentLabel(downloadClientItem.DownloadId.ToLower(), Settings.TvImportedCategory, Settings);
// old label must be explicitly removed
if (Settings.TvCategory.IsNotNullOrWhiteSpace())
{
_proxy.RemoveTorrentLabel(downloadClientItem.DownloadId.ToLower(), Settings.TvCategory, Settings);
}
}
}
protected override string AddFromMagnetLink(RemoteEpisode remoteEpisode, string hash, string magnetLink) protected override string AddFromMagnetLink(RemoteEpisode remoteEpisode, string hash, string magnetLink)
{ {
_proxy.AddTorrentFromUrl(magnetLink, Settings); _proxy.AddTorrentFromUrl(magnetLink, Settings);
_proxy.SetTorrentLabel(hash, Settings.TvCategory, Settings);
_proxy.SetTorrentSeedingConfiguration(hash, remoteEpisode.SeedConfiguration, Settings); _proxy.SetTorrentSeedingConfiguration(hash, remoteEpisode.SeedConfiguration, Settings);
if (Settings.TvCategory.IsNotNullOrWhiteSpace())
{
_proxy.SetTorrentLabel(hash, Settings.TvCategory, Settings);
}
var isRecentEpisode = remoteEpisode.IsRecentEpisode(); var isRecentEpisode = remoteEpisode.IsRecentEpisode();
if (isRecentEpisode && Settings.RecentTvPriority == (int)UTorrentPriority.First || if (isRecentEpisode && Settings.RecentTvPriority == (int)UTorrentPriority.First ||
@ -58,9 +78,13 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
protected override string AddFromTorrentFile(RemoteEpisode remoteEpisode, string hash, string filename, byte[] fileContent) protected override string AddFromTorrentFile(RemoteEpisode remoteEpisode, string hash, string filename, byte[] fileContent)
{ {
_proxy.AddTorrentFromFile(filename, fileContent, Settings); _proxy.AddTorrentFromFile(filename, fileContent, Settings);
_proxy.SetTorrentLabel(hash, Settings.TvCategory, Settings);
_proxy.SetTorrentSeedingConfiguration(hash, remoteEpisode.SeedConfiguration, Settings); _proxy.SetTorrentSeedingConfiguration(hash, remoteEpisode.SeedConfiguration, Settings);
if (Settings.TvCategory.IsNotNullOrWhiteSpace())
{
_proxy.SetTorrentLabel(hash, Settings.TvCategory, Settings);
}
var isRecentEpisode = remoteEpisode.IsRecentEpisode(); var isRecentEpisode = remoteEpisode.IsRecentEpisode();
if (isRecentEpisode && Settings.RecentTvPriority == (int)UTorrentPriority.First || if (isRecentEpisode && Settings.RecentTvPriority == (int)UTorrentPriority.First ||

View File

@ -21,6 +21,7 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
void RemoveTorrent(string hash, bool removeData, UTorrentSettings settings); void RemoveTorrent(string hash, bool removeData, UTorrentSettings settings);
void SetTorrentLabel(string hash, string label, UTorrentSettings settings); void SetTorrentLabel(string hash, string label, UTorrentSettings settings);
void RemoveTorrentLabel(string hash, string label, UTorrentSettings settings);
void MoveTorrentToTopInQueue(string hash, UTorrentSettings settings); void MoveTorrentToTopInQueue(string hash, UTorrentSettings settings);
void SetState(string hash, UTorrentState state, UTorrentSettings settings); void SetState(string hash, UTorrentState state, UTorrentSettings settings);
} }
@ -151,6 +152,20 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
ProcessRequest(requestBuilder, settings); ProcessRequest(requestBuilder, settings);
} }
public void RemoveTorrentLabel(string hash, string label, UTorrentSettings settings)
{
var requestBuilder = BuildRequest(settings)
.AddQueryParam("action", "setprops")
.AddQueryParam("hash", hash);
requestBuilder.AddQueryParam("s", "label")
.AddQueryParam("v", label)
.AddQueryParam("s", "label")
.AddQueryParam("v", "");
ProcessRequest(requestBuilder, settings);
}
public void MoveTorrentToTopInQueue(string hash, UTorrentSettings settings) public void MoveTorrentToTopInQueue(string hash, UTorrentSettings settings)
{ {
var requestBuilder = BuildRequest(settings) var requestBuilder = BuildRequest(settings)

View File

@ -41,13 +41,16 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
[FieldDefinition(4, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated downloads, but it's optional")] [FieldDefinition(4, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated downloads, but it's optional")]
public string TvCategory { get; set; } public string TvCategory { get; set; }
[FieldDefinition(5, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(UTorrentPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")] [FieldDefinition(5, Label = "Post-Import Category", Type = FieldType.Textbox, Advanced = true, HelpText = "Category for Sonarr to set after it has imported the download. Leave blank to disable this feature.")]
public string TvImportedCategory { get; set; }
[FieldDefinition(6, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(UTorrentPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")]
public int RecentTvPriority { get; set; } public int RecentTvPriority { get; set; }
[FieldDefinition(6, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(UTorrentPriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")] [FieldDefinition(7, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(UTorrentPriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")]
public int OlderTvPriority { get; set; } public int OlderTvPriority { get; set; }
[FieldDefinition(7, Label = "Initial State", Type = FieldType.Select, SelectOptions = typeof(UTorrentState), HelpText = "Initial state for torrents added to uTorrent")] [FieldDefinition(8, Label = "Initial State", Type = FieldType.Select, SelectOptions = typeof(UTorrentState), HelpText = "Initial state for torrents added to uTorrent")]
public int IntialState { get; set; } public int IntialState { get; set; }
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()

View File

@ -127,7 +127,6 @@ namespace NzbDrone.Core.Download
trackedDownload.Warn(statusMessages); trackedDownload.Warn(statusMessages);
} }
} }
} }
} }

View File

@ -147,5 +147,10 @@ namespace NzbDrone.Core.Download
return null; return null;
} }
public virtual void MarkItemAsImported(DownloadClientItem downloadClientItem)
{
throw new NotSupportedException(this.Name + " does not support marking items as imported");
}
} }
} }

View File

@ -35,15 +35,17 @@ namespace NzbDrone.Core.Download
public void Handle(DownloadCompletedEvent message) public void Handle(DownloadCompletedEvent message)
{ {
if (!_configService.RemoveCompletedDownloads || if (_configService.RemoveCompletedDownloads &&
message.TrackedDownload.DownloadItem.Removed || !message.TrackedDownload.DownloadItem.Removed &&
!message.TrackedDownload.DownloadItem.CanBeRemoved || message.TrackedDownload.DownloadItem.CanBeRemoved &&
message.TrackedDownload.DownloadItem.Status == DownloadItemStatus.Downloading) message.TrackedDownload.DownloadItem.Status != DownloadItemStatus.Downloading)
{ {
return; RemoveFromDownloadClient(message.TrackedDownload);
} }
else
RemoveFromDownloadClient(message.TrackedDownload); {
MarkItemAsImported(message.TrackedDownload);
}
} }
public void Handle(DownloadFailedEvent message) public void Handle(DownloadFailedEvent message)
@ -74,7 +76,25 @@ namespace NzbDrone.Core.Download
} }
catch (Exception e) catch (Exception e)
{ {
_logger.Error(e, "Couldn't remove item from client {0}", trackedDownload.DownloadItem.Title); _logger.Error(e, "Couldn't remove item {0} from client {1}", trackedDownload.DownloadItem.Title, downloadClient.Name);
}
}
private void MarkItemAsImported(TrackedDownload trackedDownload)
{
var downloadClient = _downloadClientProvider.Get(trackedDownload.DownloadClient);
try
{
_logger.Debug("[{0}] Marking download as imported from {1}", trackedDownload.DownloadItem.Title, trackedDownload.DownloadItem.DownloadClient);
downloadClient.MarkItemAsImported(trackedDownload.DownloadItem);
}
catch (NotSupportedException e)
{
_logger.Debug(e.Message);
}
catch (Exception e)
{
_logger.Error(e, "Couldn't mark item {0} as imported from client {1}", trackedDownload.DownloadItem.Title, downloadClient.Name);
} }
} }
} }

View File

@ -13,5 +13,6 @@ namespace NzbDrone.Core.Download
IEnumerable<DownloadClientItem> GetItems(); IEnumerable<DownloadClientItem> GetItems();
void RemoveItem(string downloadId, bool deleteData); void RemoveItem(string downloadId, bool deleteData);
DownloadClientInfo GetStatus(); DownloadClientInfo GetStatus();
void MarkItemAsImported(DownloadClientItem downloadClientItem);
} }
} }

View File

@ -152,6 +152,7 @@
<Compile Include="DecisionEngine\Specifications\UpgradeAllowedSpecification.cs" /> <Compile Include="DecisionEngine\Specifications\UpgradeAllowedSpecification.cs" />
<Compile Include="Exceptions\DownloadClientRejectedReleaseException.cs" /> <Compile Include="Exceptions\DownloadClientRejectedReleaseException.cs" />
<Compile Include="Exceptions\SearchFailedException.cs" /> <Compile Include="Exceptions\SearchFailedException.cs" />
<Compile Include="Download\Clients\QBittorrent\QBittorrentLabel.cs" />
<Compile Include="Extras\Metadata\MetadataSectionType.cs" /> <Compile Include="Extras\Metadata\MetadataSectionType.cs" />
<Compile Include="Download\Aggregation\RemoteEpisodeAggregationService.cs" /> <Compile Include="Download\Aggregation\RemoteEpisodeAggregationService.cs" />
<Compile Include="Download\Aggregation\Aggregators\AggregatePreferredWordScore.cs" /> <Compile Include="Download\Aggregation\Aggregators\AggregatePreferredWordScore.cs" />