manual search: add multiselect support for tracker/category dropdowns

This commit is contained in:
kaso17
2017-08-30 17:40:32 +02:00
parent e754d3da9f
commit 00027a41c1
7 changed files with 1783 additions and 36 deletions

View File

@@ -0,0 +1 @@
span.multiselect-native-select{position:relative}span.multiselect-native-select select{border:0!important;clip:rect(0 0 0 0)!important;height:1px!important;margin:-1px -1px -1px -3px!important;overflow:hidden!important;padding:0!important;position:absolute!important;width:1px!important;left:50%;top:30px}.multiselect-container{position:absolute;list-style-type:none;margin:0;padding:0}.multiselect-container .input-group{margin:5px}.multiselect-container>li{padding:0}.multiselect-container>li>a.multiselect-all label{font-weight:700}.multiselect-container>li.multiselect-group label{margin:0;padding:3px 20px 3px 20px;height:100%;font-weight:700}.multiselect-container>li.multiselect-group-clickable label{cursor:pointer}.multiselect-container>li>a{padding:0}.multiselect-container>li>a>label{margin:0;height:100%;cursor:pointer;font-weight:400;padding:3px 20px 3px 40px}.multiselect-container>li>a>label.radio,.multiselect-container>li>a>label.checkbox{margin:0}.multiselect-container>li>a>label>input[type=checkbox]{margin-bottom:5px}.btn-group>.btn-group:nth-child(2)>.multiselect.btn{border-top-left-radius:4px;border-bottom-left-radius:4px}.form-inline .multiselect-container label.checkbox,.form-inline .multiselect-container label.radio{padding:3px 20px 3px 40px}.form-inline .multiselect-container li a label.checkbox input[type=checkbox],.form-inline .multiselect-container li a label.radio input[type=radio]{margin-left:-20px;margin-right:0}

View File

