mirror of
https://github.com/Sonarr/Sonarr.git
synced 2024-10-30 23:42:33 +01:00
Fixed: Refactored rtorrent interface to fix reliability issues with adding magnets & torrents.
fixes #1745
This commit is contained in:
parent
3590fedeaf
commit
2326db0dea
@ -54,11 +54,11 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.RTorrentTests
|
|||||||
protected void GivenSuccessfulDownload()
|
protected void GivenSuccessfulDownload()
|
||||||
{
|
{
|
||||||
Mocker.GetMock<IRTorrentProxy>()
|
Mocker.GetMock<IRTorrentProxy>()
|
||||||
.Setup(s => s.AddTorrentFromUrl(It.IsAny<string>(), It.IsAny<RTorrentSettings>()))
|
.Setup(s => s.AddTorrentFromUrl(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<RTorrentPriority>(), It.IsAny<string>(), It.IsAny<RTorrentSettings>()))
|
||||||
.Callback(PrepareClientToReturnCompletedItem);
|
.Callback(PrepareClientToReturnCompletedItem);
|
||||||
|
|
||||||
Mocker.GetMock<IRTorrentProxy>()
|
Mocker.GetMock<IRTorrentProxy>()
|
||||||
.Setup(s => s.AddTorrentFromFile(It.IsAny<string>(), It.IsAny<byte[]>(), It.IsAny<RTorrentSettings>()))
|
.Setup(s => s.AddTorrentFromFile(It.IsAny<string>(), It.IsAny<byte[]>(), It.IsAny<string>(), It.IsAny<RTorrentPriority>(), It.IsAny<string>(), It.IsAny<RTorrentSettings>()))
|
||||||
.Callback(PrepareClientToReturnCompletedItem);
|
.Callback(PrepareClientToReturnCompletedItem);
|
||||||
|
|
||||||
|
|
||||||
|
@ -39,50 +39,29 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
|
|||||||
|
|
||||||
protected override string AddFromMagnetLink(RemoteEpisode remoteEpisode, string hash, string magnetLink)
|
protected override string AddFromMagnetLink(RemoteEpisode remoteEpisode, string hash, string magnetLink)
|
||||||
{
|
{
|
||||||
_proxy.AddTorrentFromUrl(magnetLink, Settings);
|
var priority = (RTorrentPriority)(remoteEpisode.IsRecentEpisode() ? Settings.RecentTvPriority : Settings.OlderTvPriority);
|
||||||
|
|
||||||
// Download the magnet to the appropriate directory.
|
_proxy.AddTorrentFromUrl(magnetLink, Settings.TvCategory, priority, Settings.TvDirectory, Settings);
|
||||||
_proxy.SetTorrentLabel(hash, Settings.TvCategory, Settings);
|
|
||||||
SetPriority(remoteEpisode, hash);
|
|
||||||
SetDownloadDirectory(hash);
|
|
||||||
|
|
||||||
_proxy.StartTorrent(hash, Settings);
|
|
||||||
|
|
||||||
var tries = 10;
|
var tries = 10;
|
||||||
var retryDelay = 500;
|
var retryDelay = 500;
|
||||||
|
|
||||||
// Wait a bit for the magnet to be resolved.
|
// Wait a bit for the magnet to be resolved.
|
||||||
if (!WaitForTorrent(hash, tries - 2, retryDelay))
|
if (!WaitForTorrent(hash, tries, retryDelay))
|
||||||
{
|
{
|
||||||
// Once the magnet meta download finishes, rTorrent replaces it with the actual torrent download with default settings.
|
_logger.Warn("rTorrent could not resolve magnet within {0} seconds, download may remain stuck: {1}.", tries * retryDelay / 1000, magnetLink);
|
||||||
// Schedule an event to apply the appropriate settings when that happens.
|
|
||||||
var priority = (RTorrentPriority)(remoteEpisode.IsRecentEpisode() ? Settings.RecentTvPriority : Settings.OlderTvPriority);
|
|
||||||
_proxy.SetDeferredMagnetProperties(hash, Settings.TvCategory, Settings.TvDirectory, priority, Settings);
|
|
||||||
|
|
||||||
// Wait a bit longer for the magnet to be resolved.
|
return hash;
|
||||||
if (!WaitForTorrent(hash, 2, retryDelay))
|
|
||||||
{
|
|
||||||
// The SetDeferredMagnetProperties will try set the label, priority & directory when rtorrent finishes the magnet, unless rtorrent restarts before that happens.
|
|
||||||
|
|
||||||
_logger.Warn("rTorrent could not resolve magnet within {0} seconds, download may remain stuck: {1}.", tries * retryDelay / 1000, magnetLink);
|
|
||||||
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the label is set and the torrent started, because we can't rely on SetDeferredMagnetProperties for magnets that get resolved quickly.
|
|
||||||
_proxy.SetTorrentLabel(hash, Settings.TvCategory, Settings);
|
|
||||||
SetPriority(remoteEpisode, hash);
|
|
||||||
SetDownloadDirectory(hash);
|
|
||||||
|
|
||||||
_proxy.StartTorrent(hash, Settings);
|
|
||||||
|
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
var priority = (RTorrentPriority)(remoteEpisode.IsRecentEpisode() ? Settings.RecentTvPriority : Settings.OlderTvPriority);
|
||||||
|
|
||||||
|
_proxy.AddTorrentFromFile(filename, fileContent, Settings.TvCategory, priority, Settings.TvDirectory, Settings);
|
||||||
|
|
||||||
var tries = 10;
|
var tries = 10;
|
||||||
var retryDelay = 500;
|
var retryDelay = 500;
|
||||||
@ -93,12 +72,6 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
|
|||||||
throw new ReleaseDownloadException(remoteEpisode.Release, "Downloading torrent failed");
|
throw new ReleaseDownloadException(remoteEpisode.Release, "Downloading torrent failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
_proxy.SetTorrentLabel(hash, Settings.TvCategory, Settings);
|
|
||||||
SetPriority(remoteEpisode, hash);
|
|
||||||
SetDownloadDirectory(hash);
|
|
||||||
|
|
||||||
_proxy.StartTorrent(hash, Settings);
|
|
||||||
|
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,20 +211,6 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
|
|||||||
return result.Errors.First();
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool WaitForTorrent(string hash, int tries, int retryDelay)
|
private bool WaitForTorrent(string hash, int tries, int retryDelay)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < tries; i++)
|
for (var i = 0; i < tries; i++)
|
||||||
|
@ -13,15 +13,10 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
|
|||||||
string GetVersion(RTorrentSettings settings);
|
string GetVersion(RTorrentSettings settings);
|
||||||
List<RTorrentTorrent> GetTorrents(RTorrentSettings settings);
|
List<RTorrentTorrent> GetTorrents(RTorrentSettings settings);
|
||||||
|
|
||||||
void AddTorrentFromUrl(string torrentUrl, RTorrentSettings settings);
|
void AddTorrentFromUrl(string torrentUrl, string label, RTorrentPriority priority, string directory, RTorrentSettings settings);
|
||||||
void AddTorrentFromFile(string fileName, byte[] fileContent, 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 SetTorrentPriority(string hash, RTorrentPriority priority, 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);
|
|
||||||
void SetDeferredMagnetProperties(string hash, string category, string directory, RTorrentPriority priority, RTorrentSettings settings);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IRTorrent : IXmlRpcProxy
|
public interface IRTorrent : IXmlRpcProxy
|
||||||
@ -29,35 +24,20 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
|
|||||||
[XmlRpcMethod("d.multicall2")]
|
[XmlRpcMethod("d.multicall2")]
|
||||||
object[] TorrentMulticall(params string[] parameters);
|
object[] TorrentMulticall(params string[] parameters);
|
||||||
|
|
||||||
[XmlRpcMethod("load.normal")]
|
[XmlRpcMethod("load.start")]
|
||||||
int LoadUrl(string target, string data);
|
int LoadStart(string target, string data, params string[] commands);
|
||||||
|
|
||||||
[XmlRpcMethod("load.raw")]
|
[XmlRpcMethod("load.raw_start")]
|
||||||
int LoadBinary(string target, byte[] data);
|
int LoadRawStart(string target, byte[] data, params string[] commands);
|
||||||
|
|
||||||
[XmlRpcMethod("d.erase")]
|
[XmlRpcMethod("d.erase")]
|
||||||
int Remove(string hash);
|
int Remove(string hash);
|
||||||
|
|
||||||
[XmlRpcMethod("d.custom1.set")]
|
|
||||||
string SetLabel(string hash, string label);
|
|
||||||
|
|
||||||
[XmlRpcMethod("d.priority.set")]
|
|
||||||
int SetPriority(string hash, long priority);
|
|
||||||
|
|
||||||
[XmlRpcMethod("d.directory.set")]
|
|
||||||
int SetDirectory(string hash, string directory);
|
|
||||||
|
|
||||||
[XmlRpcMethod("method.set_key")]
|
|
||||||
int SetKey(string target, string key, string cmd_key, string value);
|
|
||||||
|
|
||||||
[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
|
||||||
@ -122,26 +102,26 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
|
|||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddTorrentFromUrl(string torrentUrl, RTorrentSettings settings)
|
public void AddTorrentFromUrl(string torrentUrl, string label, RTorrentPriority priority, string directory, RTorrentSettings settings)
|
||||||
{
|
{
|
||||||
_logger.Debug("Executing remote method: load.normal");
|
_logger.Debug("Executing remote method: load.normal");
|
||||||
|
|
||||||
var client = BuildClient(settings);
|
var client = BuildClient(settings);
|
||||||
|
|
||||||
var response = client.LoadUrl("", torrentUrl);
|
var response = client.LoadStart("", torrentUrl, GetCommands(label, priority, directory));
|
||||||
if (response != 0)
|
if (response != 0)
|
||||||
{
|
{
|
||||||
throw new DownloadClientException("Could not add torrent: {0}.", torrentUrl);
|
throw new DownloadClientException("Could not add torrent: {0}.", torrentUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddTorrentFromFile(string fileName, byte[] fileContent, RTorrentSettings settings)
|
public void AddTorrentFromFile(string fileName, byte[] fileContent, string label, RTorrentPriority priority, string directory, RTorrentSettings settings)
|
||||||
{
|
{
|
||||||
_logger.Debug("Executing remote method: load.raw");
|
_logger.Debug("Executing remote method: load.raw");
|
||||||
|
|
||||||
var client = BuildClient(settings);
|
var client = BuildClient(settings);
|
||||||
|
|
||||||
var response = client.LoadBinary("", fileContent);
|
var response = client.LoadRawStart("", fileContent, GetCommands(label, priority, directory));
|
||||||
if (response != 0)
|
if (response != 0)
|
||||||
{
|
{
|
||||||
throw new DownloadClientException("Could not add torrent: {0}.", fileName);
|
throw new DownloadClientException("Could not add torrent: {0}.", fileName);
|
||||||
@ -161,94 +141,26 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetTorrentPriority(string hash, RTorrentPriority priority, RTorrentSettings settings)
|
private string[] GetCommands(string label, RTorrentPriority priority, string directory)
|
||||||
{
|
{
|
||||||
_logger.Debug("Executing remote method: d.priority.set");
|
var result = new List<string>();
|
||||||
|
|
||||||
var client = BuildClient(settings);
|
if (label.IsNotNullOrWhiteSpace())
|
||||||
|
|
||||||
var response = client.SetPriority(hash, (long) priority);
|
|
||||||
if (response != 0)
|
|
||||||
{
|
{
|
||||||
throw new DownloadClientException("Could not set priority on torrent: {0}.", hash);
|
result.Add("d.custom1.set=" + label);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetTorrentLabel(string hash, string label, RTorrentSettings settings)
|
|
||||||
{
|
|
||||||
_logger.Debug("Executing remote method: d.custom1.set");
|
|
||||||
|
|
||||||
var labelEncoded = System.Web.HttpUtility.UrlEncode(label);
|
|
||||||
|
|
||||||
var client = BuildClient(settings);
|
|
||||||
|
|
||||||
var setLabel = client.SetLabel(hash, labelEncoded);
|
|
||||||
if (setLabel != labelEncoded)
|
|
||||||
{
|
|
||||||
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 void SetDeferredMagnetProperties(string hash, string category, string directory, RTorrentPriority priority, RTorrentSettings settings)
|
|
||||||
{
|
|
||||||
var commands = new List<string>();
|
|
||||||
|
|
||||||
if (category.IsNotNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
commands.Add("d.custom1.set=" + category);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (directory.IsNotNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
commands.Add("d.directory.set=" + directory);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (priority != RTorrentPriority.Normal)
|
if (priority != RTorrentPriority.Normal)
|
||||||
{
|
{
|
||||||
commands.Add("d.priority.set=" + (long)priority);
|
result.Add("d.priority.set=" + (int)priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure it gets started if the user doesn't have schedule=...,start_tied=
|
if (directory.IsNotNullOrWhiteSpace())
|
||||||
commands.Add("d.open=");
|
|
||||||
commands.Add("d.start=");
|
|
||||||
|
|
||||||
if (commands.Any())
|
|
||||||
{
|
{
|
||||||
var key = "event.download.inserted_new";
|
result.Add("d.directory.set=" + label);
|
||||||
var cmd_key = "sonarr_deferred_" + hash;
|
|
||||||
|
|
||||||
commands.Add(string.Format("print=\"Applying deferred properties to {0}\"", hash));
|
|
||||||
|
|
||||||
// Remove event handler once triggered.
|
|
||||||
commands.Add(string.Format("\"method.set_key={0},{1}\"", key, cmd_key));
|
|
||||||
|
|
||||||
var setKeyValue = string.Format("branch=\"equal=d.hash=,cat={0}\",{{{1}}}", hash, string.Join(",", commands));
|
|
||||||
|
|
||||||
_logger.Debug("Executing remote method: method.set_key = {0},{1},{2}", key, cmd_key, setKeyValue);
|
|
||||||
|
|
||||||
var client = BuildClient(settings);
|
|
||||||
|
|
||||||
// Commands need a target, in this case the target is an empty string
|
|
||||||
// See: https://github.com/rakshasa/rtorrent/issues/227
|
|
||||||
var response = client.SetKey("", key, cmd_key, setKeyValue);
|
|
||||||
if (response != 0)
|
|
||||||
{
|
|
||||||
throw new DownloadClientException("Could set properties for torrent: {0}.", hash);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HasHashTorrent(string hash, RTorrentSettings settings)
|
public bool HasHashTorrent(string hash, RTorrentSettings settings)
|
||||||
@ -270,32 +182,6 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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>();
|
||||||
|
Loading…
Reference in New Issue
Block a user