parent
f6e87858a8
commit
0252617730
@ -0,0 +1,226 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using FluentValidation.Results;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Disk;
|
||||||
|
using NzbDrone.Common.Http;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
|
using NzbDrone.Core.Download.Clients.Flood.Models;
|
||||||
|
using NzbDrone.Core.MediaFiles.TorrentInfo;
|
||||||
|
using NzbDrone.Core.Parser.Model;
|
||||||
|
using NzbDrone.Core.RemotePathMappings;
|
||||||
|
using NzbDrone.Core.ThingiProvider;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Download.Clients.Flood
|
||||||
|
{
|
||||||
|
public class Flood : TorrentClientBase<FloodSettings>
|
||||||
|
{
|
||||||
|
private readonly IFloodProxy _proxy;
|
||||||
|
|
||||||
|
public Flood(IFloodProxy proxy,
|
||||||
|
ITorrentFileInfoReader torrentFileInfoReader,
|
||||||
|
IHttpClient httpClient,
|
||||||
|
IConfigService configService,
|
||||||
|
IDiskProvider diskProvider,
|
||||||
|
IRemotePathMappingService remotePathMappingService,
|
||||||
|
Logger logger)
|
||||||
|
: base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger)
|
||||||
|
{
|
||||||
|
_proxy = proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<string> HandleTags(RemoteBook remoteBook, FloodSettings settings)
|
||||||
|
{
|
||||||
|
var result = new HashSet<string>();
|
||||||
|
|
||||||
|
if (settings.Tags.Any())
|
||||||
|
{
|
||||||
|
result.UnionWith(settings.Tags);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.AdditionalTags.Any())
|
||||||
|
{
|
||||||
|
foreach (var additionalTag in settings.AdditionalTags)
|
||||||
|
{
|
||||||
|
switch (additionalTag)
|
||||||
|
{
|
||||||
|
case (int)AdditionalTags.Author:
|
||||||
|
result.Add(remoteBook.Author.Name);
|
||||||
|
break;
|
||||||
|
case (int)AdditionalTags.Format:
|
||||||
|
result.Add(remoteBook.ParsedBookInfo.Quality.Quality.ToString());
|
||||||
|
break;
|
||||||
|
case (int)AdditionalTags.ReleaseGroup:
|
||||||
|
result.Add(remoteBook.ParsedBookInfo.ReleaseGroup);
|
||||||
|
break;
|
||||||
|
case (int)AdditionalTags.Year:
|
||||||
|
result.UnionWith(remoteBook.Books.ConvertAll(book => book.ReleaseDate.Value.Year.ToString()));
|
||||||
|
break;
|
||||||
|
case (int)AdditionalTags.Indexer:
|
||||||
|
result.Add(remoteBook.Release.Indexer);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new DownloadClientException("Unexpected additional tag ID");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Name => "Flood";
|
||||||
|
public override ProviderMessage Message => new ProviderMessage("Readarr is unable to remove torrents that have finished seeding when using Flood", ProviderMessageType.Warning);
|
||||||
|
|
||||||
|
protected override string AddFromTorrentFile(RemoteBook remoteBook, string hash, string filename, byte[] fileContent)
|
||||||
|
{
|
||||||
|
_proxy.AddTorrentByFile(Convert.ToBase64String(fileContent), HandleTags(remoteBook, Settings), Settings);
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override string AddFromMagnetLink(RemoteBook remoteBook, string hash, string magnetLink)
|
||||||
|
{
|
||||||
|
_proxy.AddTorrentByUrl(magnetLink, HandleTags(remoteBook, Settings), Settings);
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IEnumerable<DownloadClientItem> GetItems()
|
||||||
|
{
|
||||||
|
var items = new List<DownloadClientItem>();
|
||||||
|
|
||||||
|
var list = _proxy.GetTorrents(Settings);
|
||||||
|
|
||||||
|
foreach (var torrent in list)
|
||||||
|
{
|
||||||
|
var properties = torrent.Value;
|
||||||
|
|
||||||
|
if (!Settings.Tags.All(tag => properties.Tags.Contains(tag)))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var item = new DownloadClientItem
|
||||||
|
{
|
||||||
|
DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this),
|
||||||
|
DownloadId = torrent.Key,
|
||||||
|
Title = properties.Name,
|
||||||
|
OutputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(properties.Directory)),
|
||||||
|
Category = properties.Tags.Count > 0 ? properties.Tags[0] : null,
|
||||||
|
RemainingSize = properties.SizeBytes - properties.BytesDone,
|
||||||
|
TotalSize = properties.SizeBytes,
|
||||||
|
SeedRatio = properties.Ratio,
|
||||||
|
Message = properties.Message,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (properties.Eta > 0)
|
||||||
|
{
|
||||||
|
item.RemainingTime = TimeSpan.FromSeconds(properties.Eta);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (properties.Status.Contains("error"))
|
||||||
|
{
|
||||||
|
item.Status = DownloadItemStatus.Warning;
|
||||||
|
}
|
||||||
|
else if (properties.Status.Contains("seeding") || properties.Status.Contains("complete"))
|
||||||
|
{
|
||||||
|
item.Status = DownloadItemStatus.Completed;
|
||||||
|
}
|
||||||
|
else if (properties.Status.Contains("downloading"))
|
||||||
|
{
|
||||||
|
item.Status = DownloadItemStatus.Downloading;
|
||||||
|
}
|
||||||
|
else if (properties.Status.Contains("stopped"))
|
||||||
|
{
|
||||||
|
item.Status = DownloadItemStatus.Paused;
|
||||||
|
}
|
||||||
|
|
||||||
|
item.CanMoveFiles = item.CanBeRemoved = false;
|
||||||
|
|
||||||
|
items.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override DownloadClientItem GetImportItem(DownloadClientItem item, DownloadClientItem previousImportAttempt)
|
||||||
|
{
|
||||||
|
var result = item.Clone();
|
||||||
|
|
||||||
|
var contentPaths = _proxy.GetTorrentContentPaths(item.DownloadId, Settings);
|
||||||
|
|
||||||
|
if (contentPaths.Count < 1)
|
||||||
|
{
|
||||||
|
throw new DownloadClientUnavailableException($"Failed to fetch list of contents of torrent: {item.DownloadId}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contentPaths.Count == 1)
|
||||||
|
{
|
||||||
|
// For single-file torrent, OutputPath should be the path of file.
|
||||||
|
result.OutputPath = item.OutputPath + new OsPath(contentPaths[0]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// For multi-file torrent, OutputPath should be the path of base directory of torrent.
|
||||||
|
var baseDirectoryPaths = contentPaths.ConvertAll(path =>
|
||||||
|
path.Split(new char[] { '\\', '/' }, StringSplitOptions.RemoveEmptyEntries)[0]);
|
||||||
|
|
||||||
|
// Check first segment (directory) of paths of contents. If all contents share the same directory, use that directory.
|
||||||
|
if (baseDirectoryPaths.TrueForAll(path => path == baseDirectoryPaths[0]))
|
||||||
|
{
|
||||||
|
result.OutputPath = item.OutputPath + new OsPath(baseDirectoryPaths[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, OutputPath is already the base directory.
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void MarkItemAsImported(DownloadClientItem downloadClientItem)
|
||||||
|
{
|
||||||
|
if (Settings.PostImportTags.Any())
|
||||||
|
{
|
||||||
|
var list = _proxy.GetTorrents(Settings);
|
||||||
|
|
||||||
|
if (list.ContainsKey(downloadClientItem.DownloadId))
|
||||||
|
{
|
||||||
|
_proxy.SetTorrentsTags(downloadClientItem.DownloadId,
|
||||||
|
list[downloadClientItem.DownloadId].Tags.Concat(Settings.PostImportTags).ToHashSet(),
|
||||||
|
Settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void RemoveItem(string downloadId, bool deleteData)
|
||||||
|
{
|
||||||
|
_proxy.DeleteTorrent(downloadId, deleteData, Settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override DownloadClientInfo GetStatus()
|
||||||
|
{
|
||||||
|
return new DownloadClientInfo
|
||||||
|
{
|
||||||
|
IsLocalhost = Settings.Host == "127.0.0.1" || Settings.Host == "::1" || Settings.Host == "localhost",
|
||||||
|
OutputRootFolders = new List<OsPath> { _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(Settings.Destination)) }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Test(List<ValidationFailure> failures)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_proxy.AuthVerify(Settings);
|
||||||
|
}
|
||||||
|
catch (DownloadClientAuthenticationException ex)
|
||||||
|
{
|
||||||
|
failures.Add(new ValidationFailure("Password", ex.Message));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
failures.Add(new ValidationFailure("Host", ex.Message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,213 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Cache;
|
||||||
|
using NzbDrone.Common.Http;
|
||||||
|
using NzbDrone.Common.Serializer;
|
||||||
|
using NzbDrone.Core.Download.Clients.Flood.Types;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Download.Clients.Flood
|
||||||
|
{
|
||||||
|
public interface IFloodProxy
|
||||||
|
{
|
||||||
|
void AuthVerify(FloodSettings settings);
|
||||||
|
void AddTorrentByUrl(string url, IEnumerable<string> tags, FloodSettings settings);
|
||||||
|
void AddTorrentByFile(string file, IEnumerable<string> tags, FloodSettings settings);
|
||||||
|
void DeleteTorrent(string hash, bool deleteData, FloodSettings settings);
|
||||||
|
Dictionary<string, Torrent> GetTorrents(FloodSettings settings);
|
||||||
|
List<string> GetTorrentContentPaths(string hash, FloodSettings settings);
|
||||||
|
void SetTorrentsTags(string hash, IEnumerable<string> tags, FloodSettings settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FloodProxy : IFloodProxy
|
||||||
|
{
|
||||||
|
private readonly IHttpClient _httpClient;
|
||||||
|
private readonly Logger _logger;
|
||||||
|
private readonly ICached<Dictionary<string, string>> _authCookieCache;
|
||||||
|
|
||||||
|
public FloodProxy(IHttpClient httpClient, ICacheManager cacheManager, Logger logger)
|
||||||
|
{
|
||||||
|
_httpClient = httpClient;
|
||||||
|
_logger = logger;
|
||||||
|
_authCookieCache = cacheManager.GetCache<Dictionary<string, string>>(GetType(), "authCookies");
|
||||||
|
}
|
||||||
|
|
||||||
|
private string BuildUrl(FloodSettings settings)
|
||||||
|
{
|
||||||
|
return $"{(settings.UseSsl ? "https://" : "http://")}{settings.Host}:{settings.Port}/{settings.UrlBase}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string BuildCachedCookieKey(FloodSettings settings)
|
||||||
|
{
|
||||||
|
return $"{BuildUrl(settings)}:{settings.Username}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpRequestBuilder BuildRequest(FloodSettings settings)
|
||||||
|
{
|
||||||
|
var requestBuilder = new HttpRequestBuilder(HttpUri.CombinePath(BuildUrl(settings), "/api"))
|
||||||
|
{
|
||||||
|
LogResponseContent = true,
|
||||||
|
NetworkCredential = new NetworkCredential(settings.Username, settings.Password)
|
||||||
|
};
|
||||||
|
|
||||||
|
requestBuilder.Headers.ContentType = "application/json";
|
||||||
|
requestBuilder.SetCookies(AuthAuthenticate(requestBuilder, settings));
|
||||||
|
|
||||||
|
return requestBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpResponse HandleRequest(HttpRequest request, FloodSettings settings)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return _httpClient.Execute(request);
|
||||||
|
}
|
||||||
|
catch (HttpException ex)
|
||||||
|
{
|
||||||
|
if (ex.Response.StatusCode == HttpStatusCode.Forbidden ||
|
||||||
|
ex.Response.StatusCode == HttpStatusCode.Unauthorized)
|
||||||
|
{
|
||||||
|
_authCookieCache.Remove(BuildCachedCookieKey(settings));
|
||||||
|
throw new DownloadClientAuthenticationException("Failed to authenticate with Flood.");
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new DownloadClientException("Unable to connect to Flood, please check your settings");
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
throw new DownloadClientException("Unable to connect to Flood, please check your settings");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Dictionary<string, string> AuthAuthenticate(HttpRequestBuilder requestBuilder, FloodSettings settings, bool force = false)
|
||||||
|
{
|
||||||
|
var cachedCookies = _authCookieCache.Find(BuildCachedCookieKey(settings));
|
||||||
|
|
||||||
|
if (cachedCookies == null || force)
|
||||||
|
{
|
||||||
|
var authenticateRequest = requestBuilder.Resource("/auth/authenticate").Post().Build();
|
||||||
|
|
||||||
|
var body = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "username", settings.Username },
|
||||||
|
{ "password", settings.Password }
|
||||||
|
};
|
||||||
|
authenticateRequest.SetContent(body.ToJson());
|
||||||
|
|
||||||
|
var response = HandleRequest(authenticateRequest, settings);
|
||||||
|
cachedCookies = response.GetCookies();
|
||||||
|
_authCookieCache.Set(BuildCachedCookieKey(settings), cachedCookies);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cachedCookies;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AuthVerify(FloodSettings settings)
|
||||||
|
{
|
||||||
|
var verifyRequest = BuildRequest(settings).Resource("/auth/verify").Build();
|
||||||
|
|
||||||
|
verifyRequest.Method = HttpMethod.GET;
|
||||||
|
|
||||||
|
HandleRequest(verifyRequest, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddTorrentByFile(string file, IEnumerable<string> tags, FloodSettings settings)
|
||||||
|
{
|
||||||
|
var addRequest = BuildRequest(settings).Resource("/torrents/add-files").Post().Build();
|
||||||
|
|
||||||
|
var body = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "files", new List<string> { file } },
|
||||||
|
{ "tags", tags.ToList() }
|
||||||
|
};
|
||||||
|
|
||||||
|
if (settings.Destination != null)
|
||||||
|
{
|
||||||
|
body.Add("destination", settings.Destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!settings.AddPaused)
|
||||||
|
{
|
||||||
|
body.Add("start", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
addRequest.SetContent(body.ToJson());
|
||||||
|
|
||||||
|
HandleRequest(addRequest, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddTorrentByUrl(string url, IEnumerable<string> tags, FloodSettings settings)
|
||||||
|
{
|
||||||
|
var addRequest = BuildRequest(settings).Resource("/torrents/add-urls").Post().Build();
|
||||||
|
|
||||||
|
var body = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "urls", new List<string> { url } },
|
||||||
|
{ "tags", tags.ToList() }
|
||||||
|
};
|
||||||
|
|
||||||
|
if (settings.Destination != null)
|
||||||
|
{
|
||||||
|
body.Add("destination", settings.Destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!settings.AddPaused)
|
||||||
|
{
|
||||||
|
body.Add("start", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
addRequest.SetContent(body.ToJson());
|
||||||
|
|
||||||
|
HandleRequest(addRequest, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteTorrent(string hash, bool deleteData, FloodSettings settings)
|
||||||
|
{
|
||||||
|
var deleteRequest = BuildRequest(settings).Resource("/torrents/delete").Post().Build();
|
||||||
|
|
||||||
|
var body = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "hashes", new List<string> { hash } },
|
||||||
|
{ "deleteData", deleteData }
|
||||||
|
};
|
||||||
|
deleteRequest.SetContent(body.ToJson());
|
||||||
|
|
||||||
|
HandleRequest(deleteRequest, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dictionary<string, Torrent> GetTorrents(FloodSettings settings)
|
||||||
|
{
|
||||||
|
var getTorrentsRequest = BuildRequest(settings).Resource("/torrents").Build();
|
||||||
|
|
||||||
|
getTorrentsRequest.Method = HttpMethod.GET;
|
||||||
|
|
||||||
|
return Json.Deserialize<TorrentListSummary>(HandleRequest(getTorrentsRequest, settings).Content).Torrents;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<string> GetTorrentContentPaths(string hash, FloodSettings settings)
|
||||||
|
{
|
||||||
|
var contentsRequest = BuildRequest(settings).Resource($"/torrents/{hash}/contents").Build();
|
||||||
|
|
||||||
|
contentsRequest.Method = HttpMethod.GET;
|
||||||
|
|
||||||
|
return Json.Deserialize<List<TorrentContent>>(HandleRequest(contentsRequest, settings).Content).ConvertAll(content => content.Path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetTorrentsTags(string hash, IEnumerable<string> tags, FloodSettings settings)
|
||||||
|
{
|
||||||
|
var tagsRequest = BuildRequest(settings).Resource("/torrents/tags").Build();
|
||||||
|
|
||||||
|
tagsRequest.Method = HttpMethod.PATCH;
|
||||||
|
|
||||||
|
var body = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "hashes", new List<string> { hash } },
|
||||||
|
{ "tags", tags.ToList() }
|
||||||
|
};
|
||||||
|
tagsRequest.SetContent(body.ToJson());
|
||||||
|
|
||||||
|
HandleRequest(tagsRequest, settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using FluentValidation;
|
||||||
|
using NzbDrone.Core.Annotations;
|
||||||
|
using NzbDrone.Core.Download.Clients.Flood.Models;
|
||||||
|
using NzbDrone.Core.ThingiProvider;
|
||||||
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Download.Clients.Flood
|
||||||
|
{
|
||||||
|
public class FloodSettingsValidator : AbstractValidator<FloodSettings>
|
||||||
|
{
|
||||||
|
public FloodSettingsValidator()
|
||||||
|
{
|
||||||
|
RuleFor(c => c.Host).ValidHost();
|
||||||
|
RuleFor(c => c.Port).InclusiveBetween(1, 65535);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FloodSettings : IProviderConfig
|
||||||
|
{
|
||||||
|
private static readonly FloodSettingsValidator Validator = new FloodSettingsValidator();
|
||||||
|
|
||||||
|
public FloodSettings()
|
||||||
|
{
|
||||||
|
UseSsl = false;
|
||||||
|
Host = "localhost";
|
||||||
|
Port = 3000;
|
||||||
|
Tags = new string[]
|
||||||
|
{
|
||||||
|
"readarr"
|
||||||
|
};
|
||||||
|
AdditionalTags = Enumerable.Empty<int>();
|
||||||
|
AddPaused = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
[FieldDefinition(0, Label = "Use SSL", Type = FieldType.Checkbox)]
|
||||||
|
public bool UseSsl { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(1, Label = "Host", Type = FieldType.Textbox)]
|
||||||
|
public string Host { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(2, Label = "Port", Type = FieldType.Textbox)]
|
||||||
|
public int Port { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(3, Label = "Url Base", Type = FieldType.Textbox, HelpText = "Optionally adds a prefix to Flood API, such as [protocol]://[host]:[port]/[urlBase]api")]
|
||||||
|
public string UrlBase { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(4, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)]
|
||||||
|
public string Username { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(5, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
|
||||||
|
public string Password { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(6, Label = "Destination", Type = FieldType.Textbox, HelpText = "Manually specifies download destination")]
|
||||||
|
public string Destination { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(7, Label = "Tags", Type = FieldType.Tag, HelpText = "Initial tags of a download. To be recognized, a download must have all initial tags. This avoids conflicts with unrelated downloads.")]
|
||||||
|
public IEnumerable<string> Tags { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(8, Label = "Post-Import Tags", Type = FieldType.Tag, HelpText = "Appends tags after a download is imported.", Advanced = true)]
|
||||||
|
public IEnumerable<string> PostImportTags { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(9, Label = "Additional Tags", Type = FieldType.Select, SelectOptions = typeof(AdditionalTags), HelpText = "Adds properties of media as tags. Hints are examples.", Advanced = true)]
|
||||||
|
public IEnumerable<int> AdditionalTags { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(10, Label = "Add Paused", Type = FieldType.Checkbox)]
|
||||||
|
public bool AddPaused { get; set; }
|
||||||
|
|
||||||
|
public NzbDroneValidationResult Validate()
|
||||||
|
{
|
||||||
|
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
using NzbDrone.Core.Annotations;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Download.Clients.Flood.Models
|
||||||
|
{
|
||||||
|
public enum AdditionalTags
|
||||||
|
{
|
||||||
|
[FieldOption(Hint = "J.R.R. Tolkien")]
|
||||||
|
Author = 0,
|
||||||
|
|
||||||
|
[FieldOption(Hint = "EPUB")]
|
||||||
|
Format = 1,
|
||||||
|
|
||||||
|
[FieldOption(Hint = "Example-Raws")]
|
||||||
|
ReleaseGroup = 2,
|
||||||
|
|
||||||
|
[FieldOption(Hint = "1954")]
|
||||||
|
Year = 3,
|
||||||
|
|
||||||
|
[FieldOption(Hint = "Torznab")]
|
||||||
|
Indexer = 4,
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Download.Clients.Flood.Types
|
||||||
|
{
|
||||||
|
public sealed class Torrent
|
||||||
|
{
|
||||||
|
[JsonProperty(PropertyName = "bytesDone")]
|
||||||
|
public long BytesDone { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "directory")]
|
||||||
|
public string Directory { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "eta")]
|
||||||
|
public long Eta { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "message")]
|
||||||
|
public string Message { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "name")]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "ratio")]
|
||||||
|
public float Ratio { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "sizeBytes")]
|
||||||
|
public long SizeBytes { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "status")]
|
||||||
|
public List<string> Status { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "tags")]
|
||||||
|
public List<string> Tags { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Download.Clients.Flood.Types
|
||||||
|
{
|
||||||
|
public sealed class TorrentContent
|
||||||
|
{
|
||||||
|
[JsonProperty(PropertyName = "path")]
|
||||||
|
public string Path { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Download.Clients.Flood.Types
|
||||||
|
{
|
||||||
|
public sealed class TorrentListSummary
|
||||||
|
{
|
||||||
|
[JsonProperty(PropertyName = "id")]
|
||||||
|
public long Id { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "torrents")]
|
||||||
|
public Dictionary<string, Torrent> Torrents { get; set; }
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue