Skip to content

Commit

Permalink
New: (Cardigann) Allow JSON filters
Browse files Browse the repository at this point in the history
Fixes #844
  • Loading branch information
Qstick committed Feb 28, 2022
1 parent c29fba3 commit 76afb70
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public class IndexerDefinitionUpdateService : IIndexerDefinitionUpdateService, I
/* 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<string> _defintionBlocklist = new List<string>()
Expand Down
100 changes: 97 additions & 3 deletions src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ public class CardigannBase
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(@"\:(?<filter>.+?)\((?<key>.+?)\)(?=:|\z)", RegexOptions.Compiled);

public CardigannSettings Settings { get; set; }

public CardigannBase(IConfigService configService,
Expand Down Expand Up @@ -234,13 +237,20 @@ protected string HandleJsonSelector(SelectorBlock selector, JToken parentObj, Di

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;
Expand Down Expand Up @@ -851,5 +861,89 @@ protected string ResolveSiteLink()

return settingsBaseUrl;
}

protected JArray JsonParseRowsSelector(JToken parsedJson, string rowSelector)
{
var selector = rowSelector.Split(':')[0];
var rowsObj = parsedJson.SelectToken(selector).Value<JArray>();
return new JArray(rowsObj.Where(t =>
JsonParseFieldSelector(t.Value<JObject>(), 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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ public class RowsBlock : SelectorBlock
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
Expand Down Expand Up @@ -200,8 +201,6 @@ public class BeforeBlock : RequestBlock
public class ResponseBlock
{
public string Type { get; set; }
public string Attribute { get; set; }
public bool Multiple { get; set; }
public string NoResultsMessage { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,16 +83,16 @@ public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
}
}

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<JArray>())
foreach (var row in rowsArray)
{
var selObj = request.SearchPath.Response.Attribute != null ? row.SelectToken(request.SearchPath.Response.Attribute).Value<JToken>() : row;
var mulRows = request.SearchPath.Response.Multiple == true ? selObj.Values<JObject>() : new List<JObject> { selObj.Value<JObject>() };
var selObj = search.Rows.Attribute != null ? row.SelectToken(search.Rows.Attribute).Value<JToken>() : row;
var mulRows = search.Rows.Multiple ? selObj.Values<JObject>() : new List<JObject> { selObj.Value<JObject>() };

foreach (var mulRow in mulRows)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,6 @@ public class CardigannRequest : IndexerRequest
public Dictionary<string, object> Variables { get; private set; }
public SearchPathBlock SearchPath { get; private set; }

public CardigannRequest(string url, HttpAccept httpAccept, Dictionary<string, object> variables, SearchPathBlock searchPath)
: base(url, httpAccept)
{
Variables = variables;
SearchPath = searchPath;
}

public CardigannRequest(HttpRequest httpRequest, Dictionary<string, object> variables, SearchPathBlock searchPath)
: base(httpRequest)
{
Expand Down

0 comments on commit 76afb70

Please sign in to comment.