From 4a1a9eea60fc8111247ab97c54182d4c3257e354 Mon Sep 17 00:00:00 2001 From: JackDandy Date: Sat, 7 Aug 2021 00:00:07 +0100 Subject: [PATCH] Change use diskcache instead of memcache for TVmaze cards. Change disable AKAs for TVmaze cards since each calls the API and are not needed here. Fix handling of card filters and sort states. --- CHANGES.md | 2 + gui/slick/interfaces/default/home_browseShows.tmpl | 24 +++++----- lib/api_tvmaze/tvmaze_api.py | 56 +++++++++++++++------- lib/tvinfo_base/base.py | 5 +- sickbeard/webserve.py | 42 +++++++--------- 5 files changed, 72 insertions(+), 57 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index b36fd34..55e55b6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -83,6 +83,7 @@ * Add show name/networks card user input filter * Change only auto refresh card view if a recoverable error occurs * Update Requests library 2.25.1 (bdc00eb) to 2.26.0 (b0e025a) +* Fix handling of card filters and sort states [develop changelog] @@ -93,6 +94,7 @@ * Fix genre field in tvmaze_api * Change api interface folders to api_* to fix legacy tmdb_api folder cleanup on new installs * Fix TVmaze genres property to be lowercase string +* Change use diskcache instead of memcache for TVmaze cards ### 0.24.15 (2021-08-05 11:45:00 UTC) diff --git a/gui/slick/interfaces/default/home_browseShows.tmpl b/gui/slick/interfaces/default/home_browseShows.tmpl index 8ae6865..ebfcc7e 100644 --- a/gui/slick/interfaces/default/home_browseShows.tmpl +++ b/gui/slick/interfaces/default/home_browseShows.tmpl @@ -102,7 +102,7 @@ $(document).ready(function(){ }); #raw $('#showsort').on('change', function(){ - var sortCriteria = this.value.replace('by_', ''), el$ = $('#container'), shuffle = !1; + var elValue = this.value, sortCriteria = elValue.replace('by_', ''), el$ = $('#container'), shuffle = !1; switch (sortCriteria) { case 'asc': case 'desc': @@ -119,15 +119,13 @@ $(document).ready(function(){ #end if break; #if $use_ratings - case 'rating': - shuffle = !0; - break; #if $use_votes case 'rating_votes': sortCriteria = ['rating', 'votes']; + #end if + case 'rating': shuffle = !0; break; - #end if #end if default: sortCriteria = 'name' @@ -140,21 +138,21 @@ $(document).ready(function(){ option$ = showSort.filter('option[value="asc"].selected, option[value="desc"].selected'); option$.removeClass('selected').removeAttr('selected').html(option$.text().replace(/^>\s/i, '')); - option$ = showSort.filter($('option[value="' + this.value + '"]')); + option$ = showSort.filter($('option[value="' + elValue + '"]')); option$.addClass('selected').attr('selected', !0).html('> ' + option$.text()); savePrefs(); el$.one('layoutComplete', llUpdate); el$.isotope({ - sortAscending: 'asc' === this.value, + sortAscending: 'asc' === elValue, sortBy: showSort.filter($('option[value^="by_"][class*="selected"]')).val().replace('by_', '') }); - } else if(0 == this.value.indexOf('by_')){ + } else if(0 == elValue.indexOf('by_')){ option$ = showSort.filter($('option[value^="by_"][class*="selected"]')); option$.removeClass('selected').removeAttr('selected').html(option$.text().replace(/^>\s/i, '')); - option$ = showSort.filter($('option[value="' + this.value + '"]')); + option$ = showSort.filter($('option[value="' + elValue + '"]')); option$.addClass('selected').attr('selected', !0).html('> ' + option$.text()); savePrefs(); @@ -184,12 +182,12 @@ $(document).ready(function(){ option$ = showSort.filter($('option[value^="*"].selected, #showsort option[value^="."].selected')); option$.removeClass('selected').removeAttr('selected').html(option$.text().replace(/^>\s/i, '')); - option$ = showSort.filter($('option[value="' + this.value + '"]')); + option$ = showSort.filter($('option[value="' + elValue + '"]')); option$.addClass('selected').attr('selected', !0).html('> ' + option$.text()); savePrefs(); - var showCards = $('.show-card'), filter = this.value; + var showCards = $('.show-card'), filter = elValue; if('.hide' === filter){ showCards.filter($('.hide')).removeClass('hide').addClass('to-hide'); filter = '.to-hide'; @@ -278,7 +276,7 @@ $(document).ready(function(){ } }); }); - $('.resetshows').click(function() { + $('.resetshows').click(function() { var input = $('#search_show_name'); if ('' !== input.val()){ input.val('').trigger('input').change(); @@ -324,7 +322,7 @@ $(document).ready(function(){ -# omit for TVMaze as Original == First Aired +## omit for TVMaze as original order == First Aired #if 'TVmaze' not in $browse_type #end if diff --git a/lib/api_tvmaze/tvmaze_api.py b/lib/api_tvmaze/tvmaze_api.py index 55e0063..78d04f5 100644 --- a/lib/api_tvmaze/tvmaze_api.py +++ b/lib/api_tvmaze/tvmaze_api.py @@ -14,6 +14,7 @@ import requests from urllib3.util.retry import Retry from requests.adapters import HTTPAdapter +from _23 import filter_iter from six import integer_types, iteritems, string_types from sg_helpers import get_url, try_int from lib.dateutil.parser import parser @@ -549,18 +550,16 @@ class TvMaze(TVInfoBase): if p: return self._convert_person(p) - def get_premieres(self, result_count=100, get_extra_images=False, **kwargs): + def get_premieres(self, **kwargs): # type: (...) -> List[TVInfoEpisode] - return self._filtered_schedule(lambda e: all([1 == e.season_number, 1 == e.episode_number]), - get_images=get_extra_images) + return self._filtered_schedule(**kwargs).get('premieres') - def get_returning(self, result_count=100, get_extra_images=False, **kwargs): + def get_returning(self, **kwargs): # type: (...) -> List[TVInfoEpisode] - return self._filtered_schedule(lambda e: all([1 != e.season_number, 1 == e.episode_number]), - get_images=get_extra_images) + return self._filtered_schedule(**kwargs).get('returning') - def _make_episode(self, episode_data, show_data=None, get_images=False): - # type: (TVMazeEpisode, TVMazeShow, bool) -> TVInfoEpisode + def _make_episode(self, episode_data, show_data=None, get_images=False, get_akas=False): + # type: (TVMazeEpisode, TVMazeShow, bool, bool) -> TVInfoEpisode """ make out of TVMazeEpisode object and optionally TVMazeShow a TVInfoEpisode """ @@ -581,7 +580,8 @@ class TvMaze(TVInfoBase): ti_show.show_type = show_data.type ti_show.lastupdated = show_data.updated ti_show.poster = show_data.image and show_data.image.get('original') - ti_show.aliases = [a.name for a in show_data.akas] + if get_akas: + ti_show.aliases = [a.name for a in show_data.akas] if 'days' in show_data.schedule: ti_show.airs_dayofweek = ', '.join(show_data.schedule['days']) network = show_data.network or show_data.web_channel @@ -656,12 +656,32 @@ class TvMaze(TVInfoBase): ti_episode.show = ti_show return ti_episode - def _filtered_schedule(self, condition, get_images=False): - try: - result = sorted([ - e for e in tvmaze.get_full_schedule() - if condition(e) and (None is e.show.language or re.search('(?i)eng|jap', e.show.language))], - key=lambda x: x.show.premiered or x.airstamp) - return [self._make_episode(r, r.show, get_images) for r in result] - except(BaseException, Exception): - return [] + def _filtered_schedule(self, **kwargs): + cache_name_key = 'tvmaze_schedule' + is_none, schedule = self._get_cache_entry(cache_name_key) + if None is schedule and not is_none: + schedule = [] + try: + schedule = tvmaze.get_full_schedule() + except(BaseException, Exception): + pass + + premieres = [] + returning = [] + rc_lang = re.compile('(?i)eng|jap') + for cur_show in filter_iter(lambda s: 1 == s.episode_number and ( + None is s.show.language or rc_lang.search(s.show.language)), schedule): + if 1 == cur_show.season_number: + premieres += [cur_show] + else: + returning += [cur_show] + + premieres = [self._make_episode(r, r.show, **kwargs) + for r in sorted(premieres, key=lambda e: e.show.premiered)] + returning = [self._make_episode(r, r.show, **kwargs) + for r in sorted(returning, key=lambda e: e.airstamp)] + + schedule = dict(premieres=premieres, returning=returning) + self._set_cache_entry(cache_name_key, schedule, expire=self.schedule_cache_expire) + + return schedule diff --git a/lib/tvinfo_base/base.py b/lib/tvinfo_base/base.py index 1b3c675..82bd80b 100644 --- a/lib/tvinfo_base/base.py +++ b/lib/tvinfo_base/base.py @@ -807,6 +807,7 @@ class TVInfoBase(object): self.diskcache = diskcache.Cache(directory=self._cachedir, disk_pickle_protocol=2) # type: diskcache.Cache self.cache_expire = 60 * 60 * 18 # type: integer_types self.search_cache_expire = 60 * 15 # type: integer_types + self.schedule_cache_expire = 60 * 30 # type: integer_types self.config = { 'apikey': '', 'debug_enabled': False, @@ -1168,14 +1169,14 @@ class TVInfoBase(object): # type: (...) -> List[TVInfoEpisode] return [] - def get_premieres(self, result_count=100, **kwargs): + def get_premieres(self, **kwargs): # type: (...) -> List[TVInfoEpisode] """ get all premiering shows """ return [] - def get_returning(self, result_count=100, get_extra_images=False, **kwargs): + def get_returning(self, **kwargs): # type: (...) -> List[TVInfoShow] """ get all returning shows diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py index b459355..6ba39ad 100644 --- a/sickbeard/webserve.py +++ b/sickbeard/webserve.py @@ -5187,28 +5187,19 @@ class AddShows(Home): footnote = None filtered = [] - def card_cache(mem_key): - # noinspection PyProtectedMember - from lib.dateutil.tz.tz import _datetime_to_timestamp - - now = int(_datetime_to_timestamp(datetime.datetime.now())) - if (sickbeard.MEMCACHE.get(mem_key, {}).get('data') - and (now < sickbeard.MEMCACHE.get(mem_key, {}).get('expire', 0))): - return sickbeard.MEMCACHE.get(mem_key).get('data') - - tvinfo_config = sickbeard.TVInfoAPI(TVINFO_TVMAZE).api_params.copy() - t = sickbeard.TVInfoAPI(TVINFO_TVMAZE).setup(**tvinfo_config) - if 'prem' in mem_key: - data = t.get_premieres() - else: - data = t.get_returning() - sickbeard.MEMCACHE[mem_key] = dict(expire=(30*60) + now, data=data) - return data - + tvinfo_config = sickbeard.TVInfoAPI(TVINFO_TVMAZE).api_params.copy() + t = sickbeard.TVInfoAPI(TVINFO_TVMAZE).setup(**tvinfo_config) if 'New' in browse_title: - episodes = card_cache('tvmaze_premiere') + episodes = t.get_premieres() else: - episodes = card_cache('tvmaze_returning') + episodes = t.get_returning() + + # handle switching between returning and premieres + sickbeard.BROWSELIST_MRU.setdefault(browse_type, dict()) + showfilter = ('by_returning', 'by_premiered')['New' in browse_title] + saved_showsort = sickbeard.BROWSELIST_MRU[browse_type].get('tvm_%s' % kwargs['mode']) or '*,asc' + showsort = saved_showsort + (',%s' % showfilter, '')[3 == len(saved_showsort.split(','))] + sickbeard.BROWSELIST_MRU[browse_type].update(dict(showfilter=showfilter, showsort=showsort)) oldest, newest, oldest_dt, newest_dt, use_networks = None, None, 9999999, 0, False dedupe = [] @@ -5680,8 +5671,12 @@ class AddShows(Home): save_config = False if browse_type in ('AniDB', 'IMDb', 'Metacritic', 'Trakt', 'TVCalendar', 'TVmaze', 'Nextepisode'): save_config = True - sickbeard.BROWSELIST_MRU[browse_type] = dict( - showfilter=kwargs.get('showfilter', ''), showsort=kwargs.get('showsort', '')) + if browse_type in ('TVmaze',) and kwargs.get('showfilter') and kwargs.get('showsort'): + sickbeard.BROWSELIST_MRU.setdefault(browse_type, dict()) \ + .update({kwargs.get('showfilter'): kwargs.get('showsort')}) + else: + sickbeard.BROWSELIST_MRU[browse_type] = dict( + showfilter=kwargs.get('showfilter', ''), showsort=kwargs.get('showsort', '')) if save_config: sickbeard.save_config() return json.dumps({'success': save_config}) @@ -5700,8 +5695,7 @@ class AddShows(Home): showsort = t.saved_showsort.split(',') t.saved_showsort_sortby = 3 == len(showsort) and showsort[2] or 'by_order' t.reset_showsort_sortby = ('votes' in t.saved_showsort_sortby and not kwargs.get('use_votes', True) - or 'rating' in t.saved_showsort_sortby and not kwargs.get('use_ratings', True) - or 'returning' in t.saved_showsort_sortby and 'Returning' not in browse_title) + or 'rating' in t.saved_showsort_sortby and not kwargs.get('use_ratings', True)) t.is_showsort_desc = ('desc' == (2 <= len(showsort) and showsort[1] or 'asc')) and not t.reset_showsort_sortby t.saved_showsort_view = 1 <= len(showsort) and showsort[0] or '*' t.all_shows = []