diff --git a/src/NzbDrone.Core/IndexerVersions/IndexerDefinitionUpdateService.cs b/src/NzbDrone.Core/IndexerVersions/IndexerDefinitionUpdateService.cs index a84cdec05..2bc83aae7 100644 --- a/src/NzbDrone.Core/IndexerVersions/IndexerDefinitionUpdateService.cs +++ b/src/NzbDrone.Core/IndexerVersions/IndexerDefinitionUpdateService.cs @@ -28,7 +28,7 @@ namespace NzbDrone.Core.IndexerVersions /* Update Service will fall back if version # does not exist for an indexer per Ta */ private const string DEFINITION_BRANCH = "master"; - private const int DEFINITION_VERSION = 4; + private const int DEFINITION_VERSION = 5; //Used when moving yml to C# private readonly List _defintionBlocklist = new List() diff --git a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannBase.cs b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannBase.cs index 0976a9087..3dc12d51a 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannBase.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannBase.cs @@ -48,6 +48,9 @@ namespace NzbDrone.Core.Indexers.Cardigann protected static readonly Regex _LogicFunctionRegex = new Regex( $@"\b({string.Join("|", _SupportedLogicFunctions.Select(Regex.Escape))})(?:\s+(\(?\.[^\)\s]+\)?|""[^""]+"")){{2,}}"); + // Matches CSS selectors for the JSON parser + protected static readonly Regex _jsonSelectorRegex = new Regex(@"\:(?.+?)\((?.+?)\)(?=:|\z)", RegexOptions.Compiled); + public CardigannSettings Settings { get; set; } public CardigannBase(IConfigService configService, @@ -234,13 +237,20 @@ namespace NzbDrone.Core.Indexers.Cardigann if (selector.Selector != null) { - var selector_Selector = ApplyGoTemplateText(selector.Selector.TrimStart('.'), variables); - var selection = parentObj.SelectToken(selector_Selector); + var selectorSelector = ApplyGoTemplateText(selector.Selector.TrimStart('.'), variables); + selectorSelector = JsonParseFieldSelector(parentObj, selectorSelector); + + JToken selection = null; + if (selectorSelector != null) + { + selection = parentObj.SelectToken(selectorSelector); + } + if (selection == null) { if (required) { - throw new Exception(string.Format("Selector \"{0}\" didn't match {1}", selector_Selector, parentObj.ToString())); + throw new Exception(string.Format("Selector \"{0}\" didn't match {1}", selectorSelector, parentObj.ToString())); } return null; @@ -851,5 +861,89 @@ namespace NzbDrone.Core.Indexers.Cardigann return settingsBaseUrl; } + + protected JArray JsonParseRowsSelector(JToken parsedJson, string rowSelector) + { + var selector = rowSelector.Split(':')[0]; + var rowsObj = parsedJson.SelectToken(selector).Value(); + return new JArray(rowsObj.Where(t => + JsonParseFieldSelector(t.Value(), rowSelector.Remove(0, selector.Length)) != null)); + } + + private string JsonParseFieldSelector(JToken parsedJson, string rowSelector) + { + var selector = rowSelector.Split(':')[0]; + JToken parsedObject; + if (string.IsNullOrWhiteSpace(selector)) + { + parsedObject = parsedJson; + } + else if (parsedJson.SelectToken(selector) != null) + { + parsedObject = parsedJson.SelectToken(selector); + } + else + { + return null; + } + + foreach (Match match in _jsonSelectorRegex.Matches(rowSelector)) + { + var filter = match.Result("${filter}"); + var key = match.Result("${key}"); + Match innerMatch; + switch (filter) + { + case "has": + innerMatch = _jsonSelectorRegex.Match(key); + if (innerMatch.Success) + { + if (JsonParseFieldSelector(parsedObject, key) == null) + { + return null; + } + } + else + { + if (parsedObject.SelectToken(key) == null) + { + return null; + } + } + + break; + case "not": + innerMatch = _jsonSelectorRegex.Match(key); + if (innerMatch.Success) + { + if (JsonParseFieldSelector(parsedObject, key) != null) + { + return null; + } + } + else + { + if (parsedObject.SelectToken(key) != null) + { + return null; + } + } + + break; + case "contains": + if (!parsedObject.ToString().Contains(key)) + { + return null; + } + + break; + default: + _logger.Error(string.Format("CardigannIndexer ({0}): Unsupported selector: {1}", _definition.Id, rowSelector)); + continue; + } + } + + return selector; + } } } diff --git a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannDefinition.cs b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannDefinition.cs index 602d0f662..9b00b4043 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannDefinition.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannDefinition.cs @@ -151,6 +151,7 @@ namespace NzbDrone.Core.Indexers.Cardigann public int After { get; set; } public SelectorBlock Dateheaders { get; set; } public SelectorBlock Count { get; set; } + public bool Multiple { get; set; } = false; } public class SearchPathBlock : RequestBlock @@ -200,8 +201,6 @@ namespace NzbDrone.Core.Indexers.Cardigann public class ResponseBlock { public string Type { get; set; } - public string Attribute { get; set; } - public bool Multiple { get; set; } public string NoResultsMessage { get; set; } } } diff --git a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannParser.cs b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannParser.cs index f09dfe81b..1916a01ed 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannParser.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannParser.cs @@ -83,16 +83,16 @@ namespace NzbDrone.Core.Indexers.Cardigann } } - var rowsObj = parsedJson.SelectToken(search.Rows.Selector); - if (rowsObj == null) + var rowsArray = JsonParseRowsSelector(parsedJson, search.Rows.Selector); + if (rowsArray == null) { throw new IndexerException(indexerResponse, "Error Parsing Rows Selector"); } - foreach (var row in rowsObj.Value()) + foreach (var row in rowsArray) { - var selObj = request.SearchPath.Response.Attribute != null ? row.SelectToken(request.SearchPath.Response.Attribute).Value() : row; - var mulRows = request.SearchPath.Response.Multiple == true ? selObj.Values() : new List { selObj.Value() }; + var selObj = search.Rows.Attribute != null ? row.SelectToken(search.Rows.Attribute).Value() : row; + var mulRows = search.Rows.Multiple ? selObj.Values() : new List { selObj.Value() }; foreach (var mulRow in mulRows) { diff --git a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannRequest.cs b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannRequest.cs index 37a5c2149..31ec2f587 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannRequest.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannRequest.cs @@ -8,13 +8,6 @@ namespace NzbDrone.Core.Indexers.Cardigann public Dictionary Variables { get; private set; } public SearchPathBlock SearchPath { get; private set; } - public CardigannRequest(string url, HttpAccept httpAccept, Dictionary variables, SearchPathBlock searchPath) - : base(url, httpAccept) - { - Variables = variables; - SearchPath = searchPath; - } - public CardigannRequest(HttpRequest httpRequest, Dictionary variables, SearchPathBlock searchPath) : base(httpRequest) {