From b3f8e648cdbc06ddadef5f9309f161fe7c9394b1 Mon Sep 17 00:00:00 2001 From: Qstick Date: Fri, 30 Jul 2021 22:39:32 -0400 Subject: [PATCH] New: (Clients) Torrent and Usenet Blackhole Fixes #238 --- .../Extensions/StringExtensions.cs | 16 ++++ .../Clients/Blackhole/TorrentBlackhole.cs | 82 +++++++++++++++++++ .../Blackhole/TorrentBlackholeSettings.cs | 46 +++++++++++ .../Clients/Blackhole/UsenetBlackhole.cs | 54 ++++++++++++ .../Blackhole/UsenetBlackholeSettings.cs | 29 +++++++ 5 files changed, 227 insertions(+) create mode 100644 src/NzbDrone.Core/Download/Clients/Blackhole/TorrentBlackhole.cs create mode 100644 src/NzbDrone.Core/Download/Clients/Blackhole/TorrentBlackholeSettings.cs create mode 100644 src/NzbDrone.Core/Download/Clients/Blackhole/UsenetBlackhole.cs create mode 100644 src/NzbDrone.Core/Download/Clients/Blackhole/UsenetBlackholeSettings.cs diff --git a/src/NzbDrone.Common/Extensions/StringExtensions.cs b/src/NzbDrone.Common/Extensions/StringExtensions.cs index 29083c43a..744309caf 100644 --- a/src/NzbDrone.Common/Extensions/StringExtensions.cs +++ b/src/NzbDrone.Common/Extensions/StringExtensions.cs @@ -194,5 +194,21 @@ namespace NzbDrone.Common.Extensions var inputBytes = encoding.GetBytes(searchString); return encoding.GetString(WebUtility.UrlDecodeToBytes(inputBytes, 0, inputBytes.Length)); } + + public static string CleanFileName(this string name) + { + string result = name; + string[] badCharacters = { "\\", "/", "<", ">", "?", "*", ":", "|", "\"" }; + string[] goodCharacters = { "+", "+", "", "", "!", "-", "-", "", "" }; + + result = result.Replace(": ", " - "); + + for (int i = 0; i < badCharacters.Length; i++) + { + result = result.Replace(badCharacters[i], goodCharacters[i]); + } + + return result.TrimStart(' ', '.').TrimEnd(' '); + } } } diff --git a/src/NzbDrone.Core/Download/Clients/Blackhole/TorrentBlackhole.cs b/src/NzbDrone.Core/Download/Clients/Blackhole/TorrentBlackhole.cs new file mode 100644 index 000000000..3315d43e9 --- /dev/null +++ b/src/NzbDrone.Core/Download/Clients/Blackhole/TorrentBlackhole.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using FluentValidation.Results; +using NLog; +using NzbDrone.Common.Disk; +using NzbDrone.Common.Extensions; +using NzbDrone.Common.Http; +using NzbDrone.Core.Configuration; +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.Download.Clients.Blackhole +{ + public class TorrentBlackhole : TorrentClientBase + { + public override bool PreferTorrentFile => true; + + public TorrentBlackhole(ITorrentFileInfoReader torrentFileInfoReader, + IHttpClient httpClient, + IConfigService configService, + IDiskProvider diskProvider, + Logger logger) + : base(torrentFileInfoReader, httpClient, configService, diskProvider, logger) + { + } + + protected override string AddFromTorrentLink(ReleaseInfo release, string hash, string torrentLink) + { + throw new NotImplementedException("Blackhole does not support redirected indexers."); + } + + protected override string AddFromMagnetLink(ReleaseInfo release, string hash, string magnetLink) + { + if (!Settings.SaveMagnetFiles) + { + throw new NotSupportedException("Blackhole does not support magnet links."); + } + + var title = release.Title; + + title = title.CleanFileName(); + + var filepath = Path.Combine(Settings.TorrentFolder, $"{title}.{Settings.MagnetFileExtension.Trim('.')}"); + + 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(ReleaseInfo release, string hash, string filename, byte[] fileContent) + { + var title = release.Title; + + title = title.CleanFileName(); + + var filepath = Path.Combine(Settings.TorrentFolder, string.Format("{0}.torrent", title)); + + using (var stream = _diskProvider.OpenWriteStream(filepath)) + { + stream.Write(fileContent, 0, fileContent.Length); + } + + _logger.Debug("Torrent Download succeeded, saved to: {0}", filepath); + + return null; + } + + public override string Name => "Torrent Blackhole"; + + protected override void Test(List failures) + { + failures.AddIfNotNull(TestFolder(Settings.TorrentFolder, "TorrentFolder")); + } + } +} diff --git a/src/NzbDrone.Core/Download/Clients/Blackhole/TorrentBlackholeSettings.cs b/src/NzbDrone.Core/Download/Clients/Blackhole/TorrentBlackholeSettings.cs new file mode 100644 index 000000000..a25d0fd4d --- /dev/null +++ b/src/NzbDrone.Core/Download/Clients/Blackhole/TorrentBlackholeSettings.cs @@ -0,0 +1,46 @@ +using System.ComponentModel; +using FluentValidation; +using Newtonsoft.Json; +using NzbDrone.Core.Annotations; +using NzbDrone.Core.ThingiProvider; +using NzbDrone.Core.Validation; +using NzbDrone.Core.Validation.Paths; + +namespace NzbDrone.Core.Download.Clients.Blackhole +{ + public class TorrentBlackholeSettingsValidator : AbstractValidator + { + public TorrentBlackholeSettingsValidator() + { + //Todo: Validate that the path actually exists + RuleFor(c => c.TorrentFolder).IsValidPath(); + RuleFor(c => c.MagnetFileExtension).NotEmpty(); + } + } + + public class TorrentBlackholeSettings : IProviderConfig + { + public TorrentBlackholeSettings() + { + MagnetFileExtension = ".magnet"; + } + + private static readonly TorrentBlackholeSettingsValidator Validator = new TorrentBlackholeSettingsValidator(); + + [FieldDefinition(0, Label = "Torrent Folder", Type = FieldType.Path, HelpText = "Folder in which Prowlarr will store the .torrent file")] + public string TorrentFolder { get; set; } + + [DefaultValue(false)] + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] + [FieldDefinition(1, 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; } + + [FieldDefinition(2, Label = "Save Magnet Files", Type = FieldType.Textbox, HelpText = "Extension to use for magnet links, defaults to '.magnet'")] + public string MagnetFileExtension { get; set; } + + public NzbDroneValidationResult Validate() + { + return new NzbDroneValidationResult(Validator.Validate(this)); + } + } +} diff --git a/src/NzbDrone.Core/Download/Clients/Blackhole/UsenetBlackhole.cs b/src/NzbDrone.Core/Download/Clients/Blackhole/UsenetBlackhole.cs new file mode 100644 index 000000000..7f0dd9c0b --- /dev/null +++ b/src/NzbDrone.Core/Download/Clients/Blackhole/UsenetBlackhole.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.IO; +using FluentValidation.Results; +using NLog; +using NzbDrone.Common.Disk; +using NzbDrone.Common.Extensions; +using NzbDrone.Common.Http; +using NzbDrone.Core.Configuration; +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.Download.Clients.Blackhole +{ + public class UsenetBlackhole : UsenetClientBase + { + public UsenetBlackhole(IHttpClient httpClient, + IConfigService configService, + IDiskProvider diskProvider, + Logger logger) + : base(httpClient, configService, diskProvider, logger) + { + } + + protected override string AddFromLink(ReleaseInfo release) + { + throw new NotSupportedException("Blackhole does not support redirected indexers."); + } + + protected override string AddFromNzbFile(ReleaseInfo release, string filename, byte[] fileContent) + { + var title = release.Title; + + title = title.CleanFileName(); + + var filepath = Path.Combine(Settings.NzbFolder, title + ".nzb"); + + using (var stream = _diskProvider.OpenWriteStream(filepath)) + { + stream.Write(fileContent, 0, fileContent.Length); + } + + _logger.Debug("NZB Download succeeded, saved to: {0}", filepath); + + return null; + } + + public override string Name => "Usenet Blackhole"; + + protected override void Test(List failures) + { + failures.AddIfNotNull(TestFolder(Settings.NzbFolder, "NzbFolder")); + } + } +} diff --git a/src/NzbDrone.Core/Download/Clients/Blackhole/UsenetBlackholeSettings.cs b/src/NzbDrone.Core/Download/Clients/Blackhole/UsenetBlackholeSettings.cs new file mode 100644 index 000000000..cb53385e5 --- /dev/null +++ b/src/NzbDrone.Core/Download/Clients/Blackhole/UsenetBlackholeSettings.cs @@ -0,0 +1,29 @@ +using FluentValidation; +using NzbDrone.Core.Annotations; +using NzbDrone.Core.ThingiProvider; +using NzbDrone.Core.Validation; +using NzbDrone.Core.Validation.Paths; + +namespace NzbDrone.Core.Download.Clients.Blackhole +{ + public class UsenetBlackholeSettingsValidator : AbstractValidator + { + public UsenetBlackholeSettingsValidator() + { + RuleFor(c => c.NzbFolder).IsValidPath(); + } + } + + public class UsenetBlackholeSettings : IProviderConfig + { + private static readonly UsenetBlackholeSettingsValidator Validator = new UsenetBlackholeSettingsValidator(); + + [FieldDefinition(0, Label = "Nzb Folder", Type = FieldType.Path, HelpText = "Folder in which Prowlarr will store the .nzb file")] + public string NzbFolder { get; set; } + + public NzbDroneValidationResult Validate() + { + return new NzbDroneValidationResult(Validator.Validate(this)); + } + } +}