diff --git a/README.md b/README.md
index 4ebf8197d..38706d988 100644
--- a/README.md
+++ b/README.md
@@ -586,25 +586,26 @@ To get all Jackett indexers including their capabilities you can use `t=indexers
### Filter indexers
-Another special "filter" indexer is avaible at /api/v2.0/indexers/filter/results/torznab
-It will query the configured indexers that match the filter expression criterias and return the combined results as "all".
+Another special "filter" indexer is available at `/api/v2.0/indexers//results/torznab`
+It will query the configured indexers that match the `` expression criterias and return the combined results as "all".
Supported filters
Filter | Condition
-|-
-type:type
| where the indexer type is equal to type
-tag:tag
| where the indexer tags contains tag
-lang:tag
| where the indexer language start with lang
+`type:` | where the indexer type is equal to ``
+`tag:` | where the indexer tags contains ``
+`lang:` | where the indexer language start with ``
+`test:{passed|failed}` | where the last indexer test performed `passed` or `failed`
Supported operators
Operator | Condition
-|-
-!filter
| where not filter
-filter1+filter2+...
| where filter1 and filter2 and ...
-filter1,filter2,...
| where filter1 or filter2 or ...
+`!` | where not ``
+`+[+...]` | where `` and `` [and ``...]
+`,[+...]` | where `` or `` [or ``...]
Example:
-The "filter" indexer at /api/v2.0/indexers/tag:group1,!type:private+lang:en/results/torznab
will query all the configured indexers tagged with `group1` or all the indexers not private and with `en` language (`en-en`,`en-us`,...)
+The "filter" indexer at `/api/v2.0/indexers/tag:group1,!type:private+lang:en/results/torznab` will query all the configured indexers tagged with `group1` or all the indexers not private and with `en` language (`en-en`,`en-us`,...)
## Installation on Windows
We recommend you install Jackett as a Windows service using the supplied installer. You may also download the zipped version if you would like to configure everything manually.
diff --git a/src/Jackett.Common/Content/custom.js b/src/Jackett.Common/Content/custom.js
index 5bbf2a369..694e44ea8 100644
--- a/src/Jackett.Common/Content/custom.js
+++ b/src/Jackett.Common/Content/custom.js
@@ -84,6 +84,10 @@ function tag_filter(indexer) {
return indexer.tags.map(t => t.toLowerCase()).indexOf(this.value.toLowerCase()) > -1;
}
+function state_filter(indexer) {
+ return indexer.state == this.value;
+}
+
function getJackettConfig(callback) {
api.getServerConfig(callback).fail(function () {
doNotify("Error loading Jackett settings, request to Jackett server failed, is server running ?", "danger", "glyphicon glyphicon-alert");
@@ -211,6 +215,9 @@ function configureFilters(indexers) {
availableFilters.push(f);
}
+ availableFilters.push({id: "test:passed", apply: state_filter, value: "success" });
+ availableFilters.push({id: "test:failed", apply: state_filter, value: "error" });
+
["public", "private", "semi-private"]
.map(t => { return { id: "type:" + t, apply: type_filter, value: t } })
.forEach(add);
@@ -606,6 +613,10 @@ function updateTestState(id, state, message, parent) {
}).rows().invalidate('dom');
if (state != "inprogres")
dt.draw();
+
+ var indexer = configuredIndexers.find(x => x.id == id);
+ if (indexer)
+ indexer.state = state;
}
function testIndexer(id, notifyResult) {
diff --git a/src/Jackett.Common/Utils/FilterFunc.cs b/src/Jackett.Common/Utils/FilterFunc.cs
index 2f7f71a29..46153c224 100644
--- a/src/Jackett.Common/Utils/FilterFunc.cs
+++ b/src/Jackett.Common/Utils/FilterFunc.cs
@@ -16,10 +16,11 @@ namespace Jackett.Common.Utils
});
public static readonly FilterFuncComponent Language = Component("lang", args => indexer => indexer.Language.StartsWith(args, StringComparison.InvariantCultureIgnoreCase));
public static readonly FilterFuncComponent Type = Component("type", args => indexer => string.Equals(indexer.Type, args, StringComparison.InvariantCultureIgnoreCase));
+ public static readonly FilterFuncComponent Test = TestFilterFunc.Default;
static FilterFunc()
{
- Expression = new FilterFuncExpression(Tag, Language, Type);
+ Expression = new FilterFuncExpression(Tag, Language, Type, Test);
}
public static bool TryParse(string source, out Func func)
@@ -49,10 +50,10 @@ namespace Jackett.Common.Utils
public override Func ToFunc(string args)
{
var func = builder(args);
- return indexer => indexer != null
- ? indexer.IsConfigured && func(indexer)
- : throw new ArgumentNullException(nameof(indexer));
+ return indexer => IsValid(indexer) && func(indexer);
}
}
+
+ protected static bool IsValid(IIndexer indexer) => (indexer?.IsConfigured ?? throw new ArgumentNullException(nameof(indexer)));
}
}
diff --git a/src/Jackett.Common/Utils/FilterFuncs/TestFilterFunc.cs b/src/Jackett.Common/Utils/FilterFuncs/TestFilterFunc.cs
new file mode 100644
index 000000000..35d7a1f99
--- /dev/null
+++ b/src/Jackett.Common/Utils/FilterFuncs/TestFilterFunc.cs
@@ -0,0 +1,28 @@
+using System;
+
+using Jackett.Common.Indexers;
+
+namespace Jackett.Common.Utils.FilterFuncs
+{
+ public class TestFilterFunc : FilterFuncComponent
+ {
+ public static readonly TestFilterFunc Default = new TestFilterFunc();
+ public const string Passed = "passed";
+ public const string Failed = "failed";
+
+ private TestFilterFunc() : base("test")
+ {
+ }
+
+ public override Func ToFunc(string args)
+ {
+ if (args == null)
+ throw new ArgumentNullException(nameof(args));
+ if (string.Equals(Passed, args, StringComparison.InvariantCultureIgnoreCase))
+ return i => IsValid(i) && i.LastError == null;
+ if (string.Equals(Failed, args, StringComparison.InvariantCultureIgnoreCase))
+ return i => IsValid(i) && i.LastError != null;
+ throw new ArgumentException($"Invalid filter. Status should be '{Passed}' or '{Failed}'", nameof(args));
+ }
+ }
+}
diff --git a/src/Jackett.Test/Common/Utils/FilterFuncs/IndexerStub.cs b/src/Jackett.Test/Common/Utils/FilterFuncs/IndexerBaseStub.cs
similarity index 97%
rename from src/Jackett.Test/Common/Utils/FilterFuncs/IndexerStub.cs
rename to src/Jackett.Test/Common/Utils/FilterFuncs/IndexerBaseStub.cs
index d22db58a9..6962d7a51 100644
--- a/src/Jackett.Test/Common/Utils/FilterFuncs/IndexerStub.cs
+++ b/src/Jackett.Test/Common/Utils/FilterFuncs/IndexerBaseStub.cs
@@ -8,7 +8,7 @@ using Newtonsoft.Json.Linq;
namespace Jackett.Test.Common.Utils.FilterFuncs
{
- public class IndexerStub : IIndexer
+ public abstract class IndexerBaseStub : IIndexer
{
public virtual string SiteLink => throw TestExceptions.UnexpectedInvocation;
diff --git a/src/Jackett.Test/Common/Utils/FilterFuncs/LanguageFuncTests.cs b/src/Jackett.Test/Common/Utils/FilterFuncs/LanguageFuncTests.cs
index 9fc427dbe..946e8aed2 100644
--- a/src/Jackett.Test/Common/Utils/FilterFuncs/LanguageFuncTests.cs
+++ b/src/Jackett.Test/Common/Utils/FilterFuncs/LanguageFuncTests.cs
@@ -6,9 +6,9 @@ namespace Jackett.Test.Common.Utils.FilterFuncs
[TestFixture]
public class LanguageFuncTests
{
- private class LanguageIndexerStub : IndexerStub
+ private class IndexerStub : IndexerBaseStub
{
- public LanguageIndexerStub(string language)
+ public IndexerStub(string language)
{
Language = language;
}
@@ -24,19 +24,19 @@ namespace Jackett.Test.Common.Utils.FilterFuncs
var language = "en";
var region = "US";
- var lrLanguage = new LanguageIndexerStub($"{language.ToLower()}-{region.ToLower()}");
+ var lrLanguage = new IndexerStub(language: $"{language.ToLower()}-{region.ToLower()}");
var LRFilterFunc = Language.ToFunc($"{language.ToUpper()}-{region.ToUpper()}");
Assert.IsTrue(LRFilterFunc(lrLanguage));
- var lRLanguage = new LanguageIndexerStub($"{language.ToLower()}-{region.ToUpper()}");
+ var lRLanguage = new IndexerStub(language: $"{language.ToLower()}-{region.ToUpper()}");
var LrFilterFunc = Language.ToFunc($"{language.ToUpper()}-{region.ToLower()}");
Assert.IsTrue(LrFilterFunc(lRLanguage));
- var LrLanguage = new LanguageIndexerStub($"{language.ToUpper()}-{region.ToLower()}");
+ var LrLanguage = new IndexerStub(language: $"{language.ToUpper()}-{region.ToLower()}");
var lRFilterFunc = Language.ToFunc($"{language.ToLower()}-{region.ToUpper()}");
Assert.IsTrue(lRFilterFunc(LrLanguage));
- var LRLanguage = new LanguageIndexerStub($"{language.ToUpper()}-{region.ToUpper()}");
+ var LRLanguage = new IndexerStub(language: $"{language.ToUpper()}-{region.ToUpper()}");
var lrFilterFunc = Language.ToFunc($"{language.ToLower()}-{region.ToLower()}");
Assert.IsTrue(lrFilterFunc(LRLanguage));
}
@@ -47,9 +47,9 @@ namespace Jackett.Test.Common.Utils.FilterFuncs
var language = "en";
var funcFilter = Language.ToFunc(language);
- Assert.IsTrue(funcFilter(new LanguageIndexerStub(language)));
- Assert.IsTrue(funcFilter(new LanguageIndexerStub($"{language}-region1")));
- Assert.IsFalse(funcFilter(new LanguageIndexerStub($"language2-{language}")));
+ Assert.IsTrue(funcFilter(new IndexerStub(language: language)));
+ Assert.IsTrue(funcFilter(new IndexerStub(language: $"{language}-region1")));
+ Assert.IsFalse(funcFilter(new IndexerStub(language: $"language2-{language}")));
}
}
}
diff --git a/src/Jackett.Test/Common/Utils/FilterFuncs/TagFuncTests.cs b/src/Jackett.Test/Common/Utils/FilterFuncs/TagFuncTests.cs
index af7795163..c4a021d7f 100644
--- a/src/Jackett.Test/Common/Utils/FilterFuncs/TagFuncTests.cs
+++ b/src/Jackett.Test/Common/Utils/FilterFuncs/TagFuncTests.cs
@@ -6,9 +6,9 @@ namespace Jackett.Test.Common.Utils.FilterFuncs
[TestFixture]
public class TagFuncTests
{
- private class TagsIndexerStub : IndexerStub
+ private class IndexerStub : IndexerBaseStub
{
- public TagsIndexerStub(params string[] tags)
+ public IndexerStub(params string[] tags)
{
Tags = tags;
}
@@ -23,7 +23,7 @@ namespace Jackett.Test.Common.Utils.FilterFuncs
{
var tagId = "g1";
- var tag = new TagsIndexerStub(tagId);
+ var tag = new IndexerStub(tagId);
var upperTarget = Tag.ToFunc(tagId.ToUpper());
Assert.IsTrue(upperTarget(tag));
@@ -38,10 +38,10 @@ namespace Jackett.Test.Common.Utils.FilterFuncs
var tagId = "g1";
var target = Tag.ToFunc(tagId);
- Assert.IsTrue(target(new TagsIndexerStub(tagId)));
- Assert.IsTrue(target(new TagsIndexerStub(tagId, "g2")));
- Assert.IsTrue(target(new TagsIndexerStub("g2", tagId)));
- Assert.IsFalse(target(new TagsIndexerStub("g2")));
+ Assert.IsTrue(target(new IndexerStub(tagId)));
+ Assert.IsTrue(target(new IndexerStub(tagId, "g2")));
+ Assert.IsTrue(target(new IndexerStub("g2", tagId)));
+ Assert.IsFalse(target(new IndexerStub("g2")));
}
}
}
diff --git a/src/Jackett.Test/Common/Utils/FilterFuncs/TestFuncTests.cs b/src/Jackett.Test/Common/Utils/FilterFuncs/TestFuncTests.cs
new file mode 100644
index 000000000..a97577575
--- /dev/null
+++ b/src/Jackett.Test/Common/Utils/FilterFuncs/TestFuncTests.cs
@@ -0,0 +1,91 @@
+using System;
+using Jackett.Common.Utils;
+using Jackett.Common.Utils.FilterFuncs;
+using Jackett.Test.TestHelpers;
+using NUnit.Framework;
+
+namespace Jackett.Test.Common.Utils.FilterFuncs
+{
+ [TestFixture]
+ public class TestFuncTests
+ {
+ private static readonly IndexerStub HealthyIndexer = new IndexerStub(lastError: null);
+ private static readonly IndexerStub ErrorIndexer = new IndexerStub(lastError: "error");
+
+ private class IndexerStub : IndexerBaseStub
+ {
+ private readonly string lastError;
+
+ public IndexerStub(string lastError)
+ {
+ this.lastError = lastError;
+ }
+
+ public override bool IsConfigured => true;
+
+ public override string LastError
+ {
+ get => lastError;
+ set => throw TestExceptions.UnexpectedInvocation;
+ }
+ }
+
+ [Test]
+ public void NullStatus_ThrowsException()
+ {
+ Assert.Throws(() => FilterFunc.Test.ToFunc(null));
+ }
+
+ [Test]
+ public void EmptyStatus_ThrowsException()
+ {
+ Assert.Throws(() => FilterFunc.Test.ToFunc(string.Empty));
+ }
+
+ [Test]
+ public void InvalidStatus_ThrowsException()
+ {
+ Assert.Throws(() => FilterFunc.Test.ToFunc(TestFilterFunc.Passed + TestFilterFunc.Failed));
+ }
+
+ [Test]
+ public void PassedFilter()
+ {
+ var passedFilterFunc = FilterFunc.Test.ToFunc(TestFilterFunc.Passed);
+ Assert.IsTrue(passedFilterFunc(HealthyIndexer));
+ Assert.IsFalse(passedFilterFunc(ErrorIndexer));
+ }
+
+ [Test]
+ public void FailedFilter()
+ {
+ var failedFilterFunc = FilterFunc.Test.ToFunc(TestFilterFunc.Failed);
+ Assert.IsFalse(failedFilterFunc(HealthyIndexer));
+ Assert.IsTrue(failedFilterFunc(ErrorIndexer));
+ }
+
+ [Test]
+ public void PassedFilter_CaseInsensitiveSource()
+ {
+ var upperFilterFunc = FilterFunc.Test.ToFunc(TestFilterFunc.Passed.ToUpper());
+ Assert.IsTrue(upperFilterFunc(HealthyIndexer));
+ Assert.IsFalse(upperFilterFunc(ErrorIndexer));
+
+ var lowerFilterFunc = FilterFunc.Test.ToFunc(TestFilterFunc.Passed.ToLower());
+ Assert.IsTrue(lowerFilterFunc(HealthyIndexer));
+ Assert.IsFalse(lowerFilterFunc(ErrorIndexer));
+ }
+
+ [Test]
+ public void FailedFilter_CaseInsensitiveSource()
+ {
+ var upperFilterFunc = FilterFunc.Test.ToFunc(TestFilterFunc.Failed.ToUpper());
+ Assert.IsFalse(upperFilterFunc(HealthyIndexer));
+ Assert.IsTrue(upperFilterFunc(ErrorIndexer));
+
+ var lowerFilterFunc = FilterFunc.Test.ToFunc(TestFilterFunc.Failed.ToLower());
+ Assert.IsFalse(lowerFilterFunc(HealthyIndexer));
+ Assert.IsTrue(lowerFilterFunc(ErrorIndexer));
+ }
+ }
+}
diff --git a/src/Jackett.Test/Common/Utils/FilterFuncs/TypeFuncTests.cs b/src/Jackett.Test/Common/Utils/FilterFuncs/TypeFuncTests.cs
index 28ead99b0..1722edecb 100644
--- a/src/Jackett.Test/Common/Utils/FilterFuncs/TypeFuncTests.cs
+++ b/src/Jackett.Test/Common/Utils/FilterFuncs/TypeFuncTests.cs
@@ -1,4 +1,5 @@
using NUnit.Framework;
+
using static Jackett.Common.Utils.FilterFunc;
namespace Jackett.Test.Common.Utils.FilterFuncs
@@ -6,9 +7,9 @@ namespace Jackett.Test.Common.Utils.FilterFuncs
[TestFixture]
public class TypeFuncTests
{
- private class TypeIndexerStub : IndexerStub
+ private class IndexerStub : IndexerBaseStub
{
- public TypeIndexerStub(string type)
+ public IndexerStub(string type)
{
Type = type;
}
@@ -23,8 +24,8 @@ namespace Jackett.Test.Common.Utils.FilterFuncs
{
var typeId = "type-id";
- var lowerType = new TypeIndexerStub(typeId.ToLower());
- var upperType = new TypeIndexerStub(typeId.ToUpper());
+ var lowerType = new IndexerStub(type: typeId.ToLower());
+ var upperType = new IndexerStub(type: typeId.ToUpper());
var upperFilterFunc = Type.ToFunc(typeId.ToUpper());
Assert.IsTrue(upperFilterFunc(lowerType));
@@ -42,11 +43,9 @@ namespace Jackett.Test.Common.Utils.FilterFuncs
var funcFilter = Type.ToFunc($"{typeId}");
- Assert.IsFalse(funcFilter(new TypeIndexerStub($"{typeId}suffix")));
- Assert.IsFalse(funcFilter(new TypeIndexerStub($"prefix{typeId}")));
- Assert.IsFalse(funcFilter(new TypeIndexerStub($"prefix{typeId}suffix")));
+ Assert.IsFalse(funcFilter(new IndexerStub(type: $"{typeId}suffix")));
+ Assert.IsFalse(funcFilter(new IndexerStub(type: $"prefix{typeId}")));
+ Assert.IsFalse(funcFilter(new IndexerStub(type: $"prefix{typeId}suffix")));
}
}
-
-
}