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)
+
@@ -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