From a6122aae66e4f70d60a35aa905465f391ef9ea3c Mon Sep 17 00:00:00 2001 From: Prinz23 Date: Tue, 30 May 2023 22:28:56 +0100 Subject: [PATCH] Add search on TVmaze, TMDb, or Trakt for other shows with the actor that is viewed on Person page. --- gui/slick/interfaces/default/cast_person.tmpl | 10 ++ .../interfaces/default/home_browseShows.tmpl | 15 ++- lib/api_trakt/indexerapiinterface.py | 5 + sickgear/webserve.py | 122 ++++++++++++++++-- 4 files changed, 141 insertions(+), 11 deletions(-) diff --git a/gui/slick/interfaces/default/cast_person.tmpl b/gui/slick/interfaces/default/cast_person.tmpl index 3d9b9568..a9c1c9a0 100644 --- a/gui/slick/interfaces/default/cast_person.tmpl +++ b/gui/slick/interfaces/default/cast_person.tmpl @@ -2,6 +2,7 @@ #import re #import sickgear #from sickgear import TVInfoAPI +#from sickgear.indexers.indexer_config import TVINFO_TMDB, TVINFO_TRAKT, TVINFO_TVMAZE #from sickgear.helpers import anon_url #from sickgear.tv import PersonGenders #from sg_helpers import spoken_height @@ -207,6 +208,15 @@ def param(visible=True, rid=None, cache_person=None, cache_char=None, person=Non +#end if +#if $person.ids.get($TVINFO_TRAKT) +
Search Shows on Trake +#end if +#if $person.ids.get($TVINFO_TMDB) +
Search Shows on TMDB
+#end if +#if $person.ids.get($TVINFO_TVMAZE) +
Search Shows on TVMaze
#end if diff --git a/gui/slick/interfaces/default/home_browseShows.tmpl b/gui/slick/interfaces/default/home_browseShows.tmpl index 5429bdb2..046183d7 100644 --- a/gui/slick/interfaces/default/home_browseShows.tmpl +++ b/gui/slick/interfaces/default/home_browseShows.tmpl @@ -3,6 +3,7 @@ #from sickgear import WEB_ROOT, THEME_NAME #from sickgear.common import * #from sickgear.helpers import anon_url, try_float +#from lib.tvinfo_base import RoleTypes #from _23 import quote <% def sg_var(varname, default=False): return getattr(sickgear, varname, default) %>#slurp# <% def sg_str(varname, default=''): return getattr(sickgear, varname, default) %>#slurp# @@ -10,7 +11,7 @@ #set $mode = $kwargs and $kwargs.get('mode', '') #set $use_network = $kwargs.get('use_networks', False) #set $use_returning = 'returning' == mode -#set $use_filter = $kwargs and $kwargs.get('use_filter', True) +#set $use_filter = $kwargs and $kwargs.get('use_filter', True) and not $p_ref #set $use_ratings = $kwargs and $kwargs.get('use_ratings', True) #set $use_votes = $kwargs and $kwargs.get('use_votes', True) #set $term_vote = $kwargs and $kwargs.get('term_vote', 'Votes') @@ -463,6 +464,11 @@ $(document).ready(function(){   +#if $p_ref +
+ +
+#end if

$browse_title

