New: Choose download folder for rTorrent

Closes #626
pull/674/head
Mark McDowall 9 years ago
parent 889d3d1207
commit 1b65ead75d

@ -10,6 +10,7 @@ using NzbDrone.Core.MediaFiles.TorrentInfo;
using NLog; using NLog;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
using FluentValidation.Results; using FluentValidation.Results;
using NzbDrone.Core.Download.Clients.rTorrent;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.RemotePathMappings; using NzbDrone.Core.RemotePathMappings;
using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.ThingiProvider;
@ -19,6 +20,7 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
public class RTorrent : TorrentClientBase<RTorrentSettings> public class RTorrent : TorrentClientBase<RTorrentSettings>
{ {
private readonly IRTorrentProxy _proxy; private readonly IRTorrentProxy _proxy;
private readonly IRTorrentDirectoryValidator _rTorrentDirectoryValidator;
public RTorrent(IRTorrentProxy proxy, public RTorrent(IRTorrentProxy proxy,
ITorrentFileInfoReader torrentFileInfoReader, ITorrentFileInfoReader torrentFileInfoReader,
@ -26,10 +28,12 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
IConfigService configService, IConfigService configService,
IDiskProvider diskProvider, IDiskProvider diskProvider,
IRemotePathMappingService remotePathMappingService, IRemotePathMappingService remotePathMappingService,
IRTorrentDirectoryValidator rTorrentDirectoryValidator,
Logger logger) Logger logger)
: base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger) : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger)
{ {
_proxy = proxy; _proxy = proxy;
_rTorrentDirectoryValidator = rTorrentDirectoryValidator;
} }
protected override string AddFromMagnetLink(RemoteEpisode remoteEpisode, string hash, string magnetLink) protected override string AddFromMagnetLink(RemoteEpisode remoteEpisode, string hash, string magnetLink)
@ -40,6 +44,7 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
var TRIES = 5; var TRIES = 5;
var RETRY_DELAY = 500; //ms var RETRY_DELAY = 500; //ms
var ready = false; var ready = false;
for (var i = 0; i < TRIES; i++) for (var i = 0; i < TRIES; i++)
{ {
ready = _proxy.HasHashTorrent(hash, Settings); ready = _proxy.HasHashTorrent(hash, Settings);
@ -55,9 +60,10 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
{ {
_proxy.SetTorrentLabel(hash, Settings.TvCategory, Settings); _proxy.SetTorrentLabel(hash, Settings.TvCategory, Settings);
var priority = (RTorrentPriority)(remoteEpisode.IsRecentEpisode() ? SetPriority(remoteEpisode, hash);
Settings.RecentTvPriority : Settings.OlderTvPriority); SetDownloadDirectory(hash);
_proxy.SetTorrentPriority(hash, Settings, priority);
_proxy.StartTorrent(hash, Settings);
return hash; return hash;
} }
@ -74,12 +80,12 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
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.SetTorrentLabel(hash, Settings.TvCategory, Settings);
var priority = (RTorrentPriority)(remoteEpisode.IsRecentEpisode() ? SetPriority(remoteEpisode, hash);
Settings.RecentTvPriority : Settings.OlderTvPriority); SetDownloadDirectory(hash);
_proxy.SetTorrentPriority(hash, Settings, priority);
_proxy.StartTorrent(hash, Settings);
return hash; return hash;
} }
@ -182,6 +188,7 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
failures.AddIfNotNull(TestConnection()); failures.AddIfNotNull(TestConnection());
if (failures.Any()) return; if (failures.Any()) return;
failures.AddIfNotNull(TestGetTorrents()); failures.AddIfNotNull(TestGetTorrents());
failures.AddIfNotNull(TestDirectory());
} }
private ValidationFailure TestConnection() private ValidationFailure TestConnection()
@ -218,5 +225,31 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
return null; return null;
} }
private ValidationFailure TestDirectory()
{
var result = _rTorrentDirectoryValidator.Validate(Settings);
if (result.IsValid)
{
return null;
}
return result.Errors.First();
}
private void SetPriority(RemoteEpisode remoteEpisode, string hash)
{
var priority = (RTorrentPriority)(remoteEpisode.IsRecentEpisode() ? Settings.RecentTvPriority : Settings.OlderTvPriority);
_proxy.SetTorrentPriority(hash, priority, Settings);
}
private void SetDownloadDirectory(string hash)
{
if (Settings.TvDirectory.IsNotNullOrWhiteSpace())
{
_proxy.SetTorrentDownloadDirectory(hash, Settings.TvDirectory, Settings);
}
}
} }
} }

@ -0,0 +1,29 @@
using FluentValidation;
using FluentValidation.Results;
using NzbDrone.Core.Download.Clients.RTorrent;
using NzbDrone.Core.Validation.Paths;
namespace NzbDrone.Core.Download.Clients.rTorrent
{
public interface IRTorrentDirectoryValidator
{
ValidationResult Validate(RTorrentSettings instance);
}
public class RTorrentDirectoryValidator : AbstractValidator<RTorrentSettings>, IRTorrentDirectoryValidator
{
public RTorrentDirectoryValidator(RootFolderValidator rootFolderValidator,
PathExistsValidator pathExistsValidator,
DroneFactoryValidator droneFactoryValidator,
MappedNetworkDriveValidator mappedNetworkDriveValidator)
{
RuleFor(c => c.TvDirectory).Cascade(CascadeMode.StopOnFirstFailure)
.IsValidPath()
.SetValidator(rootFolderValidator)
.SetValidator(droneFactoryValidator)
.SetValidator(mappedNetworkDriveValidator)
.SetValidator(pathExistsValidator)
.When(c => c.Host == "localhost" || c.Host == "127.0.0.1");
}
}
}

@ -16,9 +16,11 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
void AddTorrentFromUrl(string torrentUrl, RTorrentSettings settings); void AddTorrentFromUrl(string torrentUrl, RTorrentSettings settings);
void AddTorrentFromFile(string fileName, byte[] fileContent, RTorrentSettings settings); void AddTorrentFromFile(string fileName, byte[] fileContent, RTorrentSettings settings);
void RemoveTorrent(string hash, RTorrentSettings settings); void RemoveTorrent(string hash, RTorrentSettings settings);
void SetTorrentPriority(string hash, RTorrentSettings settings, RTorrentPriority priority); void SetTorrentPriority(string hash, RTorrentPriority priority, RTorrentSettings settings);
void SetTorrentLabel(string hash, string label, RTorrentSettings settings); void SetTorrentLabel(string hash, string label, RTorrentSettings settings);
void SetTorrentDownloadDirectory(string hash, string directory, RTorrentSettings settings);
bool HasHashTorrent(string hash, RTorrentSettings settings); bool HasHashTorrent(string hash, RTorrentSettings settings);
void StartTorrent(string hash, RTorrentSettings settings);
} }
public interface IRTorrent : IXmlRpcProxy public interface IRTorrent : IXmlRpcProxy
@ -26,10 +28,10 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
[XmlRpcMethod("d.multicall2")] [XmlRpcMethod("d.multicall2")]
object[] TorrentMulticall(params string[] parameters); object[] TorrentMulticall(params string[] parameters);
[XmlRpcMethod("load.start")] [XmlRpcMethod("load.normal")]
int LoadURL(string target, string data); int LoadUrl(string target, string data);
[XmlRpcMethod("load.raw_start")] [XmlRpcMethod("load.raw")]
int LoadBinary(string target, byte[] data); int LoadBinary(string target, byte[] data);
[XmlRpcMethod("d.erase")] [XmlRpcMethod("d.erase")]
@ -41,11 +43,17 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
[XmlRpcMethod("d.priority.set")] [XmlRpcMethod("d.priority.set")]
int SetPriority(string hash, long priority); int SetPriority(string hash, long priority);
[XmlRpcMethod("d.directory.set")]
int SetDirectory(string hash, string directory);
[XmlRpcMethod("d.name")] [XmlRpcMethod("d.name")]
string GetName(string hash); string GetName(string hash);
[XmlRpcMethod("system.client_version")] [XmlRpcMethod("system.client_version")]
string GetVersion(); string GetVersion();
[XmlRpcMethod("system.multicall")]
object[] SystemMulticall(object[] parameters);
} }
public class RTorrentProxy : IRTorrentProxy public class RTorrentProxy : IRTorrentProxy
@ -108,32 +116,13 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
return items; return items;
} }
public bool HasHashTorrent(string hash, RTorrentSettings settings)
{
_logger.Debug("Executing remote method: d.name");
var client = BuildClient(settings);
try
{
var name = client.GetName(hash);
if (name.IsNullOrWhiteSpace()) return false;
bool metaTorrent = name == (hash + ".meta");
return !metaTorrent;
}
catch (Exception)
{
return false;
}
}
public void AddTorrentFromUrl(string torrentUrl, RTorrentSettings settings) public void AddTorrentFromUrl(string torrentUrl, RTorrentSettings settings)
{ {
_logger.Debug("Executing remote method: load.start"); _logger.Debug("Executing remote method: load.normal");
var client = BuildClient(settings); var client = BuildClient(settings);
var response = client.LoadURL("", torrentUrl); var response = client.LoadUrl("", torrentUrl);
if (response != 0) if (response != 0)
{ {
throw new DownloadClientException("Could not add torrent: {0}.", torrentUrl); throw new DownloadClientException("Could not add torrent: {0}.", torrentUrl);
@ -142,7 +131,7 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
public void AddTorrentFromFile(string fileName, Byte[] fileContent, RTorrentSettings settings) public void AddTorrentFromFile(string fileName, Byte[] fileContent, RTorrentSettings settings)
{ {
_logger.Debug("Executing remote method: load.raw_start"); _logger.Debug("Executing remote method: load.raw");
var client = BuildClient(settings); var client = BuildClient(settings);
@ -166,7 +155,7 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
} }
} }
public void SetTorrentPriority(string hash, RTorrentSettings settings, RTorrentPriority priority) public void SetTorrentPriority(string hash, RTorrentPriority priority, RTorrentSettings settings)
{ {
_logger.Debug("Executing remote method: d.priority.set"); _logger.Debug("Executing remote method: d.priority.set");
@ -185,13 +174,71 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
var client = BuildClient(settings); var client = BuildClient(settings);
var satLabel = client.SetLabel(hash, label); var setLabel = client.SetLabel(hash, label);
if (satLabel != label) if (setLabel != label)
{ {
throw new DownloadClientException("Could set label on torrent: {0}.", hash); throw new DownloadClientException("Could set label on torrent: {0}.", hash);
} }
} }
public void SetTorrentDownloadDirectory(string hash, string directory, RTorrentSettings settings)
{
_logger.Debug("Executing remote method: d.directory.set");
var client = BuildClient(settings);
var response = client.SetDirectory(hash, directory);
if (response != 0)
{
throw new DownloadClientException("Could not set directory for torrent: {0}.", hash);
}
}
public bool HasHashTorrent(string hash, RTorrentSettings settings)
{
_logger.Debug("Executing remote method: d.name");
var client = BuildClient(settings);
try
{
var name = client.GetName(hash);
if (name.IsNullOrWhiteSpace()) return false;
bool metaTorrent = name == (hash + ".meta");
return !metaTorrent;
}
catch (Exception)
{
return false;
}
}
public void StartTorrent(string hash, RTorrentSettings settings)
{
_logger.Debug("Executing remote methods: d.open and d.start");
var client = BuildClient(settings);
var multicallResponse = client.SystemMulticall(new[]
{
new
{
methodName = "d.open",
@params = new[] { hash }
},
new
{
methodName = "d.start",
@params = new[] { hash }
},
}).SelectMany(c => ((IEnumerable<int>)c));
if (multicallResponse.Any(r => r != 0))
{
throw new DownloadClientException("Could not start torrent: {0}.", hash);
}
}
private IRTorrent BuildClient(RTorrentSettings settings) private IRTorrent BuildClient(RTorrentSettings settings)
{ {
var client = XmlRpcProxyGen.Create<IRTorrent>(); var client = XmlRpcProxyGen.Create<IRTorrent>();

@ -1,6 +1,4 @@
using System; using FluentValidation;
using FluentValidation;
using FluentValidation.Results;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
@ -52,10 +50,13 @@ 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 = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(RTorrentPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")] [FieldDefinition(7, 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; }
[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")]
public int RecentTvPriority { get; set; } public int RecentTvPriority { get; set; }
[FieldDefinition(8, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(RTorrentPriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")] [FieldDefinition(9, 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; }
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()

@ -347,6 +347,7 @@
<Compile Include="Download\Clients\Nzbget\NzbgetSettings.cs" /> <Compile Include="Download\Clients\Nzbget\NzbgetSettings.cs" />
<Compile Include="Download\Clients\Pneumatic\Pneumatic.cs" /> <Compile Include="Download\Clients\Pneumatic\Pneumatic.cs" />
<Compile Include="Download\Clients\Pneumatic\PneumaticSettings.cs" /> <Compile Include="Download\Clients\Pneumatic\PneumaticSettings.cs" />
<Compile Include="Download\Clients\rTorrent\RTorrentDirectoryValidator.cs" />
<Compile Include="Download\Clients\Sabnzbd\JsonConverters\SabnzbdPriorityTypeConverter.cs" /> <Compile Include="Download\Clients\Sabnzbd\JsonConverters\SabnzbdPriorityTypeConverter.cs" />
<Compile Include="Download\Clients\Sabnzbd\JsonConverters\SabnzbdQueueTimeConverter.cs" /> <Compile Include="Download\Clients\Sabnzbd\JsonConverters\SabnzbdQueueTimeConverter.cs" />
<Compile Include="Download\Clients\Sabnzbd\Responses\SabnzbdRetryResponse.cs" /> <Compile Include="Download\Clients\Sabnzbd\Responses\SabnzbdRetryResponse.cs" />

Loading…
Cancel
Save