mirror of
https://github.com/Radarr/Radarr
synced 2025-02-27 08:33:21 +00:00
Download clients: New client rTorrent
This commit is contained in:
parent
944a775625
commit
4b9664d82a
9 changed files with 657 additions and 1 deletions
|
@ -0,0 +1,124 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using FluentAssertions;
|
||||||
|
using Moq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using NzbDrone.Common.Http;
|
||||||
|
using NzbDrone.Core.MediaFiles.TorrentInfo;
|
||||||
|
using NzbDrone.Core.Download;
|
||||||
|
using NzbDrone.Core.Download.Clients.RTorrent;
|
||||||
|
using NzbDrone.Test.Common;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Test.Download.DownloadClientTests.RTorrentTests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class RTorrentFixture : DownloadClientFixtureBase<RTorrent>
|
||||||
|
{
|
||||||
|
protected RTorrentTorrent _downloading;
|
||||||
|
protected RTorrentTorrent _completed;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
Subject.Definition = new DownloadClientDefinition();
|
||||||
|
Subject.Definition.Settings = new RTorrentSettings()
|
||||||
|
{
|
||||||
|
TvCategory = null
|
||||||
|
};
|
||||||
|
|
||||||
|
_downloading = new RTorrentTorrent
|
||||||
|
{
|
||||||
|
Hash = "HASH",
|
||||||
|
IsFinished = false,
|
||||||
|
IsOpen = true,
|
||||||
|
IsActive = true,
|
||||||
|
Name = _title,
|
||||||
|
TotalSize = 1000,
|
||||||
|
RemainingSize = 500,
|
||||||
|
Path = "somepath"
|
||||||
|
};
|
||||||
|
|
||||||
|
_completed = new RTorrentTorrent
|
||||||
|
{
|
||||||
|
Hash = "HASH",
|
||||||
|
IsFinished = true,
|
||||||
|
Name = _title,
|
||||||
|
TotalSize = 1000,
|
||||||
|
RemainingSize = 0,
|
||||||
|
Path = "somepath"
|
||||||
|
};
|
||||||
|
|
||||||
|
Mocker.GetMock<ITorrentFileInfoReader>()
|
||||||
|
.Setup(s => s.GetHashFromTorrentFile(It.IsAny<Byte[]>()))
|
||||||
|
.Returns("CBC2F069FE8BB2F544EAE707D75BCD3DE9DCF951");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void GivenSuccessfulDownload()
|
||||||
|
{
|
||||||
|
Mocker.GetMock<IRTorrentProxy>()
|
||||||
|
.Setup(s => s.AddTorrentFromUrl(It.IsAny<String>(), It.IsAny<RTorrentSettings>()))
|
||||||
|
.Callback(PrepareClientToReturnCompletedItem);
|
||||||
|
|
||||||
|
Mocker.GetMock<IRTorrentProxy>()
|
||||||
|
.Setup(s => s.AddTorrentFromFile(It.IsAny<String>(), It.IsAny<Byte[]>(), It.IsAny<RTorrentSettings>()))
|
||||||
|
.Callback(PrepareClientToReturnCompletedItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void GivenTorrents(List<RTorrentTorrent> torrents)
|
||||||
|
{
|
||||||
|
if (torrents == null)
|
||||||
|
{
|
||||||
|
torrents = new List<RTorrentTorrent>();
|
||||||
|
}
|
||||||
|
|
||||||
|
Mocker.GetMock<IRTorrentProxy>()
|
||||||
|
.Setup(s => s.GetTorrents(It.IsAny<RTorrentSettings>()))
|
||||||
|
.Returns(torrents);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void PrepareClientToReturnDownloadingItem()
|
||||||
|
{
|
||||||
|
GivenTorrents(new List<RTorrentTorrent>
|
||||||
|
{
|
||||||
|
_downloading
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void PrepareClientToReturnCompletedItem()
|
||||||
|
{
|
||||||
|
GivenTorrents(new List<RTorrentTorrent>
|
||||||
|
{
|
||||||
|
_completed
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void downloading_item_should_have_required_properties()
|
||||||
|
{
|
||||||
|
PrepareClientToReturnDownloadingItem();
|
||||||
|
var item = Subject.GetItems().Single();
|
||||||
|
VerifyDownloading(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void completed_download_should_have_required_properties()
|
||||||
|
{
|
||||||
|
PrepareClientToReturnCompletedItem();
|
||||||
|
var item = Subject.GetItems().Single();
|
||||||
|
VerifyCompleted(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Download_should_return_unique_id()
|
||||||
|
{
|
||||||
|
GivenSuccessfulDownload();
|
||||||
|
|
||||||
|
var remoteEpisode = CreateRemoteEpisode();
|
||||||
|
|
||||||
|
var id = Subject.Download(remoteEpisode);
|
||||||
|
|
||||||
|
id.Should().NotBeNullOrEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -157,6 +157,7 @@
|
||||||
<Compile Include="Download\DownloadClientTests\DownloadClientFixtureBase.cs" />
|
<Compile Include="Download\DownloadClientTests\DownloadClientFixtureBase.cs" />
|
||||||
<Compile Include="Download\DownloadClientTests\NzbgetTests\NzbgetFixture.cs" />
|
<Compile Include="Download\DownloadClientTests\NzbgetTests\NzbgetFixture.cs" />
|
||||||
<Compile Include="Download\DownloadClientTests\PneumaticProviderFixture.cs" />
|
<Compile Include="Download\DownloadClientTests\PneumaticProviderFixture.cs" />
|
||||||
|
<Compile Include="Download\DownloadClientTests\RTorrentTests\RTorrentFixture.cs" />
|
||||||
<Compile Include="Download\DownloadClientTests\SabnzbdTests\SabnzbdFixture.cs" />
|
<Compile Include="Download\DownloadClientTests\SabnzbdTests\SabnzbdFixture.cs" />
|
||||||
<Compile Include="Download\DownloadClientTests\TransmissionTests\TransmissionFixture.cs" />
|
<Compile Include="Download\DownloadClientTests\TransmissionTests\TransmissionFixture.cs" />
|
||||||
<Compile Include="Download\DownloadClientTests\UTorrentTests\UTorrentFixture.cs" />
|
<Compile Include="Download\DownloadClientTests\UTorrentTests\UTorrentFixture.cs" />
|
||||||
|
@ -513,4 +514,4 @@
|
||||||
<Target Name="AfterBuild">
|
<Target Name="AfterBuild">
|
||||||
</Target>
|
</Target>
|
||||||
-->
|
-->
|
||||||
</Project>
|
</Project>
|
||||||
|
|
214
src/NzbDrone.Core/Download/Clients/rTorrent/RTorrent.cs
Normal file
214
src/NzbDrone.Core/Download/Clients/rTorrent/RTorrent.cs
Normal file
|
@ -0,0 +1,214 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using NzbDrone.Common.Disk;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Common.Http;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
|
using NzbDrone.Core.MediaFiles.TorrentInfo;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Core.Validation;
|
||||||
|
using FluentValidation.Results;
|
||||||
|
using System.Net;
|
||||||
|
using NzbDrone.Core.Parser.Model;
|
||||||
|
using NzbDrone.Core.RemotePathMappings;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Download.Clients.RTorrent
|
||||||
|
{
|
||||||
|
public class RTorrent : TorrentClientBase<RTorrentSettings>
|
||||||
|
{
|
||||||
|
private readonly IRTorrentProxy _proxy;
|
||||||
|
|
||||||
|
public RTorrent(IRTorrentProxy proxy,
|
||||||
|
ITorrentFileInfoReader torrentFileInfoReader,
|
||||||
|
IHttpClient httpClient,
|
||||||
|
IConfigService configService,
|
||||||
|
IDiskProvider diskProvider,
|
||||||
|
IRemotePathMappingService remotePathMappingService,
|
||||||
|
Logger logger)
|
||||||
|
: base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger)
|
||||||
|
{
|
||||||
|
_proxy = proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override string AddFromMagnetLink(RemoteEpisode remoteEpisode, string hash, string magnetLink)
|
||||||
|
{
|
||||||
|
_proxy.AddTorrentFromUrl(magnetLink, Settings);
|
||||||
|
|
||||||
|
// Wait until url has been resolved before returning
|
||||||
|
var TRIES = 5;
|
||||||
|
var RETRY_DELAY = 500; //ms
|
||||||
|
var ready = false;
|
||||||
|
for (var i = 0; i < TRIES; i++)
|
||||||
|
{
|
||||||
|
ready = _proxy.HasHashTorrent(hash, Settings);
|
||||||
|
if (ready)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread.Sleep(RETRY_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ready)
|
||||||
|
{
|
||||||
|
_proxy.SetTorrentLabel(hash, Settings.TvCategory, Settings);
|
||||||
|
|
||||||
|
var priority = (RTorrentPriority)(remoteEpisode.IsRecentEpisode() ?
|
||||||
|
Settings.RecentTvPriority : Settings.OlderTvPriority);
|
||||||
|
_proxy.SetTorrentPriority(hash, Settings, priority);
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Debug("Magnet {0} could not be resolved in {1} tries at {2} ms intervals.", magnetLink, TRIES, RETRY_DELAY);
|
||||||
|
// Remove from client, since it is discarded
|
||||||
|
RemoveItem(hash, true);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override string AddFromTorrentFile(RemoteEpisode remoteEpisode, string hash, string filename, byte[] fileContent)
|
||||||
|
{
|
||||||
|
_proxy.AddTorrentFromFile(filename, fileContent, Settings);
|
||||||
|
|
||||||
|
_proxy.SetTorrentLabel(hash, Settings.TvCategory, Settings);
|
||||||
|
|
||||||
|
var priority = (RTorrentPriority)(remoteEpisode.IsRecentEpisode() ?
|
||||||
|
Settings.RecentTvPriority : Settings.OlderTvPriority);
|
||||||
|
_proxy.SetTorrentPriority(hash, Settings, priority);
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Name
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return "rTorrent";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IEnumerable<DownloadClientItem> GetItems()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var torrents = _proxy.GetTorrents(Settings);
|
||||||
|
|
||||||
|
_logger.Debug("Retrieved metadata of {0} torrents in client", torrents.Count);
|
||||||
|
|
||||||
|
var items = new List<DownloadClientItem>();
|
||||||
|
foreach (RTorrentTorrent torrent in torrents)
|
||||||
|
{
|
||||||
|
// Don't concern ourselves with categories other than specified
|
||||||
|
if (torrent.Category != Settings.TvCategory) continue;
|
||||||
|
|
||||||
|
if (torrent.Path.StartsWith("."))
|
||||||
|
{
|
||||||
|
throw new DownloadClientException("Download paths paths must be absolute. Please specify variable \"directory\" in rTorrent.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var item = new DownloadClientItem();
|
||||||
|
item.DownloadClient = Definition.Name;
|
||||||
|
item.Title = torrent.Name;
|
||||||
|
item.DownloadId = torrent.Hash;
|
||||||
|
item.OutputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(torrent.Path));
|
||||||
|
item.TotalSize = torrent.TotalSize;
|
||||||
|
item.RemainingSize = torrent.RemainingSize;
|
||||||
|
item.Category = torrent.Category;
|
||||||
|
|
||||||
|
if (torrent.DownRate > 0) {
|
||||||
|
var secondsLeft = torrent.RemainingSize / torrent.DownRate;
|
||||||
|
item.RemainingTime = TimeSpan.FromSeconds(secondsLeft);
|
||||||
|
} else {
|
||||||
|
item.RemainingTime = TimeSpan.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (torrent.IsFinished) item.Status = DownloadItemStatus.Completed;
|
||||||
|
else if (torrent.IsActive) item.Status = DownloadItemStatus.Downloading;
|
||||||
|
else if (!torrent.IsActive) item.Status = DownloadItemStatus.Paused;
|
||||||
|
|
||||||
|
// Since we do not know the user's intent, do not let Sonarr to remove the torrent
|
||||||
|
item.IsReadOnly = true;
|
||||||
|
|
||||||
|
items.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
catch (DownloadClientException ex)
|
||||||
|
{
|
||||||
|
_logger.ErrorException(ex.Message, ex);
|
||||||
|
return Enumerable.Empty<DownloadClientItem>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void RemoveItem(string downloadId, bool deleteData)
|
||||||
|
{
|
||||||
|
if (deleteData)
|
||||||
|
{
|
||||||
|
DeleteItemData(downloadId);
|
||||||
|
}
|
||||||
|
|
||||||
|
_proxy.RemoveTorrent(downloadId, Settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override DownloadClientStatus GetStatus()
|
||||||
|
{
|
||||||
|
// XXX: This function's correctness has not been considered
|
||||||
|
|
||||||
|
var status = new DownloadClientStatus
|
||||||
|
{
|
||||||
|
IsLocalhost = Settings.Host == "127.0.0.1" || Settings.Host == "localhost"
|
||||||
|
};
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Test(List<ValidationFailure> failures)
|
||||||
|
{
|
||||||
|
failures.AddIfNotNull(TestConnection());
|
||||||
|
if (failures.Any()) return;
|
||||||
|
failures.AddIfNotNull(TestGetTorrents());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ValidationFailure TestConnection()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var version = _proxy.GetVersion(Settings);
|
||||||
|
|
||||||
|
if (new Version(version) < new Version("0.9.0"))
|
||||||
|
{
|
||||||
|
return new ValidationFailure(string.Empty, "rTorrent version should be at least 0.9.0. Version reported is {0}", version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.ErrorException(ex.Message, ex);
|
||||||
|
return new NzbDroneValidationFailure(string.Empty, "Unknown exception: " + ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ValidationFailure TestGetTorrents()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_proxy.GetTorrents(Settings);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.ErrorException(ex.Message, ex);
|
||||||
|
return new NzbDroneValidationFailure(string.Empty, "Failed to get the list of torrents: " + ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
namespace NzbDrone.Core.Download.Clients.RTorrent
|
||||||
|
{
|
||||||
|
public enum RTorrentPriority
|
||||||
|
{
|
||||||
|
DoNotDownload = 0,
|
||||||
|
Low = 1,
|
||||||
|
Normal = 2,
|
||||||
|
High = 3
|
||||||
|
}
|
||||||
|
}
|
214
src/NzbDrone.Core/Download/Clients/rTorrent/RTorrentProxy.cs
Normal file
214
src/NzbDrone.Core/Download/Clients/rTorrent/RTorrentProxy.cs
Normal file
|
@ -0,0 +1,214 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
using CookComputing.XmlRpc;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Download.Clients.RTorrent
|
||||||
|
{
|
||||||
|
public interface IRTorrentProxy
|
||||||
|
{
|
||||||
|
string GetVersion(RTorrentSettings settings);
|
||||||
|
List<RTorrentTorrent> GetTorrents(RTorrentSettings settings);
|
||||||
|
|
||||||
|
void AddTorrentFromUrl(string torrentUrl, RTorrentSettings settings);
|
||||||
|
void AddTorrentFromFile(string fileName, byte[] fileContent, RTorrentSettings settings);
|
||||||
|
void RemoveTorrent(string hash, RTorrentSettings settings);
|
||||||
|
void SetTorrentPriority(string hash, RTorrentSettings settings, RTorrentPriority priority);
|
||||||
|
void SetTorrentLabel(string hash, string label, RTorrentSettings settings);
|
||||||
|
bool HasHashTorrent(string hash, RTorrentSettings settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IRTorrent : IXmlRpcProxy
|
||||||
|
{
|
||||||
|
[XmlRpcMethod("d.multicall")]
|
||||||
|
object[] TorrentMulticall(params string[] parameters);
|
||||||
|
|
||||||
|
[XmlRpcMethod("load_start")]
|
||||||
|
int LoadURL(string data);
|
||||||
|
|
||||||
|
[XmlRpcMethod("load_raw_start")]
|
||||||
|
int LoadBinary(byte[] data);
|
||||||
|
|
||||||
|
[XmlRpcMethod("d.erase")]
|
||||||
|
int Remove(string hash);
|
||||||
|
|
||||||
|
[XmlRpcMethod("d.set_custom1")]
|
||||||
|
string SetLabel(string hash, string label);
|
||||||
|
|
||||||
|
[XmlRpcMethod("d.set_priority")]
|
||||||
|
int SetPriority(string hash, long priority);
|
||||||
|
|
||||||
|
[XmlRpcMethod("d.get_name")]
|
||||||
|
string GetName(string hash);
|
||||||
|
|
||||||
|
[XmlRpcMethod("system.client_version")]
|
||||||
|
string GetVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RTorrentProxy : IRTorrentProxy
|
||||||
|
{
|
||||||
|
private readonly Logger _logger;
|
||||||
|
|
||||||
|
public RTorrentProxy(Logger logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetVersion(RTorrentSettings settings)
|
||||||
|
{
|
||||||
|
_logger.Debug("Executing remote method: system.client_version");
|
||||||
|
|
||||||
|
var client = BuildClient(settings);
|
||||||
|
|
||||||
|
var version = client.GetVersion();
|
||||||
|
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<RTorrentTorrent> GetTorrents(RTorrentSettings settings)
|
||||||
|
{
|
||||||
|
_logger.Debug("Executing remote method: d.multicall");
|
||||||
|
|
||||||
|
var client = BuildClient(settings);
|
||||||
|
var ret = client.TorrentMulticall("main",
|
||||||
|
"d.get_name=", // string
|
||||||
|
"d.get_hash=", // string
|
||||||
|
"d.get_base_path=", // string
|
||||||
|
"d.get_custom1=", // string (label)
|
||||||
|
"d.get_size_bytes=", // long
|
||||||
|
"d.get_left_bytes=", // long
|
||||||
|
"d.get_down_rate=", // long (in bytes / s)
|
||||||
|
"d.get_ratio=", // long
|
||||||
|
"d.is_open=", // long
|
||||||
|
"d.is_active=", // long
|
||||||
|
"d.get_complete="); //long
|
||||||
|
|
||||||
|
var items = new List<RTorrentTorrent>();
|
||||||
|
foreach (object[] torrent in ret)
|
||||||
|
{
|
||||||
|
var item = new RTorrentTorrent();
|
||||||
|
item.Name = (string) torrent[0];
|
||||||
|
item.Hash = (string) torrent[1];
|
||||||
|
item.Path = (string) torrent[2];
|
||||||
|
item.Category = (string) torrent[3];
|
||||||
|
item.TotalSize = (long) torrent[4];
|
||||||
|
item.RemainingSize = (long) torrent[5];
|
||||||
|
item.DownRate = (long) torrent[6];
|
||||||
|
item.Ratio = (long) torrent[7];
|
||||||
|
item.IsOpen = Convert.ToBoolean((long) torrent[8]);
|
||||||
|
item.IsActive = Convert.ToBoolean((long) torrent[9]);
|
||||||
|
item.IsFinished = Convert.ToBoolean((long) torrent[10]);
|
||||||
|
|
||||||
|
items.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasHashTorrent(string hash, RTorrentSettings settings)
|
||||||
|
{
|
||||||
|
_logger.Debug("Executing remote method: d.get_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)
|
||||||
|
{
|
||||||
|
_logger.Debug("Executing remote method: load_start");
|
||||||
|
|
||||||
|
var client = BuildClient(settings);
|
||||||
|
|
||||||
|
var response = client.LoadURL(torrentUrl);
|
||||||
|
if (response != 0)
|
||||||
|
{
|
||||||
|
throw new DownloadClientException("Could not add torrent: {0}.", torrentUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddTorrentFromFile(string fileName, Byte[] fileContent, RTorrentSettings settings)
|
||||||
|
{
|
||||||
|
_logger.Debug("Executing remote method: load_raw_start");
|
||||||
|
|
||||||
|
var client = BuildClient(settings);
|
||||||
|
|
||||||
|
var response = client.LoadBinary(fileContent);
|
||||||
|
if (response != 0)
|
||||||
|
{
|
||||||
|
throw new DownloadClientException("Could not add torrent: {0}.", fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveTorrent(string hash, RTorrentSettings settings)
|
||||||
|
{
|
||||||
|
_logger.Debug("Executing remote method: d.erase");
|
||||||
|
|
||||||
|
var client = BuildClient(settings);
|
||||||
|
|
||||||
|
var response = client.Remove(hash);
|
||||||
|
if (response != 0)
|
||||||
|
{
|
||||||
|
throw new DownloadClientException("Could not remove torrent: {0}.", hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetTorrentPriority(string hash, RTorrentSettings settings, RTorrentPriority priority)
|
||||||
|
{
|
||||||
|
_logger.Debug("Executing remote method: d.set_priority");
|
||||||
|
|
||||||
|
var client = BuildClient(settings);
|
||||||
|
|
||||||
|
var response = client.SetPriority(hash, (long) priority);
|
||||||
|
if (response != 0)
|
||||||
|
{
|
||||||
|
throw new DownloadClientException("Could not set priority on torrent: {0}.", hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetTorrentLabel(string hash, string label, RTorrentSettings settings)
|
||||||
|
{
|
||||||
|
_logger.Debug("Executing remote method: d.set_custom1");
|
||||||
|
|
||||||
|
var client = BuildClient(settings);
|
||||||
|
|
||||||
|
var satLabel = client.SetLabel(hash, label);
|
||||||
|
if (satLabel != label)
|
||||||
|
{
|
||||||
|
throw new DownloadClientException("Could set label on torrent: {0}.", hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IRTorrent BuildClient(RTorrentSettings settings)
|
||||||
|
{
|
||||||
|
var url = string.Format(@"{0}://{1}:{2}/{3}",
|
||||||
|
settings.UseSsl ? "https" : "http",
|
||||||
|
settings.Host,
|
||||||
|
settings.Port,
|
||||||
|
settings.UrlBase);
|
||||||
|
|
||||||
|
var client = XmlRpcProxyGen.Create<IRTorrent>();
|
||||||
|
client.Url = url;
|
||||||
|
|
||||||
|
if (!settings.Username.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
client.Credentials = new NetworkCredential(settings.Username, settings.Password);
|
||||||
|
}
|
||||||
|
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
using System;
|
||||||
|
using FluentValidation;
|
||||||
|
using FluentValidation.Results;
|
||||||
|
using NzbDrone.Core.Annotations;
|
||||||
|
using NzbDrone.Core.ThingiProvider;
|
||||||
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Download.Clients.RTorrent
|
||||||
|
{
|
||||||
|
public class RTorrentSettingsValidator : AbstractValidator<RTorrentSettings>
|
||||||
|
{
|
||||||
|
public RTorrentSettingsValidator()
|
||||||
|
{
|
||||||
|
RuleFor(c => c.Host).NotEmpty();
|
||||||
|
RuleFor(c => c.Port).InclusiveBetween(0, 65535);
|
||||||
|
RuleFor(c => c.TvCategory).NotEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RTorrentSettings : IProviderConfig
|
||||||
|
{
|
||||||
|
private static readonly RTorrentSettingsValidator Validator = new RTorrentSettingsValidator();
|
||||||
|
|
||||||
|
public RTorrentSettings()
|
||||||
|
{
|
||||||
|
Host = "localhost";
|
||||||
|
Port = 8080;
|
||||||
|
UrlBase = "RPC2";
|
||||||
|
TvCategory = "tv-sonarr";
|
||||||
|
OlderTvPriority = (int)RTorrentPriority.Normal;
|
||||||
|
RecentTvPriority = (int)RTorrentPriority.Normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
[FieldDefinition(0, Label = "Host", Type = FieldType.Textbox)]
|
||||||
|
public string Host { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)]
|
||||||
|
public int Port { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(2, Label = "UrlBase", Type = FieldType.Textbox)]
|
||||||
|
public string UrlBase { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(3, Label = "Use SSL", Type = FieldType.Checkbox)]
|
||||||
|
public bool UseSsl { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(4, Label = "Username", Type = FieldType.Textbox)]
|
||||||
|
public string Username { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(5, Label = "Password", Type = FieldType.Password)]
|
||||||
|
public string Password { get; set; }
|
||||||
|
|
||||||
|
[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; }
|
||||||
|
|
||||||
|
[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")]
|
||||||
|
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")]
|
||||||
|
public int OlderTvPriority { get; set; }
|
||||||
|
|
||||||
|
public NzbDroneValidationResult Validate()
|
||||||
|
{
|
||||||
|
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
namespace NzbDrone.Core.Download.Clients.RTorrent
|
||||||
|
{
|
||||||
|
public class RTorrentTorrent
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Hash { get; set; }
|
||||||
|
public string Path { get; set; }
|
||||||
|
public string Category { get; set; }
|
||||||
|
public long TotalSize { get; set; }
|
||||||
|
public long RemainingSize { get; set; }
|
||||||
|
public long DownRate { get; set; }
|
||||||
|
public long Ratio { get; set; }
|
||||||
|
public bool IsFinished { get; set; }
|
||||||
|
public bool IsOpen { get; set; }
|
||||||
|
public bool IsActive { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -83,6 +83,10 @@
|
||||||
<Reference Include="RestSharp, Version=105.0.1.0, Culture=neutral, processorArchitecture=MSIL">
|
<Reference Include="RestSharp, Version=105.0.1.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
<SpecificVersion>False</SpecificVersion>
|
<SpecificVersion>False</SpecificVersion>
|
||||||
<HintPath>..\packages\RestSharp.105.0.1\lib\net4\RestSharp.dll</HintPath>
|
<HintPath>..\packages\RestSharp.105.0.1\lib\net4\RestSharp.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="CookComputing.XmlRpc, Version=2.5.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>..\packages\xmlrpcnet.2.5.0\lib\net20\CookComputing.XmlRpcV2.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
|
@ -357,6 +361,11 @@
|
||||||
<Compile Include="Download\Clients\TorrentBlackhole\TorrentBlackhole.cs" />
|
<Compile Include="Download\Clients\TorrentBlackhole\TorrentBlackhole.cs" />
|
||||||
<Compile Include="Download\Clients\TorrentBlackhole\TorrentBlackholeSettings.cs" />
|
<Compile Include="Download\Clients\TorrentBlackhole\TorrentBlackholeSettings.cs" />
|
||||||
<Compile Include="Download\Clients\TorrentSeedConfiguration.cs" />
|
<Compile Include="Download\Clients\TorrentSeedConfiguration.cs" />
|
||||||
|
<Compile Include="Download\Clients\rTorrent\RTorrent.cs" />
|
||||||
|
<Compile Include="Download\Clients\rTorrent\RTorrentPriority.cs" />
|
||||||
|
<Compile Include="Download\Clients\rTorrent\RTorrentProxy.cs" />
|
||||||
|
<Compile Include="Download\Clients\rTorrent\RTorrentSettings.cs" />
|
||||||
|
<Compile Include="Download\Clients\rTorrent\RTorrentTorrent.cs" />
|
||||||
<Compile Include="Download\Clients\Transmission\Transmission.cs" />
|
<Compile Include="Download\Clients\Transmission\Transmission.cs" />
|
||||||
<Compile Include="Download\Clients\Transmission\TransmissionException.cs" />
|
<Compile Include="Download\Clients\Transmission\TransmissionException.cs" />
|
||||||
<Compile Include="Download\Clients\Transmission\TransmissionProxy.cs" />
|
<Compile Include="Download\Clients\Transmission\TransmissionProxy.cs" />
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
|
<package id="xmlrpcnet" version="2.5.0" targetFramework="net40" />
|
||||||
<package id="FluentMigrator" version="1.3.1.0" targetFramework="net40" />
|
<package id="FluentMigrator" version="1.3.1.0" targetFramework="net40" />
|
||||||
<package id="FluentMigrator.Runner" version="1.3.1.0" targetFramework="net40" />
|
<package id="FluentMigrator.Runner" version="1.3.1.0" targetFramework="net40" />
|
||||||
<package id="FluentValidation" version="5.5.0.0" targetFramework="net40" />
|
<package id="FluentValidation" version="5.5.0.0" targetFramework="net40" />
|
||||||
|
|
Loading…
Reference in a new issue