#if $kwargs and $kwargs.get('oldest')
@@ -530,6 +536,13 @@ $(document).ready(function(){
#echo ((re.sub(r'^((?:A(?!\s+to)n?)|The)\s(\w)', r'\1 \2', $this_show['title']), $this_show['title'])[$sg_var('SORT_ARTICLE')], ' ')['' == $this_show['title']]#
+ #if $this_show.get('p_chars') +
+ #for $char in $this_show['p_chars'] +
as $char[0]#if $RoleTypes.ActorMain != $char[1]# ($char[2]/$char[3] eps)#end if#
+ #end for +
+ #end if #if 'Ani' not in $browse_type #end if diff --git a/lib/api_trakt/indexerapiinterface.py b/lib/api_trakt/indexerapiinterface.py index 9a0096c9..7b279dbd 100644 --- a/lib/api_trakt/indexerapiinterface.py +++ b/lib/api_trakt/indexerapiinterface.py @@ -321,6 +321,11 @@ class TraktIndexer(TVInfoBase): ti_show.imdb_id = c['show']['ids'].get('imdb') ti_show.runtime = c['show']['runtime'] ti_show.genre_list = c['show']['genres'] + ti_show.slug = c['show'].get('ids', {}).get('slug') + ti_show.language = c['show'].get('language') + ti_show.network_country = c['show'].get('country') + ti_show.rating = c['show'].get('rating') + ti_show.vote_count = c['show'].get('votes') for ch in c.get('characters') or []: _ti_character = TVInfoCharacter(name=ch, regular=c.get('series_regular'), ti_show=ti_show, person=[result], diff --git a/sickgear/webserve.py b/sickgear/webserve.py index 1e9ba311..541fa2c4 100644 --- a/sickgear/webserve.py +++ b/sickgear/webserve.py @@ -94,7 +94,7 @@ except ImportError as e: from lib.fuzzywuzzy import fuzz from lib.api_trakt import TraktAPI from lib.api_trakt.exceptions import TraktException, TraktAuthException -from lib.tvinfo_base import TVInfoEpisode +from lib.tvinfo_base import TVInfoEpisode, RoleTypes from lib.tvinfo_base.base import tv_src_names import lib.rarfile.rarfile as rarfile @@ -108,7 +108,7 @@ if False: from typing import Any, AnyStr, Dict, List, Optional, Set, Tuple, Union from sickgear.providers.generic import TorrentProvider # prevent pyc TVInfoBase resolution by typing the derived used class to TVInfoAPI instantiation - from lib.tvinfo_base import TVInfoBase, TVInfoShow + from lib.tvinfo_base import TVInfoBase, TVInfoCharacter, TVInfoPerson, TVInfoShow # from api_imdb.imdb_api import IMDbIndexer from api_tmdb.tmdb_api import TmdbIndexer from api_trakt.indexerapiinterface import TraktIndexer @@ -4324,12 +4324,15 @@ class AddShows(Home): return json_dumps({'results': final_results}) @staticmethod - def _make_cache_image_url(iid, show_info, default_transparent_img=True): + def _make_cache_image_url(iid, show_info, default_transparent_img=True, use_source_id=False): img_url = '' trans_param = ('1', '0')[not default_transparent_img] if TVINFO_TRAKT == iid: img_url = 'imagecache?path=browse/thumb/trakt&filename=%s&trans=%s&tmdbid=%s&tvdbid=%s' % \ ('%s.jpg' % show_info['ids'].trakt, trans_param, show_info['ids'].tmdb, show_info['ids'].tvdb) + elif use_source_id and TVINFO_TVMAZE == iid: + img_url = 'imagecache?path=browse/thumb/tvmaze&filename=%s&trans=%s&tvmazeid=%s' % \ + ('%s.jpg' % show_info['ids'].tvmaze, trans_param, show_info['ids'].tvmaze) elif iid in (TVINFO_TVDB, TVINFO_TVMAZE, TVINFO_TMDB) and show_info.get('poster'): img_url = 'imagecache?path=browse/thumb/%s&filename=%s&trans=%s&source=%s' % \ (tv_src_names[iid], '%s.jpg' % show_info['id'], trans_param, show_info['poster']) @@ -5342,6 +5345,10 @@ class AddShows(Home): return self.browse_tmdb( 'Trending this week at TMDB', mode='trending_week', **kwargs) + def tmdb_person(self, person_tmdb_id=None, **kwargs): + return self.browse_tmdb( + 'Person at TMDB', mode='get_person', p_id=person_tmdb_id, **kwargs) + def browse_tmdb(self, browse_title, **kwargs): browse_type = 'TMDB' @@ -5349,6 +5356,7 @@ class AddShows(Home): footnote = None filtered = [] + p_ref = None tvid = TVINFO_TMDB tvinfo_config = sickgear.TVInfoAPI(tvid).api_params.copy() @@ -5361,6 +5369,21 @@ class AddShows(Home): items = t.get_trending() elif 'trending_week' == mode: items = t.get_trending(time_window='week') + elif 'get_person' == mode: + items = [] + p_item = t.get_person(get_show_credits=True, **kwargs) # type: TVInfoPerson + if p_item: + p_ref = f'{TVINFO_TMDB}:{p_item.id}' + dup = {} # type: Dict[int, TVInfoShow] + for c in p_item.characters: # type: TVInfoCharacter + if c.ti_show.id not in dup: + dup[c.ti_show.id] = c.ti_show + items.append(c.ti_show) + else: + dup[c.ti_show.id].cast.update(c.ti_show.cast) + del dup + else: + p_item = None else: items = t.discover() @@ -5393,6 +5416,9 @@ class AddShows(Home): language = ((cur_show_info.language and 'jap' in cur_show_info.language.lower()) and 'jp' or 'en') filtered.append(dict( + p_ref=p_ref, + p_chars=[(ch.name, r_t, RoleTypes.reverse[r_t], ch.episode_count) + for r_t in cur_show_info.cast or [] for ch in cur_show_info.cast[r_t]], ord_premiered=ord_premiered, str_premiered=str_premiered, started_past=started_past, @@ -5533,6 +5559,8 @@ class AddShows(Home): mode = kwargs.get('mode', '') items, filtered = ([], []) error_msg = None + p_item = None + p_ref = None tvid = TVINFO_TRAKT tvinfo_config = sickgear.TVInfoAPI(tvid).api_params.copy() t = sickgear.TVInfoAPI(tvid).setup(**tvinfo_config) # type: Union[TraktIndexer, TVInfoBase] @@ -5564,6 +5592,21 @@ class AddShows(Home): if not items: error_msg = 'No items in watchlist. Use the "Add to watchlist" button at the Trakt website' raise ValueError(error_msg) + elif 'get_person' == api_method: + items = [] + p_item = t.get_person(get_show_credits=True, **kwargs) # type: TVInfoPerson + if p_item: + p_ref = f'{TVINFO_TRAKT}:{p_item.id}' + dup = {} # type: Dict[int, TVInfoShow] + for c in p_item.characters: # type: TVInfoCharacter + if c.ti_show.id not in dup: + dup[c.ti_show.id] = c.ti_show + items.append(c.ti_show) + else: + dup[c.ti_show.id].cast.update(c.ti_show.cast) + del dup + else: + p_item = None else: items = t.get_trending() except TraktAuthException as e: @@ -5603,9 +5646,10 @@ class AddShows(Home): except(BaseException, Exception): episode_info = TVInfoEpisode() - if rx_ignore.search(cur_show_info.seriesname.strip()) or \ - not (language_en or country_ok) or \ - not (cur_show_info.overview or episode_info.overview): + if 'get_person' != api_method and \ + (rx_ignore.search(cur_show_info.seriesname.strip()) or + not (language_en or country_ok) or + not (cur_show_info.overview or episode_info.overview)): continue try: ord_premiered, str_premiered, started_past, oldest_dt, newest_dt, oldest, newest, \ @@ -5618,6 +5662,10 @@ class AddShows(Home): images = {} if not image else dict(poster=dict(thumb=image)) filtered.append(dict( + p_ref=p_ref, + p_item=p_item, + p_chars=[(ch.name, r_t, RoleTypes.reverse[r_t], ch.episode_count) + for r_t in cur_show_info.cast or [] for ch in cur_show_info.cast[r_t]], ord_premiered=ord_premiered, str_premiered=str_premiered, ord_returning=ord_returning, @@ -5653,6 +5701,16 @@ class AddShows(Home): return filtered, oldest, newest + def trakt_person(self, person_trakt_id=None): + + return self.browse_trakt( + 'get_person', + 'Person on Trakt', + mode='person', + footnote='Note; Expect default placeholder images in this list', + p_id=person_trakt_id + ) + def browse_trakt(self, api_method, browse_title, **kwargs): browse_type = 'Trakt' @@ -5670,9 +5728,12 @@ class AddShows(Home): error_msg = 'No items in watchlist. Use the "Add to watchlist" button at the Trakt website' return self.browse_shows(browse_type, browse_title, filtered, error_msg=error_msg, show_header=1, **kwargs) + if 'get_person' == api_method and filtered: + browse_title = f'{getattr(filtered[0]["p_item"], "name", "")} (Person) on Trakt' + kwargs.update(dict(oldest=oldest, newest=newest, error_msg=error_msg, use_networks=use_networks)) - if 'recommended' not in mode and 'watchlist' not in mode: + if not any(m in mode for m in ('recommended', 'watchlist', 'person')): mode = mode.split('-') if mode: func = 'trakt_%s' % mode[0] @@ -5923,6 +5984,10 @@ class AddShows(Home): return self.browse_tvm( 'Returning at TVmaze', mode='returning', **kwargs) + def tvm_person(self, person_tvm_id=None, **kwargs): + return self.browse_tvm( + 'Person at TVmaze', mode='get_person', p_id=person_tvm_id, **kwargs) + def browse_tvm(self, browse_title, **kwargs): browse_type = 'TVmaze' @@ -5930,12 +5995,28 @@ class AddShows(Home): footnote = None filtered = [] + p_ref = None tvid = TVINFO_TVMAZE tvinfo_config = sickgear.TVInfoAPI(tvid).api_params.copy() t = sickgear.TVInfoAPI(tvid).setup(**tvinfo_config) # type: Union[TvmazeIndexer, TVInfoBase] if 'premieres' == mode: items = t.get_premieres() + elif 'get_person' == mode: + items = [] + p_item = t.get_person(get_show_credits=True, **kwargs) # type: TVInfoPerson + if p_item: + p_ref = f'{TVINFO_TVMAZE}:{p_item.id}' + dup = {} # type: Dict[int, TVInfoShow] + for c in p_item.characters: # type: TVInfoCharacter + if c.ti_show.id not in dup: + dup[c.ti_show.id] = c.ti_show + items.append(c.ti_show) + else: + dup[c.ti_show.id].cast.update(c.ti_show.cast) + del dup + else: + p_item = None else: items = t.get_returning() @@ -5967,7 +6048,7 @@ class AddShows(Home): if 'returning' == mode and not ok_returning: continue - image = self._make_cache_image_url(tvid, cur_show_info) + image = self._make_cache_image_url(tvid, cur_show_info, use_source_id='get_person' == mode) images = {} if not image else dict(poster=dict(thumb=image)) network_name = cur_show_info.network @@ -5981,6 +6062,9 @@ class AddShows(Home): language = (('jap' in (cur_show_info.language or '').lower()) and 'jp' or 'en') filtered.append(dict( + p_ref=p_ref, + p_chars=[(ch.name, r_t, RoleTypes.reverse[r_t], ch.episode_count) + for r_t in cur_show_info.cast or [] for ch in cur_show_info.cast[r_t]], ord_premiered=ord_premiered, str_premiered=str_premiered, ord_returning=ord_returning, @@ -5990,7 +6074,7 @@ class AddShows(Home): episode_number=episode_info.episodenumber or '', episode_overview=helpers.xhtml_escape(episode_info.overview[:250:]).strip(), episode_season=getattr(episode_info.season, 'number', episode_info.seasonnumber), - genres=(cur_show_info.genre.strip('|').replace('|', ', ') + genres=((cur_show_info.genre or '').strip('|').replace('|', ', ') or ', '.join(cur_show_info.show_type) or ''), ids=cur_show_info.ids.__dict__, images=images, @@ -6106,6 +6190,7 @@ class AddShows(Home): t.submenu = self.home_menu() t.browse_type = browse_type t.browse_title = browse_title + t.p_ref = (0 < len(shows) and shows[0].get('p_ref')) or None t.saved_showfilter = sickgear.BROWSELIST_MRU.get(browse_type, {}).get('showfilter', '') t.saved_showsort = sickgear.BROWSELIST_MRU.get(browse_type, {}).get('showsort', '*,asc,by_order') showsort = t.saved_showsort.split(',') @@ -9863,7 +9948,7 @@ class CachedImages(MainHandler): for f in ['tmdb', 'tvdb', 'tvmaze']: CachedImages.delete_dummy_image('%s.%s.dummy' % (os.path.splitext(filename)[0], f)) - def index(self, path='', source=None, filename=None, tmdbid=None, tvdbid=None, trans=True): + def index(self, path='', source=None, filename=None, tmdbid=None, tvdbid=None, trans=True, tvmazeid=None): path = path.strip('/') file_name = '' @@ -9878,8 +9963,25 @@ class CachedImages(MainHandler): helpers.make_path(basepath) poster_url = '' tmdb_image = False + tvmaze_image = False if None is not source and source in sickgear.CACHE_IMAGE_URL_LIST: poster_url = source + if None is source and tvmazeid not in [None, 'None', 0, '0'] \ + and self.should_try_image(image_file, 'tvmaze'): + tvmaze_image = True + try: + tvinfo_config = sickgear.TVInfoAPI(TVINFO_TVMAZE).api_params.copy() + t = sickgear.TVInfoAPI(TVINFO_TVMAZE).setup(**tvinfo_config) + show_obj = t.get_show(tvmazeid, load_episodes=False, posters=True) + if show_obj and show_obj.poster: + poster_url = show_obj.poster + except (BaseException, Exception): + poster_url = '' + if poster_url: + sg_helpers.download_file(poster_url, image_file, nocache=True) + if tvmaze_image and not os.path.isfile(image_file): + self.create_dummy_image(image_file, 'tvmaze') + if None is source and tmdbid not in [None, 'None', 0, '0'] \ and self.should_try_image(image_file, 'tmdb'): tmdb_image = True