mirror of
https://github.com/Radarr/Radarr.git
synced 2024-09-11 20:12:41 +02:00
Fixed: Use our own HttpClient for rTorrent RPC requests
[common]
This commit is contained in:
parent
a33b861cec
commit
b626c5bbf0
103
src/NzbDrone.Common/Http/XmlRpcRequestBuilder.cs
Normal file
103
src/NzbDrone.Common/Http/XmlRpcRequestBuilder.cs
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Instrumentation;
|
||||||
|
|
||||||
|
namespace NzbDrone.Common.Http
|
||||||
|
{
|
||||||
|
public class XmlRpcRequestBuilder : HttpRequestBuilder
|
||||||
|
{
|
||||||
|
public static string XmlRpcContentType = "text/xml";
|
||||||
|
|
||||||
|
private static readonly Logger Logger = NzbDroneLogger.GetLogger(typeof(XmlRpcRequestBuilder));
|
||||||
|
|
||||||
|
public string XmlMethod { get; private set; }
|
||||||
|
public List<object> XmlParameters { get; private set; }
|
||||||
|
|
||||||
|
public XmlRpcRequestBuilder(string baseUrl)
|
||||||
|
: base(baseUrl)
|
||||||
|
{
|
||||||
|
Method = HttpMethod.Post;
|
||||||
|
XmlParameters = new List<object>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public XmlRpcRequestBuilder(bool useHttps, string host, int port, string urlBase = null)
|
||||||
|
: this(BuildBaseUrl(useHttps, host, port, urlBase))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override HttpRequestBuilder Clone()
|
||||||
|
{
|
||||||
|
var clone = base.Clone() as XmlRpcRequestBuilder;
|
||||||
|
clone.XmlParameters = new List<object>(XmlParameters);
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public XmlRpcRequestBuilder Call(string method, params object[] parameters)
|
||||||
|
{
|
||||||
|
var clone = Clone() as XmlRpcRequestBuilder;
|
||||||
|
clone.XmlMethod = method;
|
||||||
|
clone.XmlParameters = parameters.ToList();
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Apply(HttpRequest request)
|
||||||
|
{
|
||||||
|
base.Apply(request);
|
||||||
|
|
||||||
|
request.Headers.ContentType = XmlRpcContentType;
|
||||||
|
|
||||||
|
var methodCallElements = new List<XElement> { new XElement("methodName", XmlMethod) };
|
||||||
|
|
||||||
|
if (XmlParameters.Any())
|
||||||
|
{
|
||||||
|
var argElements = XmlParameters.Select(x => new XElement("param", ConvertParameter(x))).ToList();
|
||||||
|
var paramsElement = new XElement("params", argElements);
|
||||||
|
methodCallElements.Add(paramsElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
var message = new XDocument(
|
||||||
|
new XDeclaration("1.0", "utf-8", "yes"),
|
||||||
|
new XElement("methodCall", methodCallElements));
|
||||||
|
|
||||||
|
var body = message.ToString();
|
||||||
|
|
||||||
|
Logger.Debug($"Executing remote method: {XmlMethod}");
|
||||||
|
|
||||||
|
Logger.Trace($"methodCall {XmlMethod} body:\n{body}");
|
||||||
|
|
||||||
|
request.SetContent(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static XElement ConvertParameter(object value)
|
||||||
|
{
|
||||||
|
XElement data;
|
||||||
|
|
||||||
|
if (value is string s)
|
||||||
|
{
|
||||||
|
data = new XElement("string", s);
|
||||||
|
}
|
||||||
|
else if (value is List<string> l)
|
||||||
|
{
|
||||||
|
data = new XElement("array", new XElement("data", l.Select(x => new XElement("value", new XElement("string", x)))));
|
||||||
|
}
|
||||||
|
else if (value is int i)
|
||||||
|
{
|
||||||
|
data = new XElement("int", i);
|
||||||
|
}
|
||||||
|
else if (value is byte[] bytes)
|
||||||
|
{
|
||||||
|
data = new XElement("base64", Convert.ToBase64String(bytes));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Unhandled argument type {value.GetType().Name}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new XElement("value", data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -127,6 +127,12 @@ public override IEnumerable<DownloadClientItem> GetItems()
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ignore torrents with an empty path
|
||||||
|
if (torrent.Path.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (torrent.Path.StartsWith("."))
|
if (torrent.Path.StartsWith("."))
|
||||||
{
|
{
|
||||||
throw new DownloadClientException("Download paths must be absolute. Please specify variable \"directory\" in rTorrent.");
|
throw new DownloadClientException("Download paths must be absolute. Please specify variable \"directory\" in rTorrent.");
|
||||||
|
28
src/NzbDrone.Core/Download/Clients/rTorrent/RTorrentFault.cs
Normal file
28
src/NzbDrone.Core/Download/Clients/rTorrent/RTorrentFault.cs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
using System.Xml.Linq;
|
||||||
|
using System.Xml.XPath;
|
||||||
|
using NzbDrone.Core.Download.Extensions;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Download.Clients.RTorrent
|
||||||
|
{
|
||||||
|
public class RTorrentFault
|
||||||
|
{
|
||||||
|
public RTorrentFault(XElement element)
|
||||||
|
{
|
||||||
|
foreach (var e in element.XPathSelectElements("./value/struct/member"))
|
||||||
|
{
|
||||||
|
var name = e.ElementAsString("name");
|
||||||
|
if (name == "faultCode")
|
||||||
|
{
|
||||||
|
FaultCode = e.Element("value").GetIntValue();
|
||||||
|
}
|
||||||
|
else if (name == "faultString")
|
||||||
|
{
|
||||||
|
FaultString = e.Element("value").GetStringValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int FaultCode { get; set; }
|
||||||
|
public string FaultString { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,12 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using CookComputing.XmlRpc;
|
using System.Xml.Linq;
|
||||||
using NLog;
|
using System.Xml.XPath;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Common.Serializer;
|
using NzbDrone.Common.Http;
|
||||||
|
using NzbDrone.Core.Download.Extensions;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Download.Clients.RTorrent
|
namespace NzbDrone.Core.Download.Clients.RTorrent
|
||||||
{
|
{
|
||||||
@ -21,64 +23,26 @@ public interface IRTorrentProxy
|
|||||||
void PushTorrentUniqueView(string hash, string view, RTorrentSettings settings);
|
void PushTorrentUniqueView(string hash, string view, RTorrentSettings settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IRTorrent : IXmlRpcProxy
|
|
||||||
{
|
|
||||||
[XmlRpcMethod("d.multicall2")]
|
|
||||||
object[] TorrentMulticall(params string[] parameters);
|
|
||||||
|
|
||||||
[XmlRpcMethod("load.normal")]
|
|
||||||
int LoadNormal(string target, string data, params string[] commands);
|
|
||||||
|
|
||||||
[XmlRpcMethod("load.start")]
|
|
||||||
int LoadStart(string target, string data, params string[] commands);
|
|
||||||
|
|
||||||
[XmlRpcMethod("load.raw")]
|
|
||||||
int LoadRaw(string target, byte[] data, params string[] commands);
|
|
||||||
|
|
||||||
[XmlRpcMethod("load.raw_start")]
|
|
||||||
int LoadRawStart(string target, byte[] data, params string[] commands);
|
|
||||||
|
|
||||||
[XmlRpcMethod("d.erase")]
|
|
||||||
int Remove(string hash);
|
|
||||||
|
|
||||||
[XmlRpcMethod("d.name")]
|
|
||||||
string GetName(string hash);
|
|
||||||
|
|
||||||
[XmlRpcMethod("d.custom1.set")]
|
|
||||||
string SetLabel(string hash, string label);
|
|
||||||
|
|
||||||
[XmlRpcMethod("d.views.push_back_unique")]
|
|
||||||
int PushUniqueView(string hash, string view);
|
|
||||||
|
|
||||||
[XmlRpcMethod("system.client_version")]
|
|
||||||
string GetVersion();
|
|
||||||
}
|
|
||||||
|
|
||||||
public class RTorrentProxy : IRTorrentProxy
|
public class RTorrentProxy : IRTorrentProxy
|
||||||
{
|
{
|
||||||
private readonly Logger _logger;
|
private readonly IHttpClient _httpClient;
|
||||||
|
|
||||||
public RTorrentProxy(Logger logger)
|
public RTorrentProxy(IHttpClient httpClient)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_httpClient = httpClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetVersion(RTorrentSettings settings)
|
public string GetVersion(RTorrentSettings settings)
|
||||||
{
|
{
|
||||||
_logger.Debug("Executing remote method: system.client_version");
|
var document = ExecuteRequest(settings, "system.client_version");
|
||||||
|
|
||||||
var client = BuildClient(settings);
|
return document.Descendants("string").FirstOrDefault()?.Value ?? "0.0.0";
|
||||||
var version = ExecuteRequest(() => client.GetVersion());
|
|
||||||
|
|
||||||
return version;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<RTorrentTorrent> GetTorrents(RTorrentSettings settings)
|
public List<RTorrentTorrent> GetTorrents(RTorrentSettings settings)
|
||||||
{
|
{
|
||||||
_logger.Debug("Executing remote method: d.multicall2");
|
var document = ExecuteRequest(settings,
|
||||||
|
"d.multicall2",
|
||||||
var client = BuildClient(settings);
|
|
||||||
var ret = ExecuteRequest(() => client.TorrentMulticall(
|
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
"d.name=", // string
|
"d.name=", // string
|
||||||
@ -92,54 +56,34 @@ public List<RTorrentTorrent> GetTorrents(RTorrentSettings settings)
|
|||||||
"d.is_open=", // long
|
"d.is_open=", // long
|
||||||
"d.is_active=", // long
|
"d.is_active=", // long
|
||||||
"d.complete=", //long
|
"d.complete=", //long
|
||||||
"d.timestamp.finished=")); // long (unix timestamp)
|
"d.timestamp.finished="); // long (unix timestamp)
|
||||||
|
|
||||||
_logger.Trace(ret.ToJson());
|
var torrents = document.XPathSelectElement("./methodResponse/params/param/value/array/data")
|
||||||
|
?.Elements()
|
||||||
|
.Select(x => new RTorrentTorrent(x))
|
||||||
|
.ToList()
|
||||||
|
?? new List<RTorrentTorrent>();
|
||||||
|
|
||||||
var items = new List<RTorrentTorrent>();
|
return torrents;
|
||||||
|
|
||||||
foreach (object[] torrent in ret)
|
|
||||||
{
|
|
||||||
var labelDecoded = System.Web.HttpUtility.UrlDecode((string)torrent[3]);
|
|
||||||
|
|
||||||
var item = new RTorrentTorrent();
|
|
||||||
item.Name = (string)torrent[0];
|
|
||||||
item.Hash = (string)torrent[1];
|
|
||||||
item.Path = (string)torrent[2];
|
|
||||||
item.Category = labelDecoded;
|
|
||||||
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]);
|
|
||||||
item.FinishedTime = (long)torrent[11];
|
|
||||||
|
|
||||||
items.Add(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
return items;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddTorrentFromUrl(string torrentUrl, string label, RTorrentPriority priority, string directory, RTorrentSettings settings)
|
public void AddTorrentFromUrl(string torrentUrl, string label, RTorrentPriority priority, string directory, RTorrentSettings settings)
|
||||||
{
|
{
|
||||||
var client = BuildClient(settings);
|
var args = new List<object> { "", torrentUrl };
|
||||||
var response = ExecuteRequest(() =>
|
args.AddRange(GetCommands(label, priority, directory));
|
||||||
{
|
|
||||||
|
XDocument response;
|
||||||
|
|
||||||
if (settings.AddStopped)
|
if (settings.AddStopped)
|
||||||
{
|
{
|
||||||
_logger.Debug("Executing remote method: load.normal");
|
response = ExecuteRequest(settings, "load.normal", args.ToArray());
|
||||||
return client.LoadNormal("", torrentUrl, GetCommands(label, priority, directory));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_logger.Debug("Executing remote method: load.start");
|
response = ExecuteRequest(settings, "load.start", args.ToArray());
|
||||||
return client.LoadStart("", torrentUrl, GetCommands(label, priority, directory));
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
if (response != 0)
|
if (response.GetIntResponse() != 0)
|
||||||
{
|
{
|
||||||
throw new DownloadClientException("Could not add torrent: {0}.", torrentUrl);
|
throw new DownloadClientException("Could not add torrent: {0}.", torrentUrl);
|
||||||
}
|
}
|
||||||
@ -147,22 +91,21 @@ public void AddTorrentFromUrl(string torrentUrl, string label, RTorrentPriority
|
|||||||
|
|
||||||
public void AddTorrentFromFile(string fileName, byte[] fileContent, string label, RTorrentPriority priority, string directory, RTorrentSettings settings)
|
public void AddTorrentFromFile(string fileName, byte[] fileContent, string label, RTorrentPriority priority, string directory, RTorrentSettings settings)
|
||||||
{
|
{
|
||||||
var client = BuildClient(settings);
|
var args = new List<object> { "", fileContent };
|
||||||
var response = ExecuteRequest(() =>
|
args.AddRange(GetCommands(label, priority, directory));
|
||||||
{
|
|
||||||
|
XDocument response;
|
||||||
|
|
||||||
if (settings.AddStopped)
|
if (settings.AddStopped)
|
||||||
{
|
{
|
||||||
_logger.Debug("Executing remote method: load.raw");
|
response = ExecuteRequest(settings, "load.raw", args.ToArray());
|
||||||
return client.LoadRaw("", fileContent, GetCommands(label, priority, directory));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_logger.Debug("Executing remote method: load.raw_start");
|
response = ExecuteRequest(settings, "load.raw_start", args.ToArray());
|
||||||
return client.LoadRawStart("", fileContent, GetCommands(label, priority, directory));
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
if (response != 0)
|
if (response.GetIntResponse() != 0)
|
||||||
{
|
{
|
||||||
throw new DownloadClientException("Could not add torrent: {0}.", fileName);
|
throw new DownloadClientException("Could not add torrent: {0}.", fileName);
|
||||||
}
|
}
|
||||||
@ -170,12 +113,9 @@ public void AddTorrentFromFile(string fileName, byte[] fileContent, string label
|
|||||||
|
|
||||||
public void SetTorrentLabel(string hash, string label, RTorrentSettings settings)
|
public void SetTorrentLabel(string hash, string label, RTorrentSettings settings)
|
||||||
{
|
{
|
||||||
_logger.Debug("Executing remote method: d.custom1.set");
|
var response = ExecuteRequest(settings, "d.custom1.set", hash, label);
|
||||||
|
|
||||||
var client = BuildClient(settings);
|
if (response.GetStringResponse() != label)
|
||||||
var response = ExecuteRequest(() => client.SetLabel(hash, label));
|
|
||||||
|
|
||||||
if (response != label)
|
|
||||||
{
|
{
|
||||||
throw new DownloadClientException("Could not set label to {1} for torrent: {0}.", hash, label);
|
throw new DownloadClientException("Could not set label to {1} for torrent: {0}.", hash, label);
|
||||||
}
|
}
|
||||||
@ -183,11 +123,9 @@ public void SetTorrentLabel(string hash, string label, RTorrentSettings settings
|
|||||||
|
|
||||||
public void PushTorrentUniqueView(string hash, string view, RTorrentSettings settings)
|
public void PushTorrentUniqueView(string hash, string view, RTorrentSettings settings)
|
||||||
{
|
{
|
||||||
_logger.Debug("Executing remote method: d.views.push_back_unique");
|
var response = ExecuteRequest(settings, "d.views.push_back_unique", hash, view);
|
||||||
|
|
||||||
var client = BuildClient(settings);
|
if (response.GetIntResponse() != 0)
|
||||||
var response = ExecuteRequest(() => client.PushUniqueView(hash, view));
|
|
||||||
if (response != 0)
|
|
||||||
{
|
{
|
||||||
throw new DownloadClientException("Could not push unique view {0} for torrent: {1}.", view, hash);
|
throw new DownloadClientException("Could not push unique view {0} for torrent: {1}.", view, hash);
|
||||||
}
|
}
|
||||||
@ -195,12 +133,9 @@ public void PushTorrentUniqueView(string hash, string view, RTorrentSettings set
|
|||||||
|
|
||||||
public void RemoveTorrent(string hash, RTorrentSettings settings)
|
public void RemoveTorrent(string hash, RTorrentSettings settings)
|
||||||
{
|
{
|
||||||
_logger.Debug("Executing remote method: d.erase");
|
var response = ExecuteRequest(settings, "d.erase", hash);
|
||||||
|
|
||||||
var client = BuildClient(settings);
|
if (response.GetIntResponse() != 0)
|
||||||
var response = ExecuteRequest(() => client.Remove(hash));
|
|
||||||
|
|
||||||
if (response != 0)
|
|
||||||
{
|
{
|
||||||
throw new DownloadClientException("Could not remove torrent: {0}.", hash);
|
throw new DownloadClientException("Could not remove torrent: {0}.", hash);
|
||||||
}
|
}
|
||||||
@ -208,13 +143,10 @@ public void RemoveTorrent(string hash, RTorrentSettings settings)
|
|||||||
|
|
||||||
public bool HasHashTorrent(string hash, RTorrentSettings settings)
|
public bool HasHashTorrent(string hash, RTorrentSettings settings)
|
||||||
{
|
{
|
||||||
_logger.Debug("Executing remote method: d.name");
|
|
||||||
|
|
||||||
var client = BuildClient(settings);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var name = ExecuteRequest(() => client.GetName(hash));
|
var response = ExecuteRequest(settings, "d.name", hash);
|
||||||
|
var name = response.GetStringResponse();
|
||||||
|
|
||||||
if (name.IsNullOrWhiteSpace())
|
if (name.IsNullOrWhiteSpace())
|
||||||
{
|
{
|
||||||
@ -253,45 +185,34 @@ private string[] GetCommands(string label, RTorrentPriority priority, string dir
|
|||||||
return result.ToArray();
|
return result.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
private IRTorrent BuildClient(RTorrentSettings settings)
|
private XDocument ExecuteRequest(RTorrentSettings settings, string methodName, params object[] args)
|
||||||
{
|
{
|
||||||
var client = XmlRpcProxyGen.Create<IRTorrent>();
|
var requestBuilder = new XmlRpcRequestBuilder(settings.UseSsl, settings.Host, settings.Port, settings.UrlBase)
|
||||||
|
{
|
||||||
client.Url = string.Format(@"{0}://{1}:{2}/{3}",
|
LogResponseContent = true,
|
||||||
settings.UseSsl ? "https" : "http",
|
};
|
||||||
settings.Host,
|
|
||||||
settings.Port,
|
|
||||||
settings.UrlBase);
|
|
||||||
|
|
||||||
client.EnableCompression = true;
|
|
||||||
|
|
||||||
if (!settings.Username.IsNullOrWhiteSpace())
|
if (!settings.Username.IsNullOrWhiteSpace())
|
||||||
{
|
{
|
||||||
client.Credentials = new NetworkCredential(settings.Username, settings.Password);
|
requestBuilder.NetworkCredential = new NetworkCredential(settings.Username, settings.Password);
|
||||||
}
|
}
|
||||||
|
|
||||||
return client;
|
var request = requestBuilder.Call(methodName, args).Build();
|
||||||
|
|
||||||
|
var response = _httpClient.Execute(request);
|
||||||
|
|
||||||
|
var doc = XDocument.Parse(response.Content);
|
||||||
|
|
||||||
|
var faultElement = doc.XPathSelectElement("./methodResponse/fault");
|
||||||
|
|
||||||
|
if (faultElement != null)
|
||||||
|
{
|
||||||
|
var fault = new RTorrentFault(faultElement);
|
||||||
|
|
||||||
|
throw new DownloadClientException($"rTorrent returned error code {fault.FaultCode}: {fault.FaultString}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private T ExecuteRequest<T>(Func<T> task)
|
return doc;
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return task();
|
|
||||||
}
|
|
||||||
catch (XmlRpcServerException ex)
|
|
||||||
{
|
|
||||||
throw new DownloadClientException("Unable to connect to rTorrent, please check your settings", ex);
|
|
||||||
}
|
|
||||||
catch (WebException ex)
|
|
||||||
{
|
|
||||||
if (ex.Status == WebExceptionStatus.TrustFailure)
|
|
||||||
{
|
|
||||||
throw new DownloadClientUnavailableException("Unable to connect to rTorrent, certificate validation failed.", ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new DownloadClientUnavailableException("Unable to connect to rTorrent, please check your settings", ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,35 @@
|
|||||||
namespace NzbDrone.Core.Download.Clients.RTorrent
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Web;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
using NzbDrone.Core.Download.Extensions;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Download.Clients.RTorrent
|
||||||
{
|
{
|
||||||
public class RTorrentTorrent
|
public class RTorrentTorrent
|
||||||
{
|
{
|
||||||
|
public RTorrentTorrent()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public RTorrentTorrent(XElement element)
|
||||||
|
{
|
||||||
|
var data = element.Descendants("value").ToList();
|
||||||
|
|
||||||
|
Name = data[0].GetStringValue();
|
||||||
|
Hash = data[1].GetStringValue();
|
||||||
|
Path = data[2].GetStringValue();
|
||||||
|
Category = HttpUtility.UrlDecode(data[3].GetStringValue());
|
||||||
|
TotalSize = data[4].GetLongValue();
|
||||||
|
RemainingSize = data[5].GetLongValue();
|
||||||
|
DownRate = data[6].GetLongValue();
|
||||||
|
Ratio = data[7].GetLongValue();
|
||||||
|
IsOpen = Convert.ToBoolean(data[8].GetLongValue());
|
||||||
|
IsActive = Convert.ToBoolean(data[9].GetLongValue());
|
||||||
|
IsFinished = Convert.ToBoolean(data[10].GetLongValue());
|
||||||
|
FinishedTime = data[11].GetLongValue();
|
||||||
|
}
|
||||||
|
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string Hash { get; set; }
|
public string Hash { get; set; }
|
||||||
public string Path { get; set; }
|
public string Path { get; set; }
|
||||||
|
55
src/NzbDrone.Core/Download/Extensions/XmlExtensions.cs
Normal file
55
src/NzbDrone.Core/Download/Extensions/XmlExtensions.cs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
using System.Xml.XPath;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Download.Extensions
|
||||||
|
{
|
||||||
|
internal static class XmlExtensions
|
||||||
|
{
|
||||||
|
public static string GetStringValue(this XElement element)
|
||||||
|
{
|
||||||
|
return element.ElementAsString("string");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long GetLongValue(this XElement element)
|
||||||
|
{
|
||||||
|
return element.ElementAsLong("i8");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetIntValue(this XElement element)
|
||||||
|
{
|
||||||
|
return element.ElementAsInt("i4");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ElementAsString(this XElement element, XName name, bool trim = false)
|
||||||
|
{
|
||||||
|
var el = element.Element(name);
|
||||||
|
|
||||||
|
return string.IsNullOrWhiteSpace(el?.Value)
|
||||||
|
? null
|
||||||
|
: (trim ? el.Value.Trim() : el.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long ElementAsLong(this XElement element, XName name)
|
||||||
|
{
|
||||||
|
var el = element.Element(name);
|
||||||
|
return long.TryParse(el?.Value, out long value) ? value : default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int ElementAsInt(this XElement element, XName name)
|
||||||
|
{
|
||||||
|
var el = element.Element(name);
|
||||||
|
return int.TryParse(el?.Value, out int value) ? value : default(int);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetIntResponse(this XDocument document)
|
||||||
|
{
|
||||||
|
return document.XPathSelectElement("./methodResponse/params/param/value").GetIntValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetStringResponse(this XDocument document)
|
||||||
|
{
|
||||||
|
return document.XPathSelectElement("./methodResponse/params/param/value").GetStringValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user