@@ -724,6 +724,9 @@ function updateReleasesRow(row)
} }
function showSearch(selectedIndexer, query, category) { function showSearch(selectedIndexer, query, category) {
var selectedIndexers = []
if (selectedIndexer)
selectedIndexers = selectedIndexer.split(",");
$('#select-indexer-modal').remove(); $('#select-indexer-modal').remove();
var releaseTemplate = Handlebars.compile($("#jackett-search").html()); var releaseTemplate = Handlebars.compile($("#jackett-search").html());
var releaseDialog = $(releaseTemplate({ var releaseDialog = $(releaseTemplate({
@@ -741,28 +744,30 @@ function showSearch(selectedIndexer, query, category) {
window.location.hash = ''; window.location.hash = '';
}) ; }) ;
var setCategories = function (tracker, items) { var setCategories = function (trackers, items) {
var cats = {}; var cats = {};
for (var i = 0; i < items.length; i++) { for (var i = 0; i < items.length; i++) {
if (items[i].configured === true && (items[i].id === tracker || tracker === '')) { if (trackers.length == 0 || $.inArray(items[i].id, trackers) !== -1) {
indexers["'" + items[i].id + "'"] = items[i].name; for (var j in items[i].caps) {
for (var prop in items[i].caps) { var cat = items[i].caps[j]
if (prop < 100000 || tracker) if (cat.ID < 100000 || trackers.length == 1)
cats[prop] = items[i].caps[prop]; cats[cat.ID] = cat.Name;
} }
} }
} }
var select = $('#searchCategory'); var select = $('#searchCategory');
select.html("<option value=''>-- All --</option>"); var selected = select.val();
$.each(cats, function (index, value) { var options = []
select.append($("<option></option>") $.each(cats, function (ID, Name) {
.attr("value", value["ID"]).text(value["ID"] + ' (' + value["Name"] + ')')); options.push({ label: ID + ' (' + Name + ')', value: ID });
}); });
select.multiselect('dataprovider', options);
select.val(selected).multiselect("refresh");
}; };
$('#searchTracker').change(jQuery.proxy(function () { $('#searchTracker').change(jQuery.proxy(function () {
var trackerId = $('#searchTracker').val(); var trackerIds = $('#searchTracker').val();
setCategories(trackerId, this.items); setCategories(trackerIds, this.items);
}, { items: configuredIndexers })); }, { items: configuredIndexers }));
var queryField = document.getElementById("searchquery"); var queryField = document.getElementById("searchquery");
@@ -783,18 +788,16 @@ function showSearch(selectedIndexer, query, category) {
var queryObj = { var queryObj = {
Query: searchString, Query: searchString,
Category: releaseDialog.find('#searchCategory').val(), Category: releaseDialog.find('#searchCategory').val(),
Tracker: releaseDialog.find('#searchTracker').val().replace("'", "").replace("'", ""), Tracker: releaseDialog.find('#searchTracker').val()
}; };
window.location.hash = $.param({ search: queryObj.Query, tracker: queryObj.Tracker, category: queryObj.Category}); window.location.hash = $.param({ search: queryObj.Query, tracker: queryObj.Tracker.join(","), category: queryObj.Category.join(",") });
$('#jackett-search-perform').html($('#spinner').html()); $('#jackett-search-perform').html($('#spinner').html());
$('#searchResults div.dataTables_filter input').val(""); $('#searchResults div.dataTables_filter input').val("");
clearSearchResultTable($('#searchResults')); clearSearchResultTable($('#searchResults'));
var trackerId = queryObj.Tracker; var trackerId = "all";
if (trackerId == null || trackerId == "")
trackerId = "all";
api.resultsForIndexer(trackerId, queryObj, function (data) { api.resultsForIndexer(trackerId, queryObj, function (data) {
for (var i = 0; i < data.Results.length; i++) { for (var i = 0; i < data.Results.length; i++) {
var item = data.Results[i]; var item = data.Results[i];
@@ -814,18 +817,36 @@ function showSearch(selectedIndexer, query, category) {
}); });
var searchTracker = releaseDialog.find("#searchTracker"); var searchTracker = releaseDialog.find("#searchTracker");
if (selectedIndexer) var searchCategory = releaseDialog.find('#searchCategory')
searchTracker.val(selectedIndexer); searchCategory.multiselect({
maxHeight: 400,
enableFiltering: true,
includeSelectAllOption: true,
enableCaseInsensitiveFiltering: true,
nonSelectedText: 'Any'
});
if (selectedIndexers)
searchTracker.val(selectedIndexers);
searchTracker.trigger("change"); searchTracker.trigger("change");
updateSearchResultTable($('#searchResults'), []); updateSearchResultTable($('#searchResults'), []);
clearSearchResultTable($('#searchResults')); clearSearchResultTable($('#searchResults'));
releaseDialog.modal("show");
searchTracker.multiselect({
maxHeight: 400,
enableFiltering: true,
includeSelectAllOption: true,
enableCaseInsensitiveFiltering: true,
nonSelectedText: 'All'
});
if (category !== undefined) { if (category !== undefined) {
$('#searchCategory').val(category); searchCategory.val(category.split(","));
searchCategory.multiselect("refresh");
} }
releaseDialog.modal("show");
if (query !== undefined) { if (query !== undefined) {
queryField.value = query; queryField.value = query;
searchButton.click(); searchButton.click();

View File

@@ -29,6 +29,7 @@
<script src="../libs/handlebarsextend.js"></script> <script src="../libs/handlebarsextend.js"></script>
<script src="../bootstrap/bootstrap.min.js"></script> <script src="../bootstrap/bootstrap.min.js"></script>
<script src="../libs/bootstrap-notify.js"></script> <script src="../libs/bootstrap-notify.js"></script>
<script type="text/javascript" src="../libs/bootstrap-multiselect.js"></script>
<script src="https://www.google.com/recaptcha/api.js?render=explicit" async defer></script> <script src="https://www.google.com/recaptcha/api.js?render=explicit" async defer></script>
<link href="../bootstrap/bootstrap.min.css" rel="stylesheet"> <link href="../bootstrap/bootstrap.min.css" rel="stylesheet">
@@ -37,6 +38,7 @@
<link rel="stylesheet" href="../custom_mobile.css" media="only screen and (max-device-width: 480px)"> <link rel="stylesheet" href="../custom_mobile.css" media="only screen and (max-device-width: 480px)">
<link href="../css/jquery.dataTables.min.css" rel="stylesheet" type="text/css"> <link href="../css/jquery.dataTables.min.css" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="../css/bootstrap-multiselect.css" type="text/css" />
<link rel="stylesheet" href="../css/font-awesome.min.css"> <link rel="stylesheet" href="../css/font-awesome.min.css">
<title>Jackett</title> <title>Jackett</title>
</head> </head>
@@ -419,14 +421,13 @@
<label for="text">Query</label> <label for="text">Query</label>
<input type="text" name="query" id="searchquery" /> <input type="text" name="query" id="searchquery" />
<label for="tracker">Tracker</label> <label for="tracker">Tracker</label>
<select name="tracker" id="searchTracker"> <select name="tracker" id="searchTracker" multiple="multiple">
<option value="">-- All --</option>
{{#each indexers}} {{#each indexers}}
<option value="{{id}}">{{name}}</option> <option value="{{id}}" selected>{{name}}</option>
{{/each}} {{/each}}
</select> </select>
<label for="category">Category</label> <label for="category">Category</label>
<select name="category" id="searchCategory"></select> <select name="category" id="searchCategory" multiple="multiple"></select>
<button id="jackett-search-perform" class="btn btn-success btn-sm"><span class="fa fa-search"></span></button> <button id="jackett-search-perform" class="btn btn-success btn-sm"><span class="fa fa-search"></span></button>
<div id="searchResults"></div> <div id="searchResults"></div>
</div> </div>

File diff suppressed because it is too large Load Diff

View File

@@ -28,9 +28,8 @@ namespace Jackett.Controllers.V20
public override void OnAuthorization(HttpActionContext actionContext) public override void OnAuthorization(HttpActionContext actionContext)
{ {
var validApiKey = Engine.Server.Config.APIKey; var validApiKey = Engine.Server.Config.APIKey;
var queryParams = actionContext.Request.GetQueryNameValuePairs().ToDictionary(); var queryParams = actionContext.Request.GetQueryNameValuePairs();
var queryApiKey = queryParams.ContainsKey("apikey") ? queryParams["apikey"] : null; var queryApiKey = queryParams.Where(x => x.Key == "apikey" || x.Key == "passkey").Select(x => x.Value).FirstOrDefault();
queryApiKey = queryParams.ContainsKey("passkey") ? queryParams["passkey"] : queryApiKey;
#if DEBUG #if DEBUG
if (Debugger.IsAttached) if (Debugger.IsAttached)
@@ -112,6 +111,9 @@ namespace Jackett.Controllers.V20
var torznabQuery = converted as TorznabQuery; var torznabQuery = converted as TorznabQuery;
resultController.CurrentQuery = torznabQuery; resultController.CurrentQuery = torznabQuery;
if (queryType == typeof(ApiSearch)) // Skip CanHandleQuery() check for manual search (CurrentIndexer isn't used during manul search)
return;
if (!resultController.CurrentIndexer.CanHandleQuery(resultController.CurrentQuery)) if (!resultController.CurrentIndexer.CanHandleQuery(resultController.CurrentQuery))
actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, $"{resultController.CurrentIndexer.ID} does not support the requested query."); actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, $"{resultController.CurrentIndexer.ID} does not support the requested query.");
} }
@@ -157,8 +159,8 @@ namespace Jackett.Controllers.V20
{ {
var manualResult = new ManualSearchResult(); var manualResult = new ManualSearchResult();
var trackers = IndexerService.GetAllIndexers().Where(t => t.IsConfigured); var trackers = IndexerService.GetAllIndexers().Where(t => t.IsConfigured);
if (CurrentIndexer.ID != "all") if (request.Tracker != null)
trackers = trackers.Where(t => t.ID == CurrentIndexer.ID).ToList(); trackers = trackers.Where(t => request.Tracker.Contains(t.ID));
trackers = trackers.Where(t => t.CanHandleQuery(CurrentQuery)); trackers = trackers.Where(t => t.CanHandleQuery(CurrentQuery));
var tasks = trackers.ToList().Select(t => t.ResultsForQuery(CurrentQuery)).ToList(); var tasks = trackers.ToList().Select(t => t.ResultsForQuery(CurrentQuery)).ToList();

View File

@@ -420,6 +420,9 @@
<None Include="CurlSharp.dll.config"> <None Include="CurlSharp.dll.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
<Content Include="Content\css\bootstrap-multiselect.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Content\custom_mobile.css"> <Content Include="Content\custom_mobile.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
@@ -432,6 +435,9 @@
</Content> </Content>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="Content\libs\bootstrap-multiselect.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Content\libs\handlebarsextend.js"> <Content Include="Content\libs\handlebarsextend.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
@@ -567,10 +573,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup /> <ItemGroup />
<ItemGroup /> <ItemGroup />
<ItemGroup> <ItemGroup />
<Folder Include="Indexers\Feeds\" />
<Folder Include="Models\DTO\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.

View File

@@ -6,7 +6,8 @@ namespace Jackett.Models.DTO
public class ApiSearch public class ApiSearch
{ {
public string Query { get; set; } public string Query { get; set; }
public int Category { get; set; } public int[] Category { get; set; }
public string[] Tracker { get; set; }
public static TorznabQuery ToTorznabQuery(ApiSearch request) public static TorznabQuery ToTorznabQuery(ApiSearch request)
{ {
@@ -37,7 +38,9 @@ namespace Jackett.Models.DTO
} }
stringQuery.SearchTerm = queryStr; stringQuery.SearchTerm = queryStr;
stringQuery.Categories = request.Category == 0 ? new int[0] : new int[1] { request.Category }; stringQuery.Categories = request.Category;
if (stringQuery.Categories == null)
stringQuery.Categories = new int[0];
stringQuery.ExpandCatsToSubCats(); stringQuery.ExpandCatsToSubCats();
// try to build an IMDB Query // try to build an IMDB Query