mirror of https://github.com/Jackett/Jackett
Cardigann: add support for multiple paths
This commit is contained in:
parent
95dad9d67e
commit
4cee32f75c
|
@ -127,6 +127,7 @@ namespace Jackett.Indexers
|
||||||
public class searchBlock
|
public class searchBlock
|
||||||
{
|
{
|
||||||
public string Path { get; set; }
|
public string Path { get; set; }
|
||||||
|
public List<searchPathBlock> Paths { get; set; }
|
||||||
public List<filterBlock> Keywordsfilters { get; set; }
|
public List<filterBlock> Keywordsfilters { get; set; }
|
||||||
public Dictionary<string, string> Inputs { get; set; }
|
public Dictionary<string, string> Inputs { get; set; }
|
||||||
public rowsBlock Rows { get; set; }
|
public rowsBlock Rows { get; set; }
|
||||||
|
@ -140,6 +141,12 @@ namespace Jackett.Indexers
|
||||||
public selectorBlock Dateheaders { get; set; }
|
public selectorBlock Dateheaders { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class searchPathBlock : requestBlock
|
||||||
|
{
|
||||||
|
public List<string> Categories { get; set; }
|
||||||
|
public bool Inheritinputs { get; set; } = true;
|
||||||
|
}
|
||||||
|
|
||||||
public class requestBlock
|
public class requestBlock
|
||||||
{
|
{
|
||||||
public string Path { get; set; }
|
public string Path { get; set; }
|
||||||
|
@ -196,6 +203,20 @@ namespace Jackett.Indexers
|
||||||
if (Definition.Login != null && Definition.Login.Method == null)
|
if (Definition.Login != null && Definition.Login.Method == null)
|
||||||
Definition.Login.Method = "form";
|
Definition.Login.Method = "form";
|
||||||
|
|
||||||
|
if (Definition.Search.Paths == null)
|
||||||
|
{
|
||||||
|
Definition.Search.Paths = new List<searchPathBlock>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert definitions with a single search Path to a Paths entry
|
||||||
|
if (Definition.Search.Path != null)
|
||||||
|
{
|
||||||
|
var legacySearchPath = new searchPathBlock();
|
||||||
|
legacySearchPath.Path = Definition.Search.Path;
|
||||||
|
legacySearchPath.Inputs = Definition.Search.Inputs;
|
||||||
|
Definition.Search.Paths.Add(legacySearchPath);
|
||||||
|
}
|
||||||
|
|
||||||
// init missing mandatory attributes
|
// init missing mandatory attributes
|
||||||
DisplayName = Definition.Name;
|
DisplayName = Definition.Name;
|
||||||
DisplayDescription = Definition.Description;
|
DisplayDescription = Definition.Description;
|
||||||
|
@ -1022,7 +1043,9 @@ namespace Jackett.Indexers
|
||||||
variables[".Query.TraktID"] = null;
|
variables[".Query.TraktID"] = null;
|
||||||
|
|
||||||
variables[".Query.Episode"] = query.GetEpisodeSearchString();
|
variables[".Query.Episode"] = query.GetEpisodeSearchString();
|
||||||
variables[".Categories"] = MapTorznabCapsToTrackers(query);
|
|
||||||
|
var mappedCategories = MapTorznabCapsToTrackers(query);
|
||||||
|
variables[".Categories"] = mappedCategories;
|
||||||
|
|
||||||
var KeywordTokens = new List<string>();
|
var KeywordTokens = new List<string>();
|
||||||
var KeywordTokenKeys = new List<string> { "Q", "Series", "Movie", "Year" };
|
var KeywordTokenKeys = new List<string> { "Q", "Series", "Movie", "Year" };
|
||||||
|
@ -1038,321 +1061,345 @@ namespace Jackett.Indexers
|
||||||
variables[".Query.Keywords"] = string.Join(" ", KeywordTokens);
|
variables[".Query.Keywords"] = string.Join(" ", KeywordTokens);
|
||||||
variables[".Keywords"] = applyFilters((string)variables[".Query.Keywords"], Search.Keywordsfilters);
|
variables[".Keywords"] = applyFilters((string)variables[".Query.Keywords"], Search.Keywordsfilters);
|
||||||
|
|
||||||
// build search URL
|
// TODO: prepare queries first and then send them parallel
|
||||||
// HttpUtility.UrlPathEncode seems to only encode spaces, we use UrlEncode and replace + with %20 as a workaround
|
var SearchPaths = Search.Paths;
|
||||||
var searchUrl = resolvePath(applyGoTemplateText(Search.Path, variables, HttpUtility.UrlEncode).Replace("+", "%20") + "?").AbsoluteUri;
|
foreach (var SearchPath in SearchPaths)
|
||||||
var queryCollection = new NameValueCollection();
|
{
|
||||||
if (Search.Inputs != null)
|
// skip path if categories don't match
|
||||||
{
|
if (SearchPath.Categories != null && mappedCategories.Count > 0)
|
||||||
foreach (var Input in Search.Inputs)
|
|
||||||
{
|
{
|
||||||
if (Input.Key == "$raw")
|
var invertMatch = (SearchPath.Categories[0] == "!");
|
||||||
searchUrl += applyGoTemplateText(Input.Value, variables, HttpUtility.UrlEncode);
|
var hasIntersect = mappedCategories.Intersect(SearchPath.Categories).Any();
|
||||||
else
|
if (invertMatch)
|
||||||
queryCollection.Add(Input.Key, applyGoTemplateText(Input.Value, variables));
|
hasIntersect = !hasIntersect;
|
||||||
}
|
if (!hasIntersect)
|
||||||
}
|
continue;
|
||||||
if (queryCollection.Count > 0)
|
|
||||||
searchUrl += "&" + queryCollection.GetQueryString(Encoding);
|
|
||||||
|
|
||||||
// in case no args are added remove ? again (needed for KAT)
|
|
||||||
searchUrl = searchUrl.TrimEnd('?');
|
|
||||||
|
|
||||||
// send HTTP request
|
|
||||||
var response = await RequestStringWithCookies(searchUrl);
|
|
||||||
var results = response.Content;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var SearchResultParser = new HtmlParser();
|
|
||||||
var SearchResultDocument = SearchResultParser.Parse(results);
|
|
||||||
|
|
||||||
// check if we need to login again
|
|
||||||
var loginNeeded = CheckIfLoginIsNeeded(response, SearchResultDocument);
|
|
||||||
if (loginNeeded)
|
|
||||||
{
|
|
||||||
logger.Info(string.Format("CardigannIndexer ({0}): Relogin required", ID));
|
|
||||||
var LoginResult = await DoLogin();
|
|
||||||
if (!LoginResult)
|
|
||||||
throw new Exception(string.Format("Relogin failed"));
|
|
||||||
await TestLogin();
|
|
||||||
response = await RequestStringWithCookies(searchUrl);
|
|
||||||
results = results = response.Content;
|
|
||||||
SearchResultDocument = SearchResultParser.Parse(results);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var RowsDom = SearchResultDocument.QuerySelectorAll(Search.Rows.Selector);
|
// build search URL
|
||||||
List<IElement> Rows = new List<IElement>();
|
// HttpUtility.UrlPathEncode seems to only encode spaces, we use UrlEncode and replace + with %20 as a workaround
|
||||||
foreach (var RowDom in RowsDom)
|
var searchUrl = resolvePath(applyGoTemplateText(SearchPath.Path, variables, HttpUtility.UrlEncode).Replace("+", "%20") + "?").AbsoluteUri;
|
||||||
{
|
var queryCollection = new NameValueCollection();
|
||||||
Rows.Add(RowDom);
|
|
||||||
}
|
|
||||||
|
|
||||||
// merge following rows for After selector
|
var InputsList = new List<Dictionary<string, string>>();
|
||||||
var After = Definition.Search.Rows.After;
|
if (SearchPath.Inheritinputs)
|
||||||
if (After > 0)
|
InputsList.Add(Search.Inputs);
|
||||||
{
|
InputsList.Add(SearchPath.Inputs);
|
||||||
for (int i = 0; i < Rows.Count; i += 1)
|
|
||||||
{
|
foreach (var Inputs in InputsList)
|
||||||
var CurrentRow = Rows[i];
|
{
|
||||||
for (int j = 0; j < After; j += 1)
|
if (Inputs != null)
|
||||||
|
{
|
||||||
|
foreach (var Input in Inputs)
|
||||||
{
|
{
|
||||||
var MergeRowIndex = i + j + 1;
|
if (Input.Key == "$raw")
|
||||||
var MergeRow = Rows[MergeRowIndex];
|
searchUrl += applyGoTemplateText(Input.Value, variables, HttpUtility.UrlEncode);
|
||||||
List<INode> MergeNodes = new List<INode>();
|
else
|
||||||
foreach (var node in MergeRow.ChildNodes)
|
queryCollection.Add(Input.Key, applyGoTemplateText(Input.Value, variables));
|
||||||
{
|
|
||||||
MergeNodes.Add(node);
|
|
||||||
}
|
|
||||||
CurrentRow.Append(MergeNodes.ToArray());
|
|
||||||
}
|
}
|
||||||
Rows.RemoveRange(i + 1, After);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (queryCollection.Count > 0)
|
||||||
|
searchUrl += "&" + queryCollection.GetQueryString(Encoding);
|
||||||
|
|
||||||
foreach (var Row in Rows)
|
// in case no args are added remove ? again (needed for KAT)
|
||||||
{
|
searchUrl = searchUrl.TrimEnd('?');
|
||||||
try
|
|
||||||
{
|
|
||||||
var release = new ReleaseInfo();
|
|
||||||
release.MinimumRatio = 1;
|
|
||||||
release.MinimumSeedTime = 48 * 60 * 60;
|
|
||||||
|
|
||||||
// Parse fields
|
// send HTTP request
|
||||||
foreach (var Field in Search.Fields)
|
var response = await RequestStringWithCookies(searchUrl);
|
||||||
{
|
var results = response.Content;
|
||||||
var FieldParts = Field.Key.Split('|');
|
try
|
||||||
var FieldName = FieldParts[0];
|
{
|
||||||
var FieldModifiers = new List<string>();
|
var SearchResultParser = new HtmlParser();
|
||||||
for (var i = 1; i < FieldParts.Length; i++)
|
var SearchResultDocument = SearchResultParser.Parse(results);
|
||||||
FieldModifiers.Add(FieldParts[i]);
|
|
||||||
|
|
||||||
string value = null;
|
// check if we need to login again
|
||||||
var variablesKey = ".Result." + FieldName;
|
var loginNeeded = CheckIfLoginIsNeeded(response, SearchResultDocument);
|
||||||
try
|
if (loginNeeded)
|
||||||
|
{
|
||||||
|
logger.Info(string.Format("CardigannIndexer ({0}): Relogin required", ID));
|
||||||
|
var LoginResult = await DoLogin();
|
||||||
|
if (!LoginResult)
|
||||||
|
throw new Exception(string.Format("Relogin failed"));
|
||||||
|
await TestLogin();
|
||||||
|
response = await RequestStringWithCookies(searchUrl);
|
||||||
|
results = results = response.Content;
|
||||||
|
SearchResultDocument = SearchResultParser.Parse(results);
|
||||||
|
}
|
||||||
|
|
||||||
|
var RowsDom = SearchResultDocument.QuerySelectorAll(Search.Rows.Selector);
|
||||||
|
List<IElement> Rows = new List<IElement>();
|
||||||
|
foreach (var RowDom in RowsDom)
|
||||||
|
{
|
||||||
|
Rows.Add(RowDom);
|
||||||
|
}
|
||||||
|
|
||||||
|
// merge following rows for After selector
|
||||||
|
var After = Definition.Search.Rows.After;
|
||||||
|
if (After > 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Rows.Count; i += 1)
|
||||||
|
{
|
||||||
|
var CurrentRow = Rows[i];
|
||||||
|
for (int j = 0; j < After; j += 1)
|
||||||
{
|
{
|
||||||
value = handleSelector(Field.Value, Row, variables);
|
var MergeRowIndex = i + j + 1;
|
||||||
switch (FieldName)
|
var MergeRow = Rows[MergeRowIndex];
|
||||||
|
List<INode> MergeNodes = new List<INode>();
|
||||||
|
foreach (var node in MergeRow.ChildNodes)
|
||||||
{
|
{
|
||||||
case "download":
|
MergeNodes.Add(node);
|
||||||
if (string.IsNullOrEmpty(value))
|
|
||||||
{
|
|
||||||
value = null;
|
|
||||||
release.Link = null;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (value.StartsWith("magnet:"))
|
|
||||||
{
|
|
||||||
release.MagnetUri = new Uri(value);
|
|
||||||
//release.Link = release.MagnetUri;
|
|
||||||
value = release.MagnetUri.ToString();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
release.Link = resolvePath(value);
|
|
||||||
value = release.Link.ToString();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "magnet":
|
|
||||||
release.MagnetUri = new Uri(value);
|
|
||||||
value = release.MagnetUri.ToString();
|
|
||||||
break;
|
|
||||||
case "details":
|
|
||||||
var url = resolvePath(value);
|
|
||||||
release.Guid = url;
|
|
||||||
release.Comments = url;
|
|
||||||
if (release.Guid == null)
|
|
||||||
release.Guid = url;
|
|
||||||
value = url.ToString();
|
|
||||||
break;
|
|
||||||
case "comments":
|
|
||||||
var CommentsUrl = resolvePath(value);
|
|
||||||
if (release.Comments == null)
|
|
||||||
release.Comments = CommentsUrl;
|
|
||||||
if (release.Guid == null)
|
|
||||||
release.Guid = CommentsUrl;
|
|
||||||
value = CommentsUrl.ToString();
|
|
||||||
break;
|
|
||||||
case "title":
|
|
||||||
if (FieldModifiers.Contains("append"))
|
|
||||||
release.Title += value;
|
|
||||||
else
|
|
||||||
release.Title = value;
|
|
||||||
value = release.Title;
|
|
||||||
break;
|
|
||||||
case "description":
|
|
||||||
if (FieldModifiers.Contains("append"))
|
|
||||||
release.Description += value;
|
|
||||||
else
|
|
||||||
release.Description = value;
|
|
||||||
value = release.Description;
|
|
||||||
break;
|
|
||||||
case "category":
|
|
||||||
release.Category = MapTrackerCatToNewznab(value);
|
|
||||||
value = release.Category.ToString();
|
|
||||||
break;
|
|
||||||
case "size":
|
|
||||||
release.Size = ReleaseInfo.GetBytes(value);
|
|
||||||
value = release.Size.ToString();
|
|
||||||
break;
|
|
||||||
case "leechers":
|
|
||||||
var Leechers = ParseUtil.CoerceInt(value);
|
|
||||||
if (release.Peers == null)
|
|
||||||
release.Peers = Leechers;
|
|
||||||
else
|
|
||||||
release.Peers += Leechers;
|
|
||||||
value = Leechers.ToString();
|
|
||||||
break;
|
|
||||||
case "seeders":
|
|
||||||
release.Seeders = ParseUtil.CoerceInt(value);
|
|
||||||
if (release.Peers == null)
|
|
||||||
release.Peers = release.Seeders;
|
|
||||||
else
|
|
||||||
release.Peers += release.Seeders;
|
|
||||||
value = release.Seeders.ToString();
|
|
||||||
break;
|
|
||||||
case "date":
|
|
||||||
release.PublishDate = DateTimeUtil.FromUnknown(value);
|
|
||||||
value = release.PublishDate.ToString(DateTimeUtil.RFC1123ZPattern);
|
|
||||||
break;
|
|
||||||
case "files":
|
|
||||||
release.Files = ParseUtil.CoerceLong(value);
|
|
||||||
value = release.Files.ToString();
|
|
||||||
break;
|
|
||||||
case "grabs":
|
|
||||||
release.Grabs = ParseUtil.CoerceLong(value);
|
|
||||||
value = release.Grabs.ToString();
|
|
||||||
break;
|
|
||||||
case "downloadvolumefactor":
|
|
||||||
release.DownloadVolumeFactor = ParseUtil.CoerceDouble(value);
|
|
||||||
value = release.DownloadVolumeFactor.ToString();
|
|
||||||
break;
|
|
||||||
case "uploadvolumefactor":
|
|
||||||
release.UploadVolumeFactor = ParseUtil.CoerceDouble(value);
|
|
||||||
value = release.UploadVolumeFactor.ToString();
|
|
||||||
break;
|
|
||||||
case "minimumratio":
|
|
||||||
release.MinimumRatio = ParseUtil.CoerceDouble(value);
|
|
||||||
value = release.MinimumRatio.ToString();
|
|
||||||
break;
|
|
||||||
case "minimumseedtime":
|
|
||||||
release.MinimumSeedTime = ParseUtil.CoerceLong(value);
|
|
||||||
value = release.MinimumSeedTime.ToString();
|
|
||||||
break;
|
|
||||||
case "imdb":
|
|
||||||
release.Imdb = ParseUtil.GetLongFromString(value);
|
|
||||||
value = release.Imdb.ToString();
|
|
||||||
break;
|
|
||||||
case "rageid":
|
|
||||||
Regex RageIDRegEx = new Regex(@"(\d+)", RegexOptions.Compiled);
|
|
||||||
var RageIDMatch = RageIDRegEx.Match(value);
|
|
||||||
var RageID = RageIDMatch.Groups[1].Value;
|
|
||||||
release.RageID = ParseUtil.CoerceLong(RageID);
|
|
||||||
value = release.RageID.ToString();
|
|
||||||
break;
|
|
||||||
case "tvdbid":
|
|
||||||
Regex TVDBIdRegEx = new Regex(@"(\d+)", RegexOptions.Compiled);
|
|
||||||
var TVDBIdMatch = TVDBIdRegEx.Match(value);
|
|
||||||
var TVDBId = TVDBIdMatch.Groups[1].Value;
|
|
||||||
release.TVDBId = ParseUtil.CoerceLong(TVDBId);
|
|
||||||
value = release.TVDBId.ToString();
|
|
||||||
break;
|
|
||||||
case "banner":
|
|
||||||
if(!string.IsNullOrWhiteSpace(value)) {
|
|
||||||
var bannerurl = resolvePath(value);
|
|
||||||
release.BannerUrl = bannerurl;
|
|
||||||
}
|
|
||||||
value = release.BannerUrl.ToString();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
variables[variablesKey] = value;
|
CurrentRow.Append(MergeNodes.ToArray());
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
if (!variables.ContainsKey(variablesKey))
|
|
||||||
variables[variablesKey] = null;
|
|
||||||
if (OptionalFileds.Contains(Field.Key) || FieldModifiers.Contains("optional") || Field.Value.Optional)
|
|
||||||
continue;
|
|
||||||
throw new Exception(string.Format("Error while parsing field={0}, selector={1}, value={2}: {3}", Field.Key, Field.Value.Selector, (value == null ? "<null>" : value), ex.Message));
|
|
||||||
}
|
}
|
||||||
|
Rows.RemoveRange(i + 1, After);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var Filters = Definition.Search.Rows.Filters;
|
foreach (var Row in Rows)
|
||||||
var SkipRelease = false;
|
{
|
||||||
if (Filters != null)
|
try
|
||||||
{
|
{
|
||||||
foreach (filterBlock Filter in Filters)
|
var release = new ReleaseInfo();
|
||||||
|
release.MinimumRatio = 1;
|
||||||
|
release.MinimumSeedTime = 48 * 60 * 60;
|
||||||
|
|
||||||
|
// Parse fields
|
||||||
|
foreach (var Field in Search.Fields)
|
||||||
{
|
{
|
||||||
switch (Filter.Name)
|
var FieldParts = Field.Key.Split('|');
|
||||||
{
|
var FieldName = FieldParts[0];
|
||||||
case "andmatch":
|
var FieldModifiers = new List<string>();
|
||||||
int CharacterLimit = -1;
|
for (var i = 1; i < FieldParts.Length; i++)
|
||||||
if (Filter.Args != null)
|
FieldModifiers.Add(FieldParts[i]);
|
||||||
CharacterLimit = int.Parse(Filter.Args);
|
|
||||||
|
|
||||||
if (query.ImdbID != null && TorznabCaps.SupportsImdbSearch)
|
string value = null;
|
||||||
break; // skip andmatch filter for imdb searches
|
var variablesKey = ".Result." + FieldName;
|
||||||
|
|
||||||
if (!query.MatchQueryStringAND(release.Title, CharacterLimit))
|
|
||||||
{
|
|
||||||
logger.Debug(string.Format("CardigannIndexer ({0}): skipping {1} (andmatch filter)", ID, release.Title));
|
|
||||||
SkipRelease = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "strdump":
|
|
||||||
// for debugging
|
|
||||||
logger.Info(string.Format("CardigannIndexer ({0}): row strdump: {1}", ID, Row.ToHtmlPretty()));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
logger.Error(string.Format("CardigannIndexer ({0}): Unsupported rows filter: {1}", ID, Filter.Name));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SkipRelease)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// if DateHeaders is set go through the previous rows and look for the header selector
|
|
||||||
var DateHeaders = Definition.Search.Rows.Dateheaders;
|
|
||||||
if (release.PublishDate == DateTime.MinValue && DateHeaders != null)
|
|
||||||
{
|
|
||||||
var PrevRow = Row.PreviousElementSibling;
|
|
||||||
string value = null;
|
|
||||||
while (PrevRow != null)
|
|
||||||
{
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
value = handleSelector(DateHeaders, PrevRow);
|
value = handleSelector(Field.Value, Row, variables);
|
||||||
break;
|
switch (FieldName)
|
||||||
|
{
|
||||||
|
case "download":
|
||||||
|
if (string.IsNullOrEmpty(value))
|
||||||
|
{
|
||||||
|
value = null;
|
||||||
|
release.Link = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (value.StartsWith("magnet:"))
|
||||||
|
{
|
||||||
|
release.MagnetUri = new Uri(value);
|
||||||
|
//release.Link = release.MagnetUri;
|
||||||
|
value = release.MagnetUri.ToString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
release.Link = resolvePath(value);
|
||||||
|
value = release.Link.ToString();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "magnet":
|
||||||
|
release.MagnetUri = new Uri(value);
|
||||||
|
value = release.MagnetUri.ToString();
|
||||||
|
break;
|
||||||
|
case "details":
|
||||||
|
var url = resolvePath(value);
|
||||||
|
release.Guid = url;
|
||||||
|
release.Comments = url;
|
||||||
|
if (release.Guid == null)
|
||||||
|
release.Guid = url;
|
||||||
|
value = url.ToString();
|
||||||
|
break;
|
||||||
|
case "comments":
|
||||||
|
var CommentsUrl = resolvePath(value);
|
||||||
|
if (release.Comments == null)
|
||||||
|
release.Comments = CommentsUrl;
|
||||||
|
if (release.Guid == null)
|
||||||
|
release.Guid = CommentsUrl;
|
||||||
|
value = CommentsUrl.ToString();
|
||||||
|
break;
|
||||||
|
case "title":
|
||||||
|
if (FieldModifiers.Contains("append"))
|
||||||
|
release.Title += value;
|
||||||
|
else
|
||||||
|
release.Title = value;
|
||||||
|
value = release.Title;
|
||||||
|
break;
|
||||||
|
case "description":
|
||||||
|
if (FieldModifiers.Contains("append"))
|
||||||
|
release.Description += value;
|
||||||
|
else
|
||||||
|
release.Description = value;
|
||||||
|
value = release.Description;
|
||||||
|
break;
|
||||||
|
case "category":
|
||||||
|
release.Category = MapTrackerCatToNewznab(value);
|
||||||
|
value = release.Category.ToString();
|
||||||
|
break;
|
||||||
|
case "size":
|
||||||
|
release.Size = ReleaseInfo.GetBytes(value);
|
||||||
|
value = release.Size.ToString();
|
||||||
|
break;
|
||||||
|
case "leechers":
|
||||||
|
var Leechers = ParseUtil.CoerceInt(value);
|
||||||
|
if (release.Peers == null)
|
||||||
|
release.Peers = Leechers;
|
||||||
|
else
|
||||||
|
release.Peers += Leechers;
|
||||||
|
value = Leechers.ToString();
|
||||||
|
break;
|
||||||
|
case "seeders":
|
||||||
|
release.Seeders = ParseUtil.CoerceInt(value);
|
||||||
|
if (release.Peers == null)
|
||||||
|
release.Peers = release.Seeders;
|
||||||
|
else
|
||||||
|
release.Peers += release.Seeders;
|
||||||
|
value = release.Seeders.ToString();
|
||||||
|
break;
|
||||||
|
case "date":
|
||||||
|
release.PublishDate = DateTimeUtil.FromUnknown(value);
|
||||||
|
value = release.PublishDate.ToString(DateTimeUtil.RFC1123ZPattern);
|
||||||
|
break;
|
||||||
|
case "files":
|
||||||
|
release.Files = ParseUtil.CoerceLong(value);
|
||||||
|
value = release.Files.ToString();
|
||||||
|
break;
|
||||||
|
case "grabs":
|
||||||
|
release.Grabs = ParseUtil.CoerceLong(value);
|
||||||
|
value = release.Grabs.ToString();
|
||||||
|
break;
|
||||||
|
case "downloadvolumefactor":
|
||||||
|
release.DownloadVolumeFactor = ParseUtil.CoerceDouble(value);
|
||||||
|
value = release.DownloadVolumeFactor.ToString();
|
||||||
|
break;
|
||||||
|
case "uploadvolumefactor":
|
||||||
|
release.UploadVolumeFactor = ParseUtil.CoerceDouble(value);
|
||||||
|
value = release.UploadVolumeFactor.ToString();
|
||||||
|
break;
|
||||||
|
case "minimumratio":
|
||||||
|
release.MinimumRatio = ParseUtil.CoerceDouble(value);
|
||||||
|
value = release.MinimumRatio.ToString();
|
||||||
|
break;
|
||||||
|
case "minimumseedtime":
|
||||||
|
release.MinimumSeedTime = ParseUtil.CoerceLong(value);
|
||||||
|
value = release.MinimumSeedTime.ToString();
|
||||||
|
break;
|
||||||
|
case "imdb":
|
||||||
|
release.Imdb = ParseUtil.GetLongFromString(value);
|
||||||
|
value = release.Imdb.ToString();
|
||||||
|
break;
|
||||||
|
case "rageid":
|
||||||
|
Regex RageIDRegEx = new Regex(@"(\d+)", RegexOptions.Compiled);
|
||||||
|
var RageIDMatch = RageIDRegEx.Match(value);
|
||||||
|
var RageID = RageIDMatch.Groups[1].Value;
|
||||||
|
release.RageID = ParseUtil.CoerceLong(RageID);
|
||||||
|
value = release.RageID.ToString();
|
||||||
|
break;
|
||||||
|
case "tvdbid":
|
||||||
|
Regex TVDBIdRegEx = new Regex(@"(\d+)", RegexOptions.Compiled);
|
||||||
|
var TVDBIdMatch = TVDBIdRegEx.Match(value);
|
||||||
|
var TVDBId = TVDBIdMatch.Groups[1].Value;
|
||||||
|
release.TVDBId = ParseUtil.CoerceLong(TVDBId);
|
||||||
|
value = release.TVDBId.ToString();
|
||||||
|
break;
|
||||||
|
case "banner":
|
||||||
|
if(!string.IsNullOrWhiteSpace(value)) {
|
||||||
|
var bannerurl = resolvePath(value);
|
||||||
|
release.BannerUrl = bannerurl;
|
||||||
|
}
|
||||||
|
value = release.BannerUrl.ToString();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
variables[variablesKey] = value;
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// do nothing
|
if (!variables.ContainsKey(variablesKey))
|
||||||
|
variables[variablesKey] = null;
|
||||||
|
if (OptionalFileds.Contains(Field.Key) || FieldModifiers.Contains("optional") || Field.Value.Optional)
|
||||||
|
continue;
|
||||||
|
throw new Exception(string.Format("Error while parsing field={0}, selector={1}, value={2}: {3}", Field.Key, Field.Value.Selector, (value == null ? "<null>" : value), ex.Message));
|
||||||
}
|
}
|
||||||
PrevRow = PrevRow.PreviousElementSibling;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value == null && DateHeaders.Optional == false)
|
|
||||||
throw new Exception(string.Format("No date header row found for {0}", release.ToString()));
|
|
||||||
if (value != null)
|
|
||||||
release.PublishDate = DateTimeUtil.FromUnknown(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
releases.Add(release);
|
var Filters = Definition.Search.Rows.Filters;
|
||||||
}
|
var SkipRelease = false;
|
||||||
catch (Exception ex)
|
if (Filters != null)
|
||||||
{
|
{
|
||||||
logger.Error(string.Format("CardigannIndexer ({0}): Error while parsing row '{1}':\n\n{2}", ID, Row.ToHtmlPretty(), ex));
|
foreach (filterBlock Filter in Filters)
|
||||||
}
|
{
|
||||||
|
switch (Filter.Name)
|
||||||
|
{
|
||||||
|
case "andmatch":
|
||||||
|
int CharacterLimit = -1;
|
||||||
|
if (Filter.Args != null)
|
||||||
|
CharacterLimit = int.Parse(Filter.Args);
|
||||||
|
|
||||||
|
if (query.ImdbID != null && TorznabCaps.SupportsImdbSearch)
|
||||||
|
break; // skip andmatch filter for imdb searches
|
||||||
|
|
||||||
|
if (!query.MatchQueryStringAND(release.Title, CharacterLimit))
|
||||||
|
{
|
||||||
|
logger.Debug(string.Format("CardigannIndexer ({0}): skipping {1} (andmatch filter)", ID, release.Title));
|
||||||
|
SkipRelease = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "strdump":
|
||||||
|
// for debugging
|
||||||
|
logger.Info(string.Format("CardigannIndexer ({0}): row strdump: {1}", ID, Row.ToHtmlPretty()));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
logger.Error(string.Format("CardigannIndexer ({0}): Unsupported rows filter: {1}", ID, Filter.Name));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SkipRelease)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// if DateHeaders is set go through the previous rows and look for the header selector
|
||||||
|
var DateHeaders = Definition.Search.Rows.Dateheaders;
|
||||||
|
if (release.PublishDate == DateTime.MinValue && DateHeaders != null)
|
||||||
|
{
|
||||||
|
var PrevRow = Row.PreviousElementSibling;
|
||||||
|
string value = null;
|
||||||
|
while (PrevRow != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
value = handleSelector(DateHeaders, PrevRow);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
PrevRow = PrevRow.PreviousElementSibling;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value == null && DateHeaders.Optional == false)
|
||||||
|
throw new Exception(string.Format("No date header row found for {0}", release.ToString()));
|
||||||
|
if (value != null)
|
||||||
|
release.PublishDate = DateTimeUtil.FromUnknown(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
releases.Add(release);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.Error(string.Format("CardigannIndexer ({0}): Error while parsing row '{1}':\n\n{2}", ID, Row.ToHtmlPretty(), ex));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
OnParseError(results, ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OnParseError(results, ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
return releases;
|
return releases;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue