diff --git a/src/NzbDrone.Core.Test/Download/DownloadClientTests/Blackhole/TorrentBlackholeFixture.cs b/src/NzbDrone.Core.Test/Download/DownloadClientTests/Blackhole/TorrentBlackholeFixture.cs index ae15a8eca..5a61271cf 100644 --- a/src/NzbDrone.Core.Test/Download/DownloadClientTests/Blackhole/TorrentBlackholeFixture.cs +++ b/src/NzbDrone.Core.Test/Download/DownloadClientTests/Blackhole/TorrentBlackholeFixture.cs @@ -22,6 +22,7 @@ public class TorrentBlackholeFixture : DownloadClientFixtureBase(Mocker.Resolve()); @@ -132,6 +134,51 @@ public void Download_should_download_file_if_it_doesnt_exist() Mocker.GetMock().Verify(c => c.DownloadFile(It.IsAny(), It.IsAny()), Times.Never()); } + [Test] + public void Download_should_save_magnet_if_enabled() + { + Subject.Definition.Settings.As().SaveMagnetFiles = true; + + var remoteEpisode = CreateRemoteEpisode(); + remoteEpisode.Release.DownloadUrl = null; + + Subject.Download(remoteEpisode); + + Mocker.GetMock().Verify(c => c.Get(It.Is(v => v.Url.FullUri == _downloadUrl)), Times.Never()); + Mocker.GetMock().Verify(c => c.OpenWriteStream(_filePath), Times.Never()); + Mocker.GetMock().Verify(c => c.OpenWriteStream(_magnetFilePath), Times.Once()); + Mocker.GetMock().Verify(c => c.DownloadFile(It.IsAny(), It.IsAny()), Times.Never()); + } + + [Test] + public void Download_should_not_save_magnet_if_disabled() + { + var remoteEpisode = CreateRemoteEpisode(); + remoteEpisode.Release.DownloadUrl = null; + + Assert.Throws(() => Subject.Download(remoteEpisode)); + + Mocker.GetMock().Verify(c => c.Get(It.Is(v => v.Url.FullUri == _downloadUrl)), Times.Never()); + Mocker.GetMock().Verify(c => c.OpenWriteStream(_filePath), Times.Never()); + Mocker.GetMock().Verify(c => c.OpenWriteStream(_magnetFilePath), Times.Never()); + Mocker.GetMock().Verify(c => c.DownloadFile(It.IsAny(), It.IsAny()), Times.Never()); + } + + [Test] + public void Download_should_prefer_torrent_over_magnet() + { + Subject.Definition.Settings.As().SaveMagnetFiles = true; + + var remoteEpisode = CreateRemoteEpisode(); + + Subject.Download(remoteEpisode); + + Mocker.GetMock().Verify(c => c.Get(It.Is(v => v.Url.FullUri == _downloadUrl)), Times.Once()); + Mocker.GetMock().Verify(c => c.OpenWriteStream(_filePath), Times.Once()); + Mocker.GetMock().Verify(c => c.OpenWriteStream(_magnetFilePath), Times.Never()); + Mocker.GetMock().Verify(c => c.DownloadFile(It.IsAny(), It.IsAny()), Times.Never()); + } + [Test] public void Download_should_replace_illegal_characters_in_title() { diff --git a/src/NzbDrone.Core/Download/Clients/Blackhole/TorrentBlackhole.cs b/src/NzbDrone.Core/Download/Clients/Blackhole/TorrentBlackhole.cs index a7f01e81f..5617df565 100644 --- a/src/NzbDrone.Core/Download/Clients/Blackhole/TorrentBlackhole.cs +++ b/src/NzbDrone.Core/Download/Clients/Blackhole/TorrentBlackhole.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text; using FluentValidation.Results; using NLog; using NzbDrone.Common.Disk; @@ -23,6 +24,14 @@ public class TorrentBlackhole : TorrentClientBase public TimeSpan ScanGracePeriod { get; set; } + public override bool PreferTorrentFile + { + get + { + return true; + } + } + public TorrentBlackhole(IScanWatchFolder scanWatchFolder, ITorrentFileInfoReader torrentFileInfoReader, IHttpClient httpClient, @@ -39,7 +48,26 @@ public TorrentBlackhole(IScanWatchFolder scanWatchFolder, protected override string AddFromMagnetLink(RemoteEpisode remoteEpisode, string hash, string magnetLink) { - throw new NotSupportedException("Blackhole does not support magnet links."); + if (!Settings.SaveMagnetFiles) + { + throw new NotSupportedException("Blackhole does not support magnet links."); + } + + var title = remoteEpisode.Release.Title; + + title = FileNameBuilder.CleanFileName(title); + + var filepath = Path.Combine(Settings.TorrentFolder, string.Format("{0}.magnet", title)); + + var fileContent = Encoding.UTF8.GetBytes(magnetLink); + using (var stream = _diskProvider.OpenWriteStream(filepath)) + { + stream.Write(fileContent, 0, fileContent.Length); + } + + _logger.Debug("Saving magnet link succeeded, saved to: {0}", filepath); + + return null; } protected override string AddFromTorrentFile(RemoteEpisode remoteEpisode, string hash, string filename, byte[] fileContent) diff --git a/src/NzbDrone.Core/Download/Clients/Blackhole/TorrentBlackholeSettings.cs b/src/NzbDrone.Core/Download/Clients/Blackhole/TorrentBlackholeSettings.cs index 645607ca4..d05ee7f22 100644 --- a/src/NzbDrone.Core/Download/Clients/Blackhole/TorrentBlackholeSettings.cs +++ b/src/NzbDrone.Core/Download/Clients/Blackhole/TorrentBlackholeSettings.cs @@ -34,7 +34,12 @@ public TorrentBlackholeSettings() [DefaultValue(false)] [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] - [FieldDefinition(2, Label = "Read Only", Type = FieldType.Checkbox, HelpText = "Instead of moving files this will instruct Sonarr to Copy or Hardlink (depending on settings/system configuration)")] + [FieldDefinition(2, Label = "Save Magnet Files", Type = FieldType.Checkbox, HelpText = "Save a .magnet file with the magnet link if no .torrent file is available (only useful if the download client supports .magnet files)")] + public bool SaveMagnetFiles { get; set; } + + [DefaultValue(false)] + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] + [FieldDefinition(3, Label = "Read Only", Type = FieldType.Checkbox, HelpText = "Instead of moving files this will instruct Sonarr to Copy or Hardlink (depending on settings/system configuration)")] public bool ReadOnly { get; set; } public NzbDroneValidationResult Validate() diff --git a/src/NzbDrone.Core/Download/TorrentClientBase.cs b/src/NzbDrone.Core/Download/TorrentClientBase.cs index 9ef2f0164..68b78b40c 100644 --- a/src/NzbDrone.Core/Download/TorrentClientBase.cs +++ b/src/NzbDrone.Core/Download/TorrentClientBase.cs @@ -42,6 +42,14 @@ public override DownloadProtocol Protocol } } + public virtual bool PreferTorrentFile + { + get + { + return false; + } + } + protected abstract string AddFromMagnetLink(RemoteEpisode remoteEpisode, string hash, string magnetLink); protected abstract string AddFromTorrentFile(RemoteEpisode remoteEpisode, string hash, string filename, byte[] fileContent); @@ -65,32 +73,64 @@ public override string Download(RemoteEpisode remoteEpisode) { magnetUrl = torrentInfo.MagnetUrl; } - - string hash = null; - - if (magnetUrl.IsNotNullOrWhiteSpace()) + + if (PreferTorrentFile) { - try + if (torrentUrl.IsNotNullOrWhiteSpace()) { - hash = DownloadFromMagnetUrl(remoteEpisode, magnetUrl); + try + { + return DownloadFromWebUrl(remoteEpisode, torrentUrl); + } + catch (Exception ex) + { + if (!magnetUrl.IsNullOrWhiteSpace()) + { + throw; + } + + _logger.Debug("Torrent download failed, trying magnet. ({0})", ex.Message); + } } - catch (NotSupportedException ex) + + if (magnetUrl.IsNotNullOrWhiteSpace()) { - if (torrentUrl.IsNullOrWhiteSpace()) + try + { + return DownloadFromMagnetUrl(remoteEpisode, magnetUrl); + } + catch (NotSupportedException ex) { throw new ReleaseDownloadException(remoteEpisode.Release, "Magnet not supported by download client. ({0})", ex.Message); } + } + } + else + { + if (magnetUrl.IsNotNullOrWhiteSpace()) + { + try + { + return DownloadFromMagnetUrl(remoteEpisode, magnetUrl); + } + catch (NotSupportedException ex) + { + if (torrentUrl.IsNullOrWhiteSpace()) + { + throw new ReleaseDownloadException(remoteEpisode.Release, "Magnet not supported by download client. ({0})", ex.Message); + } - _logger.Debug("Magnet not supported by download client, trying torrent. ({0})", ex.Message); + _logger.Debug("Magnet not supported by download client, trying torrent. ({0})", ex.Message); + } + } + + if (torrentUrl.IsNotNullOrWhiteSpace()) + { + return DownloadFromWebUrl(remoteEpisode, torrentUrl); } } - if (hash == null && torrentUrl.IsNotNullOrWhiteSpace()) - { - hash = DownloadFromWebUrl(remoteEpisode, torrentUrl); - } - - return hash; + return null; } private string DownloadFromWebUrl(RemoteEpisode remoteEpisode, string torrentUrl)