mirror of
https://github.com/Jackett/Jackett
synced 2025-02-23 14:51:01 +00:00
manual search: add multiselect support for tracker/category dropdowns
This commit is contained in:
parent
e754d3da9f
commit
00027a41c1
7 changed files with 1783 additions and 36 deletions
1
src/Jackett/Content/css/bootstrap-multiselect.css
vendored
Normal file
1
src/Jackett/Content/css/bootstrap-multiselect.css
vendored
Normal 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}
|
|
@ -724,6 +724,9 @@ function updateReleasesRow(row)
|
|||
}
|
||||
|
||||
function showSearch(selectedIndexer, query, category) {
|
||||
var selectedIndexers = []
|
||||
if (selectedIndexer)
|
||||
selectedIndexers = selectedIndexer.split(",");
|
||||
$('#select-indexer-modal').remove();
|
||||
var releaseTemplate = Handlebars.compile($("#jackett-search").html());
|
||||
var releaseDialog = $(releaseTemplate({
|
||||
|
@ -741,28 +744,30 @@ function showSearch(selectedIndexer, query, category) {
|
|||
window.location.hash = '';
|
||||
}) ;
|
||||
|
||||
var setCategories = function (tracker, items) {
|
||||
var setCategories = function (trackers, items) {
|
||||
var cats = {};
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
if (items[i].configured === true && (items[i].id === tracker || tracker === '')) {
|
||||
indexers["'" + items[i].id + "'"] = items[i].name;
|
||||
for (var prop in items[i].caps) {
|
||||
if (prop < 100000 || tracker)
|
||||
cats[prop] = items[i].caps[prop];
|
||||
if (trackers.length == 0 || $.inArray(items[i].id, trackers) !== -1) {
|
||||
for (var j in items[i].caps) {
|
||||
var cat = items[i].caps[j]
|
||||
if (cat.ID < 100000 || trackers.length == 1)
|
||||
cats[cat.ID] = cat.Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
var select = $('#searchCategory');
|
||||
select.html("<option value=''>-- All --</option>");
|
||||
$.each(cats, function (index, value) {
|
||||
select.append($("<option></option>")
|
||||
.attr("value", value["ID"]).text(value["ID"] + ' (' + value["Name"] + ')'));
|
||||
var selected = select.val();
|
||||
var options = []
|
||||
$.each(cats, function (ID, Name) {
|
||||
options.push({ label: ID + ' (' + Name + ')', value: ID });
|
||||
});
|
||||
select.multiselect('dataprovider', options);
|
||||
select.val(selected).multiselect("refresh");
|
||||
};
|
||||
|
||||
$('#searchTracker').change(jQuery.proxy(function () {
|
||||
var trackerId = $('#searchTracker').val();
|
||||
setCategories(trackerId, this.items);
|
||||
var trackerIds = $('#searchTracker').val();
|
||||
setCategories(trackerIds, this.items);
|
||||
}, { items: configuredIndexers }));
|
||||
|
||||
var queryField = document.getElementById("searchquery");
|
||||
|
@ -783,18 +788,16 @@ function showSearch(selectedIndexer, query, category) {
|
|||
var queryObj = {
|
||||
Query: searchString,
|
||||
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());
|
||||
$('#searchResults div.dataTables_filter input').val("");
|
||||
clearSearchResultTable($('#searchResults'));
|
||||
|
||||
var trackerId = queryObj.Tracker;
|
||||
if (trackerId == null || trackerId == "")
|
||||
trackerId = "all";
|
||||
var trackerId = "all";
|
||||
api.resultsForIndexer(trackerId, queryObj, function (data) {
|
||||
for (var i = 0; i < data.Results.length; i++) {
|
||||
var item = data.Results[i];
|
||||
|
@ -814,18 +817,36 @@ function showSearch(selectedIndexer, query, category) {
|
|||
});
|
||||
|
||||
var searchTracker = releaseDialog.find("#searchTracker");
|
||||
if (selectedIndexer)
|
||||
searchTracker.val(selectedIndexer);
|
||||
var searchCategory = releaseDialog.find('#searchCategory')
|
||||
searchCategory.multiselect({
|
||||
maxHeight: 400,
|
||||
enableFiltering: true,
|
||||
includeSelectAllOption: true,
|
||||
enableCaseInsensitiveFiltering: true,
|
||||
nonSelectedText: 'Any'
|
||||
});
|
||||
if (selectedIndexers)
|
||||
searchTracker.val(selectedIndexers);
|
||||
searchTracker.trigger("change");
|
||||
|
||||
updateSearchResultTable($('#searchResults'), []);
|
||||
clearSearchResultTable($('#searchResults'));
|
||||
releaseDialog.modal("show");
|
||||
|
||||
searchTracker.multiselect({
|
||||
maxHeight: 400,
|
||||
enableFiltering: true,
|
||||
includeSelectAllOption: true,
|
||||
enableCaseInsensitiveFiltering: true,
|
||||
nonSelectedText: 'All'
|
||||
});
|
||||
|
||||
|
||||
if (category !== undefined) {
|
||||
$('#searchCategory').val(category);
|
||||
searchCategory.val(category.split(","));
|
||||
searchCategory.multiselect("refresh");
|
||||
}
|
||||
|
||||
releaseDialog.modal("show");
|
||||
if (query !== undefined) {
|
||||
queryField.value = query;
|
||||
searchButton.click();
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
<script src="../libs/handlebarsextend.js"></script>
|
||||
<script src="../bootstrap/bootstrap.min.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>
|
||||
|
||||
<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 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">
|
||||
<title>Jackett</title>
|
||||
</head>
|
||||
|
@ -419,14 +421,13 @@
|
|||
<label for="text">Query</label>
|
||||
<input type="text" name="query" id="searchquery" />
|
||||
<label for="tracker">Tracker</label>
|
||||
<select name="tracker" id="searchTracker">
|
||||
<option value="">-- All --</option>
|
||||
<select name="tracker" id="searchTracker" multiple="multiple">
|
||||
{{#each indexers}}
|
||||
<option value="{{id}}">{{name}}</option>
|
||||
<option value="{{id}}" selected>{{name}}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
<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>
|
||||
<div id="searchResults"></div>
|
||||
</div>
|
||||
|
|
1716
src/Jackett/Content/libs/bootstrap-multiselect.js
vendored
Normal file
1716
src/Jackett/Content/libs/bootstrap-multiselect.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
|
@ -28,9 +28,8 @@ namespace Jackett.Controllers.V20
|
|||
public override void OnAuthorization(HttpActionContext actionContext)
|
||||
{
|
||||
var validApiKey = Engine.Server.Config.APIKey;
|
||||
var queryParams = actionContext.Request.GetQueryNameValuePairs().ToDictionary();
|
||||
var queryApiKey = queryParams.ContainsKey("apikey") ? queryParams["apikey"] : null;
|
||||
queryApiKey = queryParams.ContainsKey("passkey") ? queryParams["passkey"] : queryApiKey;
|
||||
var queryParams = actionContext.Request.GetQueryNameValuePairs();
|
||||
var queryApiKey = queryParams.Where(x => x.Key == "apikey" || x.Key == "passkey").Select(x => x.Value).FirstOrDefault();
|
||||
|
||||
#if DEBUG
|
||||
if (Debugger.IsAttached)
|
||||
|
@ -112,6 +111,9 @@ namespace Jackett.Controllers.V20
|
|||
var torznabQuery = converted as 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))
|
||||
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 trackers = IndexerService.GetAllIndexers().Where(t => t.IsConfigured);
|
||||
if (CurrentIndexer.ID != "all")
|
||||
trackers = trackers.Where(t => t.ID == CurrentIndexer.ID).ToList();
|
||||
if (request.Tracker != null)
|
||||
trackers = trackers.Where(t => request.Tracker.Contains(t.ID));
|
||||
trackers = trackers.Where(t => t.CanHandleQuery(CurrentQuery));
|
||||
|
||||
var tasks = trackers.ToList().Select(t => t.ResultsForQuery(CurrentQuery)).ToList();
|
||||
|
|
|
@ -420,6 +420,9 @@
|
|||
<None Include="CurlSharp.dll.config">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<Content Include="Content\css\bootstrap-multiselect.css">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\custom_mobile.css">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
@ -432,6 +435,9 @@
|
|||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Content\libs\bootstrap-multiselect.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\libs\handlebarsextend.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
@ -567,10 +573,7 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<ItemGroup />
|
||||
<ItemGroup>
|
||||
<Folder Include="Indexers\Feeds\" />
|
||||
<Folder Include="Models\DTO\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- 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.
|
||||
|
|
|
@ -6,7 +6,8 @@ namespace Jackett.Models.DTO
|
|||
public class ApiSearch
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
@ -37,7 +38,9 @@ namespace Jackett.Models.DTO
|
|||
}
|
||||
|
||||
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();
|
||||
|
||||
// try to build an IMDB Query
|
||||
|
|
Loading…
Reference in a new issue