mirror of
https://github.com/lidarr/lidarr.git
synced 2025-08-14 02:37:08 -07:00
New: Newznab/Torznab categories dropdown with indexer provided category names
This commit is contained in:
parent
2d8657a77f
commit
9b1bbaef02
22 changed files with 429 additions and 36 deletions
|
@ -27,7 +27,7 @@ namespace Lidarr.Api.V1
|
|||
Get("schema", x => GetTemplates());
|
||||
Post("test", x => Test(ReadResourceFromRequest(true)));
|
||||
Post("testall", x => TestAll());
|
||||
Post("action/{action}", x => RequestAction(x.action, ReadResourceFromRequest(true)));
|
||||
Post("action/{action}", x => RequestAction(x.action, ReadResourceFromRequest(true, true)));
|
||||
|
||||
GetResourceAll = GetAll;
|
||||
GetResourceById = GetProviderById;
|
||||
|
|
|
@ -14,6 +14,7 @@ namespace Lidarr.Http.ClientSchema
|
|||
public string Type { get; set; }
|
||||
public bool Advanced { get; set; }
|
||||
public List<SelectOption> SelectOptions { get; set; }
|
||||
public string SelectOptionsProviderAction { get; set; }
|
||||
public string Section { get; set; }
|
||||
public string Hidden { get; set; }
|
||||
|
||||
|
|
|
@ -104,7 +104,14 @@ namespace Lidarr.Http.ClientSchema
|
|||
|
||||
if (fieldAttribute.Type == FieldType.Select)
|
||||
{
|
||||
field.SelectOptions = GetSelectOptions(fieldAttribute.SelectOptions);
|
||||
if (fieldAttribute.SelectOptionsProviderAction.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
field.SelectOptionsProviderAction = fieldAttribute.SelectOptionsProviderAction;
|
||||
}
|
||||
else
|
||||
{
|
||||
field.SelectOptions = GetSelectOptions(fieldAttribute.SelectOptions);
|
||||
}
|
||||
}
|
||||
|
||||
if (fieldAttribute.Hidden != HiddenType.Visible)
|
||||
|
|
|
@ -2,6 +2,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FluentValidation;
|
||||
using FluentValidation.Results;
|
||||
using Lidarr.Http.Extensions;
|
||||
using Nancy;
|
||||
using Nancy.Responses.Negotiation;
|
||||
|
@ -224,7 +225,7 @@ namespace Lidarr.Http.REST
|
|||
return Negotiate.WithModel(model).WithStatusCode(statusCode);
|
||||
}
|
||||
|
||||
protected TResource ReadResourceFromRequest(bool skipValidate = false)
|
||||
protected TResource ReadResourceFromRequest(bool skipValidate = false, bool skipSharedValidate = false)
|
||||
{
|
||||
var resource = new TResource();
|
||||
|
||||
|
@ -242,7 +243,12 @@ namespace Lidarr.Http.REST
|
|||
throw new BadRequestException("Request body can't be empty");
|
||||
}
|
||||
|
||||
var errors = SharedValidator.Validate(resource).Errors.ToList();
|
||||
var errors = new List<ValidationFailure>();
|
||||
|
||||
if (!skipSharedValidate)
|
||||
{
|
||||
errors.AddRange(SharedValidator.Validate(resource).Errors);
|
||||
}
|
||||
|
||||
if (Request.Method.Equals("POST", StringComparison.InvariantCultureIgnoreCase) && !skipValidate && !Request.Url.Path.EndsWith("/test", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
|
|
|
@ -19,6 +19,7 @@ namespace NzbDrone.Core.Annotations
|
|||
public FieldType Type { get; set; }
|
||||
public bool Advanced { get; set; }
|
||||
public Type SelectOptions { get; set; }
|
||||
public string SelectOptionsProviderAction { get; set; }
|
||||
public string Section { get; set; }
|
||||
public HiddenType Hidden { get; set; }
|
||||
public PrivacyLevel Privacy { get; set; }
|
||||
|
@ -38,6 +39,15 @@ namespace NzbDrone.Core.Annotations
|
|||
public string Hint { get; set; }
|
||||
}
|
||||
|
||||
public class FieldSelectOption
|
||||
{
|
||||
public int Value { get; set; }
|
||||
public string Name { get; set; }
|
||||
public int Order { get; set; }
|
||||
public string Hint { get; set; }
|
||||
public int? ParentValue { get; set; }
|
||||
}
|
||||
|
||||
public enum FieldType
|
||||
{
|
||||
Textbox,
|
||||
|
|
|
@ -294,7 +294,14 @@ namespace NzbDrone.Core.Indexers
|
|||
{
|
||||
var parser = GetParser();
|
||||
var generator = GetRequestGenerator();
|
||||
var releases = FetchPage(generator.GetRecentRequests().GetAllTiers().First().First(), parser);
|
||||
var firstRequest = generator.GetRecentRequests().GetAllTiers().FirstOrDefault()?.FirstOrDefault();
|
||||
|
||||
if (firstRequest == null)
|
||||
{
|
||||
return new ValidationFailure(string.Empty, "No rss feed query available. This may be an issue with the indexer or your indexer category settings.");
|
||||
}
|
||||
|
||||
var releases = FetchPage(firstRequest, parser);
|
||||
|
||||
if (releases.Empty())
|
||||
{
|
||||
|
|
|
@ -138,5 +138,31 @@ namespace NzbDrone.Core.Indexers.Newznab
|
|||
return new ValidationFailure(string.Empty, "Unable to connect to indexer, check the log for more details");
|
||||
}
|
||||
}
|
||||
|
||||
public override object RequestAction(string action, IDictionary<string, string> query)
|
||||
{
|
||||
if (action == "newznabCategories")
|
||||
{
|
||||
List<NewznabCategory> categories = null;
|
||||
try
|
||||
{
|
||||
if (Settings.BaseUrl.IsNotNullOrWhiteSpace() && Settings.ApiPath.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
categories = _capabilitiesProvider.GetCapabilities(Settings).Categories;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Use default categories
|
||||
}
|
||||
|
||||
return new
|
||||
{
|
||||
options = NewznabCategoryFieldOptionsConverter.GetFieldSelectOptions(categories)
|
||||
};
|
||||
}
|
||||
|
||||
return base.RequestAction(action, query);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ namespace NzbDrone.Core.Indexers.Newznab
|
|||
}
|
||||
|
||||
var request = new HttpRequest(url, HttpAccept.Rss);
|
||||
request.AllowAutoRedirect = true;
|
||||
|
||||
HttpResponse response;
|
||||
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Core.Annotations;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Newznab
|
||||
{
|
||||
public static class NewznabCategoryFieldOptionsConverter
|
||||
{
|
||||
public static List<FieldSelectOption> GetFieldSelectOptions(List<NewznabCategory> categories)
|
||||
{
|
||||
// Ignore categories not relevant for Lidarr
|
||||
var ignoreCategories = new[] { 0, 1000, 2000, 4000, 5000, 6000, 7000 };
|
||||
|
||||
var result = new List<FieldSelectOption>();
|
||||
|
||||
if (categories == null)
|
||||
{
|
||||
// Fetching categories failed, use default Newznab categories
|
||||
categories = new List<NewznabCategory>();
|
||||
categories.Add(new NewznabCategory
|
||||
{
|
||||
Id = 3000,
|
||||
Name = "Music",
|
||||
Subcategories = new List<NewznabCategory>
|
||||
{
|
||||
new NewznabCategory { Id = 3040, Name = "Loseless" },
|
||||
new NewznabCategory { Id = 3010, Name = "MP3" },
|
||||
new NewznabCategory { Id = 3050, Name = "Other" },
|
||||
new NewznabCategory { Id = 3030, Name = "Audiobook" }
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
foreach (var category in categories)
|
||||
{
|
||||
if (ignoreCategories.Contains(category.Id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
result.Add(new FieldSelectOption
|
||||
{
|
||||
Value = category.Id,
|
||||
Name = category.Name,
|
||||
Hint = $"({category.Id})"
|
||||
});
|
||||
|
||||
if (category.Subcategories != null)
|
||||
{
|
||||
foreach (var subcat in category.Subcategories)
|
||||
{
|
||||
result.Add(new FieldSelectOption
|
||||
{
|
||||
Value = subcat.Id,
|
||||
Name = subcat.Name,
|
||||
Hint = $"({subcat.Id})",
|
||||
ParentValue = category.Id
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.Sort((l, r) => l.Value.CompareTo(r.Value));
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -58,7 +58,7 @@ namespace NzbDrone.Core.Indexers.Newznab
|
|||
public NewznabSettings()
|
||||
{
|
||||
ApiPath = "/api";
|
||||
Categories = new[] { 3000, 3010, 3020, 3030, 3040 };
|
||||
Categories = new[] { 3000, 3010, 3030, 3040 };
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "URL")]
|
||||
|
@ -70,7 +70,7 @@ namespace NzbDrone.Core.Indexers.Newznab
|
|||
[FieldDefinition(2, Label = "API Key", Privacy = PrivacyLevel.ApiKey)]
|
||||
public string ApiKey { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Categories", HelpText = "Comma Separated list, leave blank to disable standard/daily shows", Advanced = true)]
|
||||
[FieldDefinition(3, Label = "Categories", Type = FieldType.Select, SelectOptionsProviderAction = "newznabCategories", HelpText = "Comma Separated list")]
|
||||
public IEnumerable<int> Categories { get; set; }
|
||||
|
||||
[FieldDefinition(4, Type = FieldType.Number, Label = "Early Download Limit", HelpText = "Time before release date Lidarr will download from this indexer, empty is no limit", Unit = "days", Advanced = true)]
|
||||
|
|
|
@ -106,5 +106,28 @@ namespace NzbDrone.Core.Indexers.Torznab
|
|||
return new ValidationFailure(string.Empty, "Unable to connect to indexer, check the log for more details");
|
||||
}
|
||||
}
|
||||
|
||||
public override object RequestAction(string action, IDictionary<string, string> query)
|
||||
{
|
||||
if (action == "newznabCategories")
|
||||
{
|
||||
List<NewznabCategory> categories = null;
|
||||
try
|
||||
{
|
||||
categories = _capabilitiesProvider.GetCapabilities(Settings).Categories;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Use default categories
|
||||
}
|
||||
|
||||
return new
|
||||
{
|
||||
options = NewznabCategoryFieldOptionsConverter.GetFieldSelectOptions(categories)
|
||||
};
|
||||
}
|
||||
|
||||
return base.RequestAction(action, query);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue