Commit 133d2ef3 authored by echel0n's avatar echel0n
Browse files

Added categories to quicksearch for both tv shows and tv episodes

parent f33330af
# Changelog
- * bfe7a0d - 2018-07-28: Pre-Release v9.3.56.dev9
- * ba43911 - 2018-07-29: Added categories to quicksearch for both tv shows and tv episodes
- * f33330a - 2018-07-28: Pre-Release v9.3.56.dev9
- * 4088edc - 2018-07-28: Added back table column selection for shows, show, and schedule views Fixed sorting for all tables Fixed filtering for all tables
- * 7020ea5 - 2018-07-28: Reverted text-nowrap class for schedule view table episode name column
- * 764a9f1 - 2018-07-28: Fix for issue #244 - reverse proxy
......
......@@ -1945,12 +1945,6 @@
"integrity": "sha1-OuyFAA+mGQhdqNLkmD39Z88hFMs=",
"dev": true
},
"bootstrap-table": {
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/bootstrap-table/-/bootstrap-table-1.12.1.tgz",
"integrity": "sha1-ycOXMGeEKpN8BdhjnszLAqvWumU=",
"dev": true
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
......
......@@ -43,6 +43,7 @@ import adba
import sickrage
from sickrage.core.api import API
from sickrage.core.caches.name_cache import NameCache
from sickrage.core.caches.quicksearch_cache import QuicksearchCache
from sickrage.core.common import SD, SKIPPED, WANTED
from sickrage.core.config import Config
from sickrage.core.databases.cache import CacheDB
......@@ -143,6 +144,7 @@ class Core(object):
self.auto_postprocessor = None
self.upnp_client = None
self.oidc_client = None
self.quicksearch_cache = None
def start(self):
self.started = True
......@@ -178,6 +180,7 @@ class Core(object):
self.subtitle_searcher = SubtitleSearcher()
self.auto_postprocessor = AutoPostProcessor()
self.upnp_client = UPNPClient()
self.quicksearch_cache = QuicksearchCache()
# setup oidc client
realm = KeycloakRealm(server_url='https://auth.sickrage.ca', realm_name='sickrage')
......@@ -270,7 +273,7 @@ class Core(object):
# load data for shows from database
self.load_shows()
if self.config.default_page not in ('home', 'schedule', 'history', 'news', 'IRC'):
if self.config.default_page not in ('schedule', 'history', 'IRC'):
self.config.default_page = 'home'
# cleanup cache folder
......@@ -528,12 +531,15 @@ class Core(object):
def load_shows(self):
"""
Populates the showlist with shows from the database
Populates the showlist and quicksearch cache with shows and episodes from the database
"""
self.quicksearch_cache.load()
for dbData in self.main_db.all('tv_shows'):
try:
self.log.debug("Loading data for show: [{}]".format(dbData['show_name']))
self.showlist += [TVShow(int(dbData['indexer']), int(dbData['indexer_id']))]
self.showlist.append(TVShow(int(dbData['indexer']), int(dbData['indexer_id'])))
self.quicksearch_cache.add_show(dbData['indexer_id'])
except Exception as e:
self.log.debug("Show error in [%s]: %s" % (dbData['location'], str(e)))
self.log.debug("Show error in [%s]: %s" % (dbData['location'], str(e)))
\ No newline at end of file
# Author: echel0n <[email protected]>
# URL: https://sickrage.ca
#
# This file is part of SickRage.
#
# SickRage is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# SickRage is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with SickRage. If not, see <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals
import sickrage
from sickrage.core.media.util import showImage
class QuicksearchCache(object):
def __init__(self):
self.cache = {
'shows': {},
'episodes': {}
}
def load(self):
for x in sickrage.app.cache_db.all('quicksearch'):
if x['category'] == 'shows':
self.cache['shows'][x['showid']] = x
elif x['category'] == 'episodes':
self.cache['episodes'][x['episodeid']] = x
sickrage.app.log.debug("Loaded {} shows to QuickSearch cache".format(len(self.cache['shows'])))
sickrage.app.log.debug("Loaded {} episodes to QuickSearch cache".format(len(self.cache['episodes'])))
def get_shows(self, term):
# return [d for d in sickrage.app.cache_db.all('quicksearch_shows') if term.lower() in d['name'].lower()]
return [d for d in self.cache['shows'].values() if term.lower() in d['name'].lower()]
def get_episodes(self, term):
# return [d for d in sickrage.app.cache_db.all('quicksearch_episodes') if term.lower() in d['name'].lower()]
return [d for d in self.cache['episodes'].values() if term.lower() in d['name'].lower()]
def update_show(self, indexerid):
self.del_show(indexerid)
self.add_show(indexerid)
def add_show(self, indexerid):
show_name = sickrage.app.main_db.get('tv_shows', indexerid)['show_name']
if indexerid not in self.cache['shows']:
sickrage.app.log.debug("Adding show {} to QuickSearch cache".format(show_name))
qsData = {
'_t': 'quicksearch',
'category': 'shows',
'showid': indexerid,
'seasons': len(set([e['season'] for e in sickrage.app.main_db.get_many('tv_episodes', indexerid)])),
'name': show_name,
'img': sickrage.app.config.web_root + showImage(indexerid, 'poster_thumb').url
}
self.cache['shows'][indexerid] = qsData
sickrage.app.cache_db.insert(qsData)
for e in sickrage.app.main_db.get_many('tv_episodes', indexerid):
qsData = {
'_t': 'quicksearch',
'category': 'episodes',
'showid': e['showid'],
'episodeid': e['indexerid'],
'season': e['season'],
'episode': e['episode'],
'name': e['name'],
'showname': show_name,
'img': sickrage.app.config.web_root + showImage(e['showid'], 'poster_thumb').url
}
self.cache['episodes'][e['indexerid']] = qsData
sickrage.app.cache_db.insert(qsData)
def del_show(self, indexerid):
show_name = sickrage.app.main_db.get('tv_shows', indexerid)['show_name']
sickrage.app.log.debug("Deleting show {} from QuickSearch cache".format(show_name))
if indexerid in self.cache['shows']:
del self.cache['shows'][indexerid]
for k, v in self.cache['episodes'].items():
if v['showid'] == indexerid:
del self.cache['episodes'][k]
# remove from database
[sickrage.app.cache_db.delete(x) for x in sickrage.app.cache_db.get_many('quicksearch', indexerid)]
......@@ -24,7 +24,8 @@ import sickrage
from sickrage.core.common import Quality
from sickrage.core.databases import srDatabase
from sickrage.core.databases.cache.index import CacheLastUpdateIndex, CacheLastSearchIndex, CacheSceneExceptionsIndex, \
CacheSceneNamesIndex, CacheNetworkTimezonesIndex, CacheSceneExceptionsRefreshIndex, CacheProvidersIndex
CacheSceneNamesIndex, CacheNetworkTimezonesIndex, CacheSceneExceptionsRefreshIndex, CacheProvidersIndex, \
CacheQuicksearchIndex
from sickrage.core.helpers import validate_url, is_ip_private
......@@ -37,6 +38,7 @@ class CacheDB(srDatabase):
'network_timezones': CacheNetworkTimezonesIndex,
'scene_exceptions_refresh': CacheSceneExceptionsRefreshIndex,
'providers': CacheProvidersIndex,
'quicksearch': CacheQuicksearchIndex
}
_migrate_list = {
......
......@@ -126,3 +126,18 @@ class CacheProvidersIndex(HashIndex):
def make_key(self, key):
return md5(key.encode('utf-8')).hexdigest()
class CacheQuicksearchIndex(HashIndex):
_version = 1
def __init__(self, *args, **kwargs):
kwargs['key_format'] = 'I'
super(CacheQuicksearchIndex, self).__init__(*args, **kwargs)
def make_key_value(self, data):
if data.get('_t') == 'quicksearch' and data.get('showid'):
return data.get('showid'), None
def make_key(self, key):
return key
\ No newline at end of file
......@@ -457,6 +457,8 @@ class QueueItemAdd(ShowQueueItem):
sickrage.app.name_cache.build(self.show)
sickrage.app.quicksearch_cache.add_show(self.show.indexerid)
self.finish()
sickrage.app.log.info(
......@@ -484,7 +486,7 @@ class QueueItemRefresh(ShowQueueItem):
self.show.populateCache(force=self.force)
# Load XEM data to DB for show
xem_refresh(self.show.indexerid, self.show.indexer)
# xem_refresh(self.show.indexerid, self.show.indexer)
self.show.last_refresh = datetime.date.today().toordinal()
......@@ -608,6 +610,8 @@ class QueueItemUpdate(ShowQueueItem):
scrub(DBEpList)
scrub(IndexerEpList)
sickrage.app.quicksearch_cache.update_show(self.show.indexerid)
sickrage.app.log.info(
"Finished updates in {}s for show: {}".format(round(time.time() - self.startTime, 2), self.show.name))
......@@ -639,6 +643,8 @@ class QueueItemRemove(ShowQueueItem):
def run(self):
sickrage.app.log.info("Removing show: {}".format(self.show.name))
sickrage.app.quicksearch_cache.del_show(self.show.indexerid)
self.show.deleteShow(full=self.full)
if sickrage.app.config.use_trakt:
......
......@@ -35,7 +35,7 @@ from sickrage.core.helpers import is_media_file, try_int, replaceExtension, \
safe_getattr, make_dirs, moveFile, delete_empty_folders
from sickrage.core.nameparser import NameParser, InvalidNameException, InvalidShowException
from sickrage.core.processors.post_processor import PostProcessor
from sickrage.core.scene_numbering import xem_refresh, get_scene_absolute_numbering, get_scene_numbering
from sickrage.core.scene_numbering import get_scene_absolute_numbering, get_scene_numbering
from sickrage.core.updaters import tz_updater
from sickrage.indexers import IndexerApi
from sickrage.indexers.exceptions import indexer_seasonnotfound, indexer_error, indexer_episodenotfound
......@@ -408,8 +408,6 @@ class TVEpisode(object):
self._is_proper = try_int(dbData[0]["is_proper"], self.is_proper)
self._version = try_int(dbData[0]["version"], self.version)
xem_refresh(self.show.indexerid, self.show.indexer)
self.scene_season = try_int(dbData[0]["scene_season"], self.scene_season)
self.scene_episode = try_int(dbData[0]["scene_episode"], self.scene_episode)
self.scene_absolute_number = try_int(dbData[0]["scene_absolute_number"], self.scene_absolute_number)
......@@ -497,8 +495,6 @@ class TVEpisode(object):
self.season = season
self.episode = episode
xem_refresh(self.show.indexerid, self.show.indexer)
self.scene_absolute_number = get_scene_absolute_numbering(
self.show.indexerid,
self.show.indexer,
......@@ -631,8 +627,6 @@ class TVEpisode(object):
self.episode = try_int(epDetails.findtext('episode'))
self.season = try_int(epDetails.findtext('season'))
xem_refresh(self.show.indexerid, self.show.indexer)
self.scene_absolute_number = get_scene_absolute_numbering(
self.show.indexerid,
self.show.indexer,
......
......@@ -65,7 +65,6 @@ from sickrage.core.helpers.browser import foldersAtPath
from sickrage.core.helpers.compat import cmp
from sickrage.core.helpers.srdatetime import srDateTime
from sickrage.core.imdb_popular import imdbPopular
from sickrage.core.media.util import showImage
from sickrage.core.nameparser import validator
from sickrage.core.queues.search import BacklogQueueItem, FailedQueueItem, \
MANUAL_SEARCH_HISTORY, ManualSearchQueueItem
......@@ -73,7 +72,7 @@ from sickrage.core.scene_exceptions import get_scene_exceptions, update_scene_ex
from sickrage.core.scene_numbering import get_scene_absolute_numbering, \
get_scene_absolute_numbering_for_show, get_scene_numbering, \
get_scene_numbering_for_show, get_xem_absolute_numbering_for_show, \
get_xem_numbering_for_show, set_scene_numbering, xem_refresh
get_xem_numbering_for_show, set_scene_numbering
from sickrage.core.traktapi import srTraktAPI
from sickrage.core.tv.episode import TVEpisode
from sickrage.core.tv.show.coming_episodes import ComingEpisodes
......@@ -510,19 +509,8 @@ class WebRoot(WebHandler):
return self.redirect('/logout/')
def quicksearch_json(self, term):
data = []
for s in sorted(sickrage.app.showlist, key=lambda k: k.name):
if term.lower() not in s.name.lower():
continue
data += [{
'id': s.indexerid,
'name': s.name,
'img': sickrage.app.config.web_root + showImage(s.indexerid, 'poster_thumb').url
}]
return json_encode(data)
return json_encode(
sickrage.app.quicksearch_cache.get_shows(term) + sickrage.app.quicksearch_cache.get_episodes(term))
@Route('/ui(/?.*)')
......@@ -1517,12 +1505,12 @@ class Home(WebHandler):
except CantUpdateShowException as e:
errors.append(_("Unable to force an update on scene exceptions of the show."))
if do_update_scene_numbering:
try:
xem_refresh(showObj.indexerid, showObj.indexer)
time.sleep(cpu_presets[sickrage.app.config.cpu_preset])
except CantUpdateShowException as e:
errors.append(_("Unable to force an update on scene numbering of the show."))
# if do_update_scene_numbering:
# try:
# xem_refresh(showObj.indexerid, showObj.indexer)
# time.sleep(cpu_presets[sickrage.app.config.cpu_preset])
# except CantUpdateShowException as e:
# errors.append(_("Unable to force an update on scene numbering of the show."))
if directCall:
return map(str, errors)
......@@ -3834,7 +3822,7 @@ class ConfigGeneral(Config):
sickrage.app.config.fuzzy_dating = checkbox_to_value(fuzzy_dating)
sickrage.app.config.trim_zero = checkbox_to_value(trim_zero)
#sickrage.app.config.change_web_external_port(web_external_port)
# sickrage.app.config.change_web_external_port(web_external_port)
if date_preset:
sickrage.app.config.date_preset = date_preset
......
......@@ -118,7 +118,7 @@
style="width:350px;border: none;" type="search">
<div class="input-group-append">
<div class="input-group-text bg-dark" style="border: none">
<i id="quicksearchcancel" class="fas fa-times d-none"></i>
<i id="quicksearch-icon"></i>
</div>
</div>
</div>
......
......@@ -133,6 +133,95 @@ $(document).ready(function ($) {
return uri + hash;
},
quicksearch: function () {
$.widget("custom.catcomplete", $.ui.autocomplete, {
_create: function () {
this._super();
this.widget().menu("option", "items", "> :not(.ui-autocomplete-category)");
},
_renderMenu: function (ul, items) {
var that = this,
currentCategory = "";
$.each(items, function (index, item) {
var li;
if (item.category != currentCategory) {
ul.append("<li class='ui-autocomplete-category text-warning p-1'><strong class='text-uppercase'>" + item.category + "</strong></li>");
currentCategory = item.category;
}
li = that._renderItemData(ul, item);
if (item.category) {
li.attr("aria-label", item.category + " : " + item.name);
}
});
},
_renderItem: function (ul, item) {
var $li = $('<li class="bg-transparent m-1">'),
$img = $('<img>'),
$a = $('<a>');
$li.attr('data-value', item.name);
$img.attr({
src: item.img,
alt: item.name,
class: 'img-fluid w-100'
});
if (item.category === 'shows') {
$a.attr({
href: `${SICKRAGE.srWebRoot}/home/displayShow?show=${item.showid}`,
class: 'btn btn-dark btn-block text-left'
});
$li.append($a);
$li.find('a').append($('<div class="row"><div id="show-img" class="col-2"></div><div class="col-10"><div class="row"><strong id="show-name" class="text-white text-truncate"></strong></div><div class="row"><strong id="show-seasons" class="text-secondary"></strong></div></div></div>'));
$li.find('#show-img').append($img);
$li.find('#show-name').append(item.name);
$li.find('#show-seasons').append(item.seasons + ' seasons');
} else {
$a.attr({
href: `${SICKRAGE.srWebRoot}/home/displayShow?show=${item.showid}#S${item.season}E${item.episode}`,
class: 'btn btn-dark btn-block text-left'
});
$li.append($a);
$li.find('a').append($('<div class="row"><div id="show-img" class="col-2"></div><div class="col-10"><div class="row"><strong id="ep-name" class="text-white"></strong></div><div class="row"><strong id="show-name" class="text-secondary text-truncate"></strong></div></div></div>'));
$li.find('#show-img').append($img);
$li.find('#ep-name').append(item.name);
$li.find('#show-name').append(item.showname);
}
return $li.appendTo(ul);
}
});
$('#quicksearch').catcomplete({
minLength: 1,
source: `${SICKRAGE.srWebRoot}/quicksearch.json`,
// search: function () {
// $("#quicksearch-icon").addClass('fas fa-spinner fa-spin');
// },
focus: function (event, ui) {
$('#quicksearch').val(ui.item.name);
return false;
},
open: function () {
//$("#quicksearch-icon").removeClass('fas fa-spinner fa-spin');
$("#quicksearch-icon").addClass('fas fa-times');
$("ul.ui-menu").width($(this).innerWidth());
$("ul.ui-menu").css('border', 'none');
$("ul.ui-menu").css('outline', 'none');
$("ul.ui-menu").addClass('bg-dark shadow rounded');
}
});
$("#quicksearch-icon").click(function () {
$('#quicksearch').catcomplete('close').val('');
$("#quicksearch-icon").removeClass();
});
},
common: {
init: function () {
SICKRAGE.srPID = SICKRAGE.getMeta('srPID');
......@@ -149,53 +238,8 @@ $(document).ready(function ($) {
}
});
$('#quicksearch').autocomplete({
minLength: 1,
source: `${SICKRAGE.srWebRoot}/quicksearch.json`,
search: function () {
$("#quicksearchcancel").removeClass('d-none');
},
focus: function (event, ui) {
$('#quicksearch').val(ui.item.name);
return false;
},
open: function () {
$("ul.ui-menu").width($(this).innerWidth());
$("ul.ui-menu").css('border', 'none');
$("ul.ui-menu").css('outline', 'none');
$("ul.ui-menu").addClass('bg-dark shadow rounded');
}
});
$('#quicksearch').data("ui-autocomplete")._renderItem = function (ul, item) {
var $li = $('<li class="bg-transparent m-1">'),
$img = $('<img>'),
$a = $('<a>');
$a.attr({
href: `${SICKRAGE.srWebRoot}/home/displayShow?show=${item.id}`,
class: 'btn btn-dark btn-block text-left text-white'
});
$img.attr({
src: item.img,
alt: item.name,
class: 'm-1',
style: 'width:10%;height:100%'
});
$li.attr('data-value', item.name);
$li.append($a);
$li.find('a').append($img).append(item.name);
return $li.appendTo(ul);
};
$("#quicksearchcancel").click(function () {
$('#quicksearch').autocomplete('close').val('');
$("#quicksearchcancel").addClass('d-none');
});
// init quicksearch
SICKRAGE.quicksearch();
$(window).scroll(function () {
if ($(this).scrollTop() > 50) {
......
......@@ -343,6 +343,16 @@ span.snatched b {
}
}
.ui-autocomplete {
max-height: 500px;
overflow-y: auto;
overflow-x: hidden;
}
* html .ui-autocomplete {
height: 500px;
}
#popover-target label {
margin: 0 5px;
display: block;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment