mirror of
https://github.com/Radarr/Radarr.git
synced 2024-11-05 02:22:31 +01:00
Updated to support Hadouken v5.1 and above
Fixes indentation issues Removes AuthenticationType Corrects FieldDefinitions
This commit is contained in:
parent
abf8c684e7
commit
d81e03fcc0
@ -68,8 +68,7 @@ public void Setup()
|
||||
{
|
||||
InfoHash = "HASH",
|
||||
IsFinished = true,
|
||||
State = HadoukenTorrentState.Downloading,
|
||||
IsPaused = true,
|
||||
State = HadoukenTorrentState.Paused,
|
||||
Name = _title,
|
||||
TotalSize = 1000,
|
||||
DownloadedBytes = 1000,
|
||||
@ -122,7 +121,7 @@ protected virtual void GivenTorrents(List<HadoukenTorrent> torrents)
|
||||
|
||||
Mocker.GetMock<IHadoukenProxy>()
|
||||
.Setup(s => s.GetTorrents(It.IsAny<HadoukenSettings>()))
|
||||
.Returns(torrents.ToDictionary(k => k.InfoHash));
|
||||
.Returns(torrents.ToArray());
|
||||
}
|
||||
|
||||
protected void PrepareClientToReturnQueuedItem()
|
||||
@ -218,5 +217,78 @@ public void should_return_status_with_outputdirs()
|
||||
result.OutputRootFolders.Should().NotBeNull();
|
||||
result.OutputRootFolders.First().Should().Be(@"C:\Downloads\Downloading\deluge".AsOsAgnostic());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetItems_should_return_torrents_with_DownloadId_uppercase()
|
||||
{
|
||||
var torrent = new HadoukenTorrent
|
||||
{
|
||||
InfoHash = "hash",
|
||||
IsFinished = true,
|
||||
State = HadoukenTorrentState.Paused,
|
||||
Name = _title,
|
||||
TotalSize = 1000,
|
||||
DownloadedBytes = 1000,
|
||||
Progress = 100.0,
|
||||
SavePath = "somepath"
|
||||
};
|
||||
|
||||
var torrents = new HadoukenTorrent[] { torrent };
|
||||
Mocker.GetMock<IHadoukenProxy>()
|
||||
.Setup(v => v.GetTorrents(It.IsAny<HadoukenSettings>()))
|
||||
.Returns(torrents);
|
||||
|
||||
// Act
|
||||
var result = Subject.GetItems();
|
||||
var downloadItem = result.First();
|
||||
|
||||
downloadItem.DownloadId.Should().Be("HASH");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_from_magnet_link_should_return_hash_uppercase()
|
||||
{
|
||||
var remoteEpisode = CreateRemoteEpisode();
|
||||
|
||||
remoteEpisode.Release.DownloadUrl = "magnet:?xt=urn:btih:a45129e59d8750f9da982f53552b1e4f0457ee9f";
|
||||
|
||||
Mocker.GetMock<IHadoukenProxy>()
|
||||
.Setup(v => v.AddTorrentUri(It.IsAny<HadoukenSettings>(), It.IsAny<string>()));
|
||||
|
||||
var result = Subject.Download(remoteEpisode);
|
||||
|
||||
Assert.IsFalse(result.Any(c => char.IsLower(c)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_from_torrent_file_should_return_hash_uppercase()
|
||||
{
|
||||
var remoteEpisode = CreateRemoteEpisode();
|
||||
|
||||
Mocker.GetMock<IHadoukenProxy>()
|
||||
.Setup(v => v.AddTorrentFile(It.IsAny<HadoukenSettings>(), It.IsAny<byte[]>()))
|
||||
.Returns("hash");
|
||||
|
||||
var result = Subject.Download(remoteEpisode);
|
||||
|
||||
Assert.IsFalse(result.Any(c => char.IsLower(c)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_should_return_validation_failure_for_old_hadouken()
|
||||
{
|
||||
var systemInfo = new HadoukenSystemInfo()
|
||||
{
|
||||
Versions = new Dictionary<string, string>() { { "hadouken", "5.0.0.0" } }
|
||||
};
|
||||
|
||||
Mocker.GetMock<IHadoukenProxy>()
|
||||
.Setup(v => v.GetSystemInfo(It.IsAny<HadoukenSettings>()))
|
||||
.Returns(systemInfo);
|
||||
|
||||
var result = Subject.Test();
|
||||
|
||||
result.Errors.First().ErrorMessage.Should().Be("Old Hadouken client with unsupported API, need 5.1 or higher");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +0,0 @@
|
||||
namespace NzbDrone.Core.Download.Clients.Hadouken
|
||||
{
|
||||
public enum AuthenticationType
|
||||
{
|
||||
None = 0,
|
||||
Basic,
|
||||
Token
|
||||
}
|
||||
}
|
@ -15,7 +15,7 @@
|
||||
|
||||
namespace NzbDrone.Core.Download.Clients.Hadouken
|
||||
{
|
||||
public sealed class Hadouken : TorrentClientBase<HadoukenSettings>
|
||||
public class Hadouken : TorrentClientBase<HadoukenSettings>
|
||||
{
|
||||
private readonly IHadoukenProxy _proxy;
|
||||
|
||||
@ -38,7 +38,7 @@ public override string Name
|
||||
|
||||
public override IEnumerable<DownloadClientItem> GetItems()
|
||||
{
|
||||
IDictionary<string, HadoukenTorrent> torrents;
|
||||
HadoukenTorrent[] torrents;
|
||||
|
||||
try
|
||||
{
|
||||
@ -52,11 +52,9 @@ public override IEnumerable<DownloadClientItem> GetItems()
|
||||
|
||||
var items = new List<DownloadClientItem>();
|
||||
|
||||
foreach (var torrent in torrents.Values)
|
||||
foreach (var torrent in torrents)
|
||||
{
|
||||
var outputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(torrent.SavePath));
|
||||
outputPath += torrent.Name;
|
||||
|
||||
var eta = TimeSpan.FromSeconds(0);
|
||||
|
||||
if (torrent.DownloadRate > 0 && torrent.TotalSize > 0)
|
||||
@ -67,7 +65,7 @@ public override IEnumerable<DownloadClientItem> GetItems()
|
||||
var item = new DownloadClientItem
|
||||
{
|
||||
DownloadClient = Definition.Name,
|
||||
DownloadId = torrent.InfoHash,
|
||||
DownloadId = torrent.InfoHash.ToUpper(),
|
||||
OutputPath = outputPath + torrent.Name,
|
||||
RemainingSize = torrent.TotalSize - torrent.DownloadedBytes,
|
||||
RemainingTime = eta,
|
||||
@ -88,7 +86,7 @@ public override IEnumerable<DownloadClientItem> GetItems()
|
||||
{
|
||||
item.Status = DownloadItemStatus.Queued;
|
||||
}
|
||||
else if (torrent.IsPaused)
|
||||
else if (torrent.State == HadoukenTorrentState.Paused)
|
||||
{
|
||||
item.Status = DownloadItemStatus.Paused;
|
||||
}
|
||||
@ -97,6 +95,15 @@ public override IEnumerable<DownloadClientItem> GetItems()
|
||||
item.Status = DownloadItemStatus.Downloading;
|
||||
}
|
||||
|
||||
if (torrent.IsFinished && torrent.State == HadoukenTorrentState.Paused)
|
||||
{
|
||||
item.IsReadOnly = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
item.IsReadOnly = true;
|
||||
}
|
||||
|
||||
items.Add(item);
|
||||
}
|
||||
|
||||
@ -105,7 +112,14 @@ public override IEnumerable<DownloadClientItem> GetItems()
|
||||
|
||||
public override void RemoveItem(string downloadId, bool deleteData)
|
||||
{
|
||||
_proxy.RemoveTorrent(Settings, downloadId, deleteData);
|
||||
if (deleteData)
|
||||
{
|
||||
_proxy.RemoveTorrentAndData(Settings, downloadId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_proxy.RemoveTorrent(Settings, downloadId);
|
||||
}
|
||||
}
|
||||
|
||||
public override DownloadClientStatus GetStatus()
|
||||
@ -136,7 +150,8 @@ protected override void Test(List<ValidationFailure> failures)
|
||||
protected override string AddFromMagnetLink(RemoteEpisode remoteEpisode, string hash, string magnetLink)
|
||||
{
|
||||
_proxy.AddTorrentUri(Settings, magnetLink);
|
||||
return hash;
|
||||
|
||||
return hash.ToUpper();
|
||||
}
|
||||
|
||||
protected override string AddFromTorrentFile(RemoteEpisode remoteEpisode, string hash, string filename, byte[] fileContent)
|
||||
@ -151,20 +166,15 @@ private ValidationFailure TestConnection()
|
||||
var sysInfo = _proxy.GetSystemInfo(Settings);
|
||||
var version = new Version(sysInfo.Versions["hadouken"]);
|
||||
|
||||
if (version.Major < 5)
|
||||
if (version < new Version("5.1"))
|
||||
{
|
||||
return new ValidationFailure(string.Empty, "Old Hadouken client with unsupported API, need 5.0 or higher");
|
||||
return new ValidationFailure(string.Empty, "Old Hadouken client with unsupported API, need 5.1 or higher");
|
||||
}
|
||||
}
|
||||
catch (DownloadClientAuthenticationException ex)
|
||||
{
|
||||
_logger.ErrorException(ex.Message, ex);
|
||||
|
||||
if (Settings.AuthenticationType == (int) AuthenticationType.Token)
|
||||
{
|
||||
return new NzbDroneValidationFailure("Token", "Authentication failed");
|
||||
}
|
||||
|
||||
return new NzbDroneValidationFailure("Password", "Authentication failed");
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
namespace NzbDrone.Core.Download.Clients.Hadouken
|
||||
{
|
||||
public sealed class HadoukenProxy : IHadoukenProxy
|
||||
public class HadoukenProxy : IHadoukenProxy
|
||||
{
|
||||
private static int _callId;
|
||||
private readonly Logger _logger;
|
||||
@ -23,34 +23,39 @@ public HadoukenSystemInfo GetSystemInfo(HadoukenSettings settings)
|
||||
return ProcessRequest<HadoukenSystemInfo>(settings, "core.getSystemInfo").Result;
|
||||
}
|
||||
|
||||
public IDictionary<string, HadoukenTorrent> GetTorrents(HadoukenSettings settings)
|
||||
public HadoukenTorrent[] GetTorrents(HadoukenSettings settings)
|
||||
{
|
||||
return ProcessRequest<Dictionary<string, HadoukenTorrent>>(settings, "session.getTorrents").Result;
|
||||
var result = ProcessRequest<HadoukenResponseResult>(settings, "webui.list").Result;
|
||||
|
||||
return GetTorrents(result.Torrents);
|
||||
}
|
||||
|
||||
public IDictionary<string, object> GetConfig(HadoukenSettings settings)
|
||||
{
|
||||
return ProcessRequest<IDictionary<string, object>>(settings, "config.get").Result;
|
||||
return ProcessRequest<IDictionary<string, object>>(settings, "webui.getSettings").Result;
|
||||
}
|
||||
|
||||
public string AddTorrentFile(HadoukenSettings settings, byte[] fileContent)
|
||||
{
|
||||
return ProcessRequest<string>(settings, "session.addTorrentFile", Convert.ToBase64String(fileContent)).Result;
|
||||
return ProcessRequest<string>(settings, "webui.addTorrent", "file", Convert.ToBase64String(fileContent)).Result;
|
||||
}
|
||||
|
||||
public void AddTorrentUri(HadoukenSettings settings, string torrentUrl)
|
||||
{
|
||||
ProcessRequest<string>(settings, "session.addTorrentUri", torrentUrl);
|
||||
ProcessRequest<string>(settings, "webui.addTorrent", "url", torrentUrl);
|
||||
}
|
||||
|
||||
public void RemoveTorrent(HadoukenSettings settings, string downloadId, bool deleteData)
|
||||
public void RemoveTorrent(HadoukenSettings settings, string downloadId)
|
||||
{
|
||||
ProcessRequest<bool>(settings, "session.removeTorrent", downloadId, deleteData);
|
||||
ProcessRequest<bool>(settings, "webui.perform", "remove", new string[] { downloadId });
|
||||
}
|
||||
|
||||
private HadoukenResponse<TResult> ProcessRequest<TResult>(HadoukenSettings settings,
|
||||
string method,
|
||||
params object[] parameters)
|
||||
public void RemoveTorrentAndData(HadoukenSettings settings, string downloadId)
|
||||
{
|
||||
ProcessRequest<bool>(settings, "webui.perform", "removedata", new string[] { downloadId });
|
||||
}
|
||||
|
||||
private HadoukenResponse<TResult> ProcessRequest<TResult>(HadoukenSettings settings, string method, params object[] parameters)
|
||||
{
|
||||
var client = BuildClient(settings);
|
||||
return ProcessRequest<TResult>(client, method, parameters);
|
||||
@ -86,17 +91,10 @@ private IRestClient BuildClient(HadoukenSettings settings)
|
||||
var restClient = RestClientFactory.BuildClient(url);
|
||||
restClient.Timeout = 4000;
|
||||
|
||||
if (settings.AuthenticationType == (int) AuthenticationType.Basic)
|
||||
{
|
||||
var basicData = Encoding.UTF8.GetBytes(string.Format("{0}:{1}", settings.Username, settings.Password));
|
||||
var basicHeader = Convert.ToBase64String(basicData);
|
||||
|
||||
restClient.AddDefaultHeader("Authorization", string.Format("Basic {0}", basicHeader));
|
||||
}
|
||||
else if (settings.AuthenticationType == (int) AuthenticationType.Token)
|
||||
{
|
||||
restClient.AddDefaultHeader("Authorization", string.Format("Token {0}", settings.Token));
|
||||
}
|
||||
|
||||
return restClient;
|
||||
}
|
||||
@ -105,5 +103,76 @@ private int GetCallId()
|
||||
{
|
||||
return System.Threading.Interlocked.Increment(ref _callId);
|
||||
}
|
||||
|
||||
private HadoukenTorrent[] GetTorrents(object[][] torrentsRaw)
|
||||
{
|
||||
if (torrentsRaw == null)
|
||||
{
|
||||
return new HadoukenTorrent[0];
|
||||
}
|
||||
|
||||
var torrents = new List<HadoukenTorrent>();
|
||||
|
||||
foreach (var item in torrentsRaw)
|
||||
{
|
||||
var torrent = MapTorrent(item);
|
||||
if (torrent != null)
|
||||
{
|
||||
torrent.IsFinished = torrent.Progress >= 1000;
|
||||
torrents.Add(torrent);
|
||||
}
|
||||
}
|
||||
|
||||
return torrents.ToArray();
|
||||
}
|
||||
|
||||
private HadoukenTorrent MapTorrent(object[] item)
|
||||
{
|
||||
HadoukenTorrent torrent = null;
|
||||
|
||||
try
|
||||
{
|
||||
torrent = new HadoukenTorrent()
|
||||
{
|
||||
InfoHash = Convert.ToString(item[0]),
|
||||
State = ParseState(Convert.ToInt32(item[1])),
|
||||
Name = Convert.ToString(item[2]),
|
||||
TotalSize = Convert.ToInt64(item[3]),
|
||||
Progress = Convert.ToDouble(item[4]),
|
||||
DownloadedBytes = Convert.ToInt64(item[5]),
|
||||
DownloadRate = Convert.ToInt64(item[9]),
|
||||
Error = Convert.ToString(item[21]),
|
||||
SavePath = Convert.ToString(item[26])
|
||||
};
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Failed to map Hadouken torrent data.", ex);
|
||||
}
|
||||
|
||||
return torrent;
|
||||
}
|
||||
|
||||
private HadoukenTorrentState ParseState(int state)
|
||||
{
|
||||
if ((state & 1) == 1)
|
||||
{
|
||||
return HadoukenTorrentState.Downloading;
|
||||
}
|
||||
else if ((state & 2) == 2)
|
||||
{
|
||||
return HadoukenTorrentState.CheckingFiles;
|
||||
}
|
||||
else if ((state & 32) == 32)
|
||||
{
|
||||
return HadoukenTorrentState.Paused;
|
||||
}
|
||||
else if ((state & 64) == 64)
|
||||
{
|
||||
return HadoukenTorrentState.QueuedForChecking;
|
||||
}
|
||||
|
||||
return HadoukenTorrentState.Unknown;
|
||||
}
|
||||
}
|
||||
}
|
@ -6,4 +6,9 @@ public class HadoukenResponse<TResult>
|
||||
public TResult Result { get; set; }
|
||||
public HadoukenError Error { get; set; }
|
||||
}
|
||||
|
||||
public class HadoukenResponseResult
|
||||
{
|
||||
public object[][] Torrents { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
namespace NzbDrone.Core.Download.Clients.Hadouken
|
||||
{
|
||||
public sealed class HadoukenSettings : IProviderConfig
|
||||
public class HadoukenSettings : IProviderConfig
|
||||
{
|
||||
private static readonly HadoukenSettingsValidator Validator = new HadoukenSettingsValidator();
|
||||
|
||||
@ -20,19 +20,13 @@ public HadoukenSettings()
|
||||
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)]
|
||||
public int Port { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Auth. type", Type = FieldType.Select, SelectOptions = typeof(AuthenticationType), HelpText = "How to authenticate against Hadouken.")]
|
||||
public int AuthenticationType { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Username", Type = FieldType.Textbox, HelpText = "Only used for basic auth.")]
|
||||
[FieldDefinition(2, Label = "Username", Type = FieldType.Textbox)]
|
||||
public string Username { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "Password", Type = FieldType.Password, HelpText = "Only used for basic auth.")]
|
||||
[FieldDefinition(3, Label = "Password", Type = FieldType.Password)]
|
||||
public string Password { get; set; }
|
||||
|
||||
[FieldDefinition(5, Label = "Token", Type = FieldType.Password, HelpText = "Only used for token auth.")]
|
||||
public string Token { get; set; }
|
||||
|
||||
[FieldDefinition(6, Label = "Use SSL", Type = FieldType.Checkbox, Advanced = true)]
|
||||
[FieldDefinition(4, Label = "Use SSL", Type = FieldType.Checkbox, Advanced = true)]
|
||||
public bool UseSsl { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
@ -3,24 +3,18 @@
|
||||
|
||||
namespace NzbDrone.Core.Download.Clients.Hadouken
|
||||
{
|
||||
public sealed class HadoukenSettingsValidator : AbstractValidator<HadoukenSettings>
|
||||
public class HadoukenSettingsValidator : AbstractValidator<HadoukenSettings>
|
||||
{
|
||||
public HadoukenSettingsValidator()
|
||||
{
|
||||
RuleFor(c => c.Host).ValidHost();
|
||||
RuleFor(c => c.Port).GreaterThan(0);
|
||||
|
||||
RuleFor(c => c.Token).NotEmpty()
|
||||
.When(c => c.AuthenticationType == (int) AuthenticationType.Token)
|
||||
.WithMessage("Token must not be empty when using token auth.");
|
||||
|
||||
RuleFor(c => c.Username).NotEmpty()
|
||||
.When(c => c.AuthenticationType == (int)AuthenticationType.Basic)
|
||||
.WithMessage("Username must not be empty when using basic auth.");
|
||||
.WithMessage("Username must not be empty.");
|
||||
|
||||
RuleFor(c => c.Password).NotEmpty()
|
||||
.When(c => c.AuthenticationType == (int)AuthenticationType.Basic)
|
||||
.WithMessage("Password must not be empty when using basic auth.");
|
||||
.WithMessage("Password must not be empty.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,10 +6,11 @@ namespace NzbDrone.Core.Download.Clients.Hadouken
|
||||
public interface IHadoukenProxy
|
||||
{
|
||||
HadoukenSystemInfo GetSystemInfo(HadoukenSettings settings);
|
||||
IDictionary<string, HadoukenTorrent> GetTorrents(HadoukenSettings settings);
|
||||
HadoukenTorrent[] GetTorrents(HadoukenSettings settings);
|
||||
IDictionary<string, object> GetConfig(HadoukenSettings settings);
|
||||
string AddTorrentFile(HadoukenSettings settings, byte[] fileContent);
|
||||
void AddTorrentUri(HadoukenSettings settings, string torrentUrl);
|
||||
void RemoveTorrent(HadoukenSettings settings, string downloadId, bool deleteData);
|
||||
void RemoveTorrent(HadoukenSettings settings, string downloadId);
|
||||
void RemoveTorrentAndData(HadoukenSettings settings, string downloadId);
|
||||
}
|
||||
}
|
||||
|
@ -2,13 +2,15 @@
|
||||
{
|
||||
public enum HadoukenTorrentState
|
||||
{
|
||||
QueuedForChecking = 0,
|
||||
CheckingFiles = 1,
|
||||
DownloadingMetadata = 2,
|
||||
Downloading = 3,
|
||||
Finished = 4,
|
||||
Seeding = 5,
|
||||
Allocating = 6,
|
||||
CheckingResumeData = 7
|
||||
Unknown = 0,
|
||||
QueuedForChecking = 1,
|
||||
CheckingFiles = 2,
|
||||
DownloadingMetadata = 3,
|
||||
Downloading = 4,
|
||||
Finished = 5,
|
||||
Seeding = 6,
|
||||
Allocating = 7,
|
||||
CheckingResumeData = 8,
|
||||
Paused = 9
|
||||
}
|
||||
}
|
||||
|
@ -348,7 +348,6 @@
|
||||
<Compile Include="Download\Clients\Deluge\DelugeUpdateUIResult.cs" />
|
||||
<Compile Include="Download\Clients\DownloadClientAuthenticationException.cs" />
|
||||
<Compile Include="Download\Clients\DownloadClientException.cs" />
|
||||
<Compile Include="Download\Clients\Hadouken\AuthenticationType.cs" />
|
||||
<Compile Include="Download\Clients\Hadouken\Hadouken.cs" />
|
||||
<Compile Include="Download\Clients\Hadouken\HadoukenError.cs" />
|
||||
<Compile Include="Download\Clients\Hadouken\HadoukenProxy.cs" />
|
||||
|
Loading…
Reference in New Issue
Block a user