mirror of
https://github.com/SickGear/SickGear.git
synced 2024-12-24 11:43:38 +00:00
Change improve tmdb_api, trakt_api, and TVInfoShow object.
Add `spoken_languages` to tmdb API and TVInfoShow object. Add `trailers`, `homepage` to trakt API and TVInfoShow object. Add trakt episode data if returned from api. Add trakt API methods. - get_most_played - get_most_watched - get_most_collected - get_anticipated - get_recommended - get_trending - get_popular - get_recommended_for_account - get_new_shows - get_new_seasons - get_watchlisted_for_account - get_similar - hide_recommended_for_account (to hide/remove recommended shows for account) - unhide_recommended_for_account - list_hidden_recommended_for_account Fix caching tmdb language list over different runtime instances. Add episode_count and fix ti_show in tmdb_api person object. Change set additional properties in get_person trakt_api. Add tmdb API methods and tvinfo_base. - get_recommended_for_show - get_similar --- fix supported language caching improve print output (source name) of tvinfo_api_tests fix tvinfo_api_tests data creation --- Add code so that it runs with all_test use mock today() and now() dates add option to only get new urls mock data try also to make object creation only when needed fix person parser in tmdb_api add search_person test in tvinfo_api_tests restore mocked methods at the end of the tvinfo_api_tests to prevent other tests to fail when called via all_tests switch gzip with better lzma compression for mock files (default lib in py3) move mock files in test unit sub folder --- Fix trakt method `get_recommended`. Fix browse trakt tests in tvinfo_api_tests. Change set episode id in trakt api. --- Add test_browse_endpoints to tvinfo_api_tests. --- Add enforce_type to sg_helpers. Change use enforce str for overviews. Change remove `if PY2` code sections Add support for datetime.time in _make_airtime in tv.py Refactor tvmaze_api show data setter. Change test to not allow None for seriesname. Add additional missing showdata with caller load_data(). Add load_data() to TVInfoShow. Add guestcast, guestcrew to episodes in pytvmaze lib. --- Change make seriesid of TVInfoShow a alias property of id. Add tvinfo tests. Add search tests. Add show, person tests. Change add trakt tests. Change add tmdb search tests. tvmaze_api exclude rating from mapping. Allow None for seriesname. Fix origin_countries in trakt_api search. Fix show_type in tvmaze_api. Fix airtime for episodes in tvmaze_api. --- Change switch to property instead of legacy dict-like use for trakt search results. Change optimize speed of get() function. Fix make BaseTVinfoSeasonnotfound and BaseTVinfoAttributenotfound also a subclass of AttributeError and KeyError. Change mock get() to work with and without default args just like dict get(). Change add language to tmdb_api search results. Change improve person search by remote id, by getting the complete persons data when there is only 1 result. Change trakt API search results to tvinfoshow. Change search results to TVInfoShow objs in tvmaze_api. Change simplify poster URL generation for search results. Change search results to TVInfoShow objs. Change add tvdb genre links to displayShow. Change workaround for missing data in person data (series set to None). Fix add show to characters of person if there is no name on IMDb (set to 'unknown name'). Change add config and icons for linkedin, reddit, wikidata, youtube. Add TVInfoIDs, TVInfoSocialIDs to Trakt. Add TVInfoIDs to tmdb_api. Add TVInfoIDs to tvmaze_api. add TVInfoIDs to imdb_api. Change make character name '' if None. Fix for 'unknown name' persons and characters. Add contentrating. Change fill in new fields to get_person results. ---- Change set new in/active dates to network. Change add active_date, inactive_date to TVInfoNetwork class. Change add default kwargs to tmdb discover method if no kwargs are set. Change default: English language shows with first air date greater then today. Change add slug field to returned data from discover. Change add 'score' mapped to rating to discover returned results. Fix valid_data for discover method. Change add result_count to discover. Change add _sanitise_image_uri to discover method. Fix convert_person. Change add missing _sanitise_image_uri for images in some places. Fix crew. Change return type of tvinfo base: discover to list tvinfoshow. Fix people remote id search. Change add tmdb person id search. Change fix people endpoint fieldname changes. Change add biography to person object. Change move 401 expired token handling into TvdbAuth class. Change get new token if old token is expired. Change add raise error if episodes fallback fails to load data. Change add break if no valid_data to absolute and alternative numberings. Change add filter only networks. Change add new required parameter meta=translations to get translated (includes the original language) show overviews. Change add check if show is set for person compare. Fix person update properties with no show set. Change add person image. Change add alternative episode orders. Change add alt_ep_numbering to TVINFO_Show. Change add old interface for dvd order. Change add trakt slug tvinfo search test cases. Change add mock for old tvdb get new token. Change old lib to newer tvinfo data. Fix person id (not available on old api). Change more places to new TVInfoAPI interface.
This commit is contained in:
parent
5ff18a8652
commit
7a6936823e
293 changed files with 2158 additions and 426 deletions
BIN
gui/slick/images/linkedin16.png
Normal file
BIN
gui/slick/images/linkedin16.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 399 B |
BIN
gui/slick/images/reddit16.png
Normal file
BIN
gui/slick/images/reddit16.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 630 B |
BIN
gui/slick/images/tiktok16.png
Normal file
BIN
gui/slick/images/tiktok16.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 622 B |
BIN
gui/slick/images/wikidata16.png
Normal file
BIN
gui/slick/images/wikidata16.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 125 B |
BIN
gui/slick/images/youtube16.png
Normal file
BIN
gui/slick/images/youtube16.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 214 B |
|
@ -77,7 +77,7 @@ def param(visible=True, rid=None, cache_person=None, cache_char=None, person=Non
|
|||
#elif $PersonGenders.male == $cur_person.gender#
|
||||
#set $gender = 'himself'
|
||||
#end if#
|
||||
#set $name = ($cur_person.name, $gender)[$rc_clean.sub('', $cur_person.name.lower()) == $rc_clean.sub('', $character.name.lower())]
|
||||
#set $name = ($cur_person.name, $gender)[$rc_clean.sub('', $cur_person.name.lower()) == $rc_clean.sub('', ($character.name or 'unknown name').lower())]
|
||||
<a href="$sbRoot/home/person?$param(person=$cur_person)">$name</a>#if 2 <= $num_people and $cur_enum + 1 == $num_people# and #elif 2 < $num_people and $cur_enum < $num_people#<span>, </span>#end if#
|
||||
#end for
|
||||
</div>
|
||||
|
|
|
@ -168,7 +168,7 @@
|
|||
<h2 class="title" id="scene_exception_$show_obj.tvid_prodid"><span>$show_obj.name</span>#echo ('', '<em id="title-status"> (ended)</em>')[$show_ended]#</h2>
|
||||
#set $genres_done = False
|
||||
#if $sg_var('USE_IMDB_INFO') and 'genres' in $show_obj.imdb_info and '' != $show_obj.imdb_info['genres']
|
||||
#for $imdbgenre in $show_obj.imdb_info['genres'].split('|')
|
||||
#for $imdbgenre in [$g for $g in $show_obj.imdb_info['genres'].split('|') if $g]
|
||||
#set $genres_done = True
|
||||
<span class="label"><a href="<%= anon_url('http://www.imdb.com/search/title?at=0&genres=', imdbgenre.lower().replace('-','_'),'&sort=moviemeter,asc&title_type=tv_series') %>" target="_blank" title="View other popular $imdbgenre shows on imdb.com" class="addQTip">$imdbgenre.replace('Sci-Fi','Science-Fiction')</a></span>
|
||||
#end for
|
||||
|
@ -176,7 +176,13 @@
|
|||
#if not $genres_done and $show_obj.genre
|
||||
#for $genre in $show_obj.genre.split('|')
|
||||
#set $genres_done = True
|
||||
<span class="label">$genre</span>
|
||||
<span class="label">
|
||||
#if $TVINFO_TVDB == $show_obj.tvid
|
||||
<a href="https://thetvdb.com/genres/$genre" target="_blank" title="View other popular $genre shows on thetvdb.com" class="addQTip">$genre</a>
|
||||
#else
|
||||
$genre
|
||||
#end if
|
||||
</span>
|
||||
#end for#
|
||||
#end if
|
||||
#if not $genres_done
|
||||
|
|
|
@ -20,9 +20,13 @@ from lib.tvinfo_base import (
|
|||
# TVINFO_FACEBOOK, TVINFO_INSTAGRAM, TVINFO_TMDB, TVINFO_TRAKT,
|
||||
# TVINFO_TVDB, TVINFO_TVRAGE, TVINFO_TWITTER, TVINFO_WIKIPEDIA,
|
||||
TVInfoBase, TVInfoIDs, TVInfoShow)
|
||||
from sg_helpers import get_url, try_int
|
||||
from sg_helpers import clean_data, enforce_type, get_url, try_int
|
||||
from json_helper import json_loads
|
||||
|
||||
from six import iteritems
|
||||
from six.moves import http_client as httplib
|
||||
from six.moves.urllib.parse import urlencode, urljoin, quote, unquote
|
||||
|
||||
|
||||
# noinspection PyUnreachableCode
|
||||
if False:
|
||||
|
@ -34,6 +38,37 @@ log = logging.getLogger('imdb.api')
|
|||
log.addHandler(logging.NullHandler())
|
||||
|
||||
|
||||
def _get_imdb(self, url, query=None, params=None):
|
||||
headers = {'Accept-Language': self.locale}
|
||||
if params:
|
||||
full_url = '{0}?{1}'.format(url, urlencode(params))
|
||||
else:
|
||||
full_url = url
|
||||
headers.update(self.get_auth_headers(full_url))
|
||||
resp = get_url(url, headers=headers, params=params, return_response=True)
|
||||
|
||||
if not resp.ok:
|
||||
if resp.status_code == httplib.NOT_FOUND:
|
||||
raise LookupError('Resource {0} not found'.format(url))
|
||||
else:
|
||||
msg = '{0} {1}'.format(resp.status_code, resp.text)
|
||||
raise imdbpie.ImdbAPIError(msg)
|
||||
resp_data = resp.content.decode('utf-8')
|
||||
try:
|
||||
resp_dict = json_loads(resp_data)
|
||||
except ValueError:
|
||||
resp_dict = self._parse_dirty_json(
|
||||
data=resp_data, query=query
|
||||
)
|
||||
|
||||
if resp_dict.get('error'):
|
||||
return None
|
||||
return resp_dict
|
||||
|
||||
|
||||
imdbpie.Imdb._get = _get_imdb
|
||||
|
||||
|
||||
class IMDbIndexer(TVInfoBase):
|
||||
# supported_id_searches = [TVINFO_IMDB]
|
||||
supported_person_id_searches = [TVINFO_IMDB]
|
||||
|
@ -71,9 +106,9 @@ class IMDbIndexer(TVInfoBase):
|
|||
ti_show = TVInfoShow()
|
||||
ti_show.seriesname, ti_show.id, ti_show.firstaired, ti_show.genre_list, ti_show.overview, \
|
||||
ti_show.poster, ti_show.ids = \
|
||||
s['title'], imdb_id, s.get('releaseDetails', {}).get('date') or s.get('year'), s.get('genres'), \
|
||||
s.get('plot', {}).get('outline', {}).get('text'), s.get('image') and s['image'].get('url'), \
|
||||
TVInfoIDs(imdb=imdb_id)
|
||||
clean_data(s['title']), imdb_id, s.get('releaseDetails', {}).get('date') or s.get('year'), \
|
||||
s.get('genres'), enforce_type(clean_data(s.get('plot', {}).get('outline', {}).get('text')), str, ''), \
|
||||
s.get('image') and s['image'].get('url'), TVInfoIDs(imdb=imdb_id)
|
||||
return ti_show
|
||||
|
||||
results = []
|
||||
|
@ -108,12 +143,12 @@ class IMDbIndexer(TVInfoBase):
|
|||
def _convert_person(person_obj, filmography=None, bio=None):
|
||||
if isinstance(person_obj, dict) and 'imdb_id' in person_obj:
|
||||
imdb_id = try_int(re.search(r'(\d+)', person_obj['imdb_id']).group(1))
|
||||
return TVInfoPerson(p_id=imdb_id, name=person_obj['name'], ids={TVINFO_IMDB: imdb_id})
|
||||
return TVInfoPerson(p_id=imdb_id, name=person_obj['name'], ids=TVInfoIDs(ids={TVINFO_IMDB: imdb_id}))
|
||||
characters = []
|
||||
for known_for in (filmography and filmography['filmography']) or []:
|
||||
if known_for['titleType'] not in ('tvSeries', 'tvMiniSeries'):
|
||||
continue
|
||||
for character in known_for.get('characters') or []:
|
||||
for character in known_for.get('characters') or ['unknown name']:
|
||||
ti_show = TVInfoShow()
|
||||
ti_show.id = try_int(re.search(r'(\d+)', known_for.get('id')).group(1))
|
||||
ti_show.ids.imdb = ti_show.id
|
||||
|
@ -133,7 +168,7 @@ class IMDbIndexer(TVInfoBase):
|
|||
deathdate = None
|
||||
imdb_id = try_int(re.search(r'(\d+)', person_obj['id']).group(1))
|
||||
return TVInfoPerson(
|
||||
p_id=imdb_id, ids={TVINFO_IMDB: imdb_id}, characters=characters,
|
||||
p_id=imdb_id, ids=TVInfoIDs(ids={TVINFO_IMDB: imdb_id}), characters=characters,
|
||||
name=person_obj['base'].get('name'), real_name=person_obj['base'].get('realName'),
|
||||
nicknames=set((person_obj['base'].get('nicknames') and person_obj['base'].get('nicknames')) or []),
|
||||
akas=set((person_obj['base'].get('akas') and person_obj['base'].get('akas')) or []),
|
||||
|
|
|
@ -18,13 +18,13 @@ from lib.tvinfo_base import CastList, PersonGenders, RoleTypes, \
|
|||
TVINFO_IMDB, TVINFO_TMDB, TVINFO_TVDB, \
|
||||
TVINFO_FACEBOOK, TVINFO_INSTAGRAM, TVINFO_TWITTER
|
||||
from json_helper import json_dumps
|
||||
from sg_helpers import clean_data, get_url, iterate_chunk, try_int
|
||||
from sg_helpers import clean_data, enforce_type, get_url, iterate_chunk, try_int
|
||||
|
||||
from six import iteritems
|
||||
|
||||
# noinspection PyUnreachableCode
|
||||
if False:
|
||||
from typing import Any, AnyStr, Dict, List, Optional
|
||||
from typing import Any, AnyStr, Dict, List, Optional, Union
|
||||
from six import integer_types
|
||||
|
||||
log = logging.getLogger('tmdb.api')
|
||||
|
@ -179,17 +179,19 @@ class TmdbIndexer(TVInfoBase):
|
|||
self.size_map = response.get('size_map')
|
||||
self.tv_genres = response.get('genres')
|
||||
|
||||
def _search_show(self, name=None, ids=None, **kwargs):
|
||||
# type: (AnyStr, Dict[integer_types, integer_types], Optional[Any]) -> List[TVInfoShow]
|
||||
def _search_show(self, name=None, ids=None, lang=None, **kwargs):
|
||||
# type: (Union[AnyStr, List[AnyStr]], Dict[integer_types, integer_types], Optional[string_types], Optional[Any]) -> List[Dict]
|
||||
"""This searches TMDB for the series name,
|
||||
"""
|
||||
tmdb_lang = ('en-US', lang)[lang in self._tmdb_supported_lang_list]
|
||||
|
||||
def _make_result_dict(s):
|
||||
ti_show = TVInfoShow()
|
||||
ti_show.seriesname, ti_show.id, ti_show.seriesid, ti_show.firstaired, ti_show.genre_list, \
|
||||
ti_show.overview, ti_show.poster, ti_show.ids, ti_show.language, ti_show.popularity, ti_show.rating = \
|
||||
clean_data(s['name']), s['id'], s['id'], clean_data(s.get('first_air_date')) or None, \
|
||||
clean_data([self.tv_genres.get(g) for g in s.get('genre_ids') or []]), \
|
||||
clean_data(s.get('overview')), s.get('poster_path') and '%s%s%s' % (
|
||||
enforce_type(clean_data(s.get('overview')), str, ''), s.get('poster_path') and '%s%s%s' % (
|
||||
self.img_base_url, self.size_map[TVInfoImageType.poster][TVInfoImageSize.original],
|
||||
s.get('poster_path')), \
|
||||
TVInfoIDs(tvdb=s.get('external_ids') and s['external_ids'].get('tvdb_id'),
|
||||
|
@ -209,7 +211,7 @@ class TmdbIndexer(TVInfoBase):
|
|||
is_none, shows = self._get_cache_entry(cache_id_key)
|
||||
if not self.config.get('cache_search') or (None is shows and not is_none):
|
||||
try:
|
||||
show = tmdbsimple.TV(id=p).info(append_to_response='external_ids')
|
||||
show = tmdbsimple.TV(id=p).info(append_to_response='external_ids', language=tmdb_lang)
|
||||
except (BaseException, Exception):
|
||||
continue
|
||||
self._set_cache_entry(cache_id_key, show, expire=self.search_cache_expire)
|
||||
|
@ -223,10 +225,10 @@ class TmdbIndexer(TVInfoBase):
|
|||
if not self.config.get('cache_search') or (None is shows and not is_none):
|
||||
try:
|
||||
show = tmdbsimple.Find(id=(p, 'tt%07d' % p)[t == TVINFO_IMDB]).info(
|
||||
external_source=id_map[t])
|
||||
external_source=id_map[t], language=tmdb_lang)
|
||||
if show.get('tv_results') and 1 == len(show['tv_results']):
|
||||
show = tmdbsimple.TV(id=show['tv_results'][0]['id']).info(
|
||||
append_to_response='external_ids')
|
||||
append_to_response='external_ids', language=tmdb_lang)
|
||||
except (BaseException, Exception):
|
||||
continue
|
||||
self._set_cache_entry(cache_id_key, show, expire=self.search_cache_expire)
|
||||
|
@ -241,7 +243,7 @@ class TmdbIndexer(TVInfoBase):
|
|||
is_none, shows = self._get_cache_entry(cache_name_key)
|
||||
if not self.config.get('cache_search') or (None is shows and not is_none):
|
||||
try:
|
||||
shows = tmdbsimple.Search().tv(query=n)
|
||||
shows = tmdbsimple.Search().tv(query=n, language=tmdb_lang)
|
||||
self._set_cache_entry(cache_name_key, shows, expire=self.search_cache_expire)
|
||||
results.extend([_make_result_dict(s) for s in shows.get('results') or []])
|
||||
except (BaseException, Exception) as e:
|
||||
|
@ -252,32 +254,23 @@ class TmdbIndexer(TVInfoBase):
|
|||
results = [seen.add(r.id) or r for r in results if r.id not in seen]
|
||||
return results
|
||||
|
||||
def _convert_person_obj(self, person_obj):
|
||||
gender = PersonGenders.tmdb_map.get(person_obj.get('gender'), PersonGenders.unknown)
|
||||
def _convert_person_obj(self, tmdb_person_obj):
|
||||
gender = PersonGenders.tmdb_map.get(tmdb_person_obj.get('gender'), PersonGenders.unknown)
|
||||
try:
|
||||
birthdate = person_obj.get('birthday') and tz_p.parse(person_obj.get('birthday')).date()
|
||||
birthdate = tmdb_person_obj.get('birthday') and tz_p.parse(tmdb_person_obj.get('birthday')).date()
|
||||
except (BaseException, Exception):
|
||||
birthdate = None
|
||||
try:
|
||||
deathdate = person_obj.get('deathday') and tz_p.parse(person_obj.get('deathday')).date()
|
||||
deathdate = tmdb_person_obj.get('deathday') and tz_p.parse(tmdb_person_obj.get('deathday')).date()
|
||||
except (BaseException, Exception):
|
||||
deathdate = None
|
||||
|
||||
cast = person_obj.get('cast') or person_obj.get('tv_credits', {}).get('cast')
|
||||
person_imdb_id = tmdb_person_obj.get('imdb_id') and try_int(tmdb_person_obj['imdb_id'].replace('nm', ''), None)
|
||||
person_ids = {TVINFO_TMDB: tmdb_person_obj.get('id')}
|
||||
if person_imdb_id:
|
||||
person_ids.update({TVINFO_IMDB: person_imdb_id})
|
||||
|
||||
characters = []
|
||||
for character in cast or []:
|
||||
ti_show = TVInfoShow()
|
||||
ti_show.id = character.get('id')
|
||||
ti_show.ids = TVInfoIDs(ids={TVINFO_TMDB: ti_show.id})
|
||||
ti_show.seriesname = clean_data(character.get('original_name'))
|
||||
ti_show.overview = clean_data(character.get('overview'))
|
||||
ti_show.firstaired = clean_data(character.get('first_air_date'))
|
||||
characters.append(
|
||||
TVInfoCharacter(name=clean_data(character.get('character')), show=ti_show)
|
||||
)
|
||||
|
||||
pi = person_obj.get('images')
|
||||
pi = tmdb_person_obj.get('images')
|
||||
image_url, main_image, thumb_url, main_thumb, image_list = None, None, None, None, []
|
||||
if pi:
|
||||
for i in sorted(pi['profiles'], key=lambda a: a['vote_average'] or 0, reverse=True):
|
||||
|
@ -308,20 +301,62 @@ class TmdbIndexer(TVInfoBase):
|
|||
rating=i['vote_average'],
|
||||
votes=i['vote_count']
|
||||
))
|
||||
elif tmdb_person_obj.get('profile_path'):
|
||||
main_image = '%s%s%s' % (
|
||||
self.img_base_url, self.size_map[TVInfoImageType.person_poster][TVInfoImageSize.original],
|
||||
tmdb_person_obj['profile_path'])
|
||||
main_thumb = '%s%s%s' % (
|
||||
self.img_base_url, self.size_map[TVInfoImageType.person_poster][TVInfoImageSize.medium],
|
||||
tmdb_person_obj['profile_path'])
|
||||
|
||||
person_imdb_id = person_obj.get('imdb_id') and try_int(person_obj['imdb_id'].replace('nm', ''), None)
|
||||
person_ids = {TVINFO_TMDB: person_obj.get('id')}
|
||||
if person_imdb_id:
|
||||
person_ids.update({TVINFO_IMDB: person_imdb_id})
|
||||
return TVInfoPerson(
|
||||
p_id=person_obj.get('id'), ids=person_ids, characters=characters,
|
||||
name=clean_data(person_obj.get('name')), akas=clean_data(set(person_obj.get('also_known_as') or [])),
|
||||
bio=clean_data(person_obj.get('biography')), gender=gender,
|
||||
_it_person_obj = TVInfoPerson(
|
||||
p_id=tmdb_person_obj.get('id'), ids=TVInfoIDs(ids=person_ids), name=clean_data(tmdb_person_obj.get('name')),
|
||||
akas=clean_data(set(tmdb_person_obj.get('also_known_as') or [])),
|
||||
bio=clean_data(tmdb_person_obj.get('biography')), gender=gender,
|
||||
image=main_image, images=image_list, thumb_url=main_thumb,
|
||||
birthdate=birthdate, birthplace=clean_data(person_obj.get('place_of_birth')),
|
||||
deathdate=deathdate, homepage=person_obj.get('homepage')
|
||||
birthdate=birthdate, birthplace=clean_data(tmdb_person_obj.get('place_of_birth')),
|
||||
deathdate=deathdate, homepage=tmdb_person_obj.get('homepage')
|
||||
)
|
||||
|
||||
cast = tmdb_person_obj.get('cast') or tmdb_person_obj.get('tv_credits', {}).get('cast') or \
|
||||
tmdb_person_obj.get('known_for')
|
||||
|
||||
characters = []
|
||||
for character in cast or []:
|
||||
ti_show = TVInfoShow()
|
||||
ti_show.id = character.get('id')
|
||||
ti_show.ids = TVInfoIDs(ids={TVINFO_TMDB: ti_show.id})
|
||||
ti_show.seriesname = enforce_type(clean_data(character.get('original_name')), str, '')
|
||||
ti_show.overview = enforce_type(clean_data(character.get('overview')), str, '')
|
||||
ti_show.firstaired = clean_data(character.get('first_air_date'))
|
||||
ti_show.language = clean_data(character.get('original_language'))
|
||||
ti_show.genre_list = []
|
||||
for g in character.get('genre_ids') or []:
|
||||
if g in self.tv_genres:
|
||||
ti_show.genre_list.append(self.tv_genres.get(g))
|
||||
ti_show.genre = '|'.join(ti_show.genre_list)
|
||||
if character.get('poster_path'):
|
||||
ti_show.poster = '%s%s%s' % \
|
||||
(self.img_base_url,
|
||||
self.size_map[TVInfoImageType.person_poster][TVInfoImageSize.original],
|
||||
character['poster_path'])
|
||||
ti_show.poster_thumb = '%s%s%s' % \
|
||||
(self.img_base_url,
|
||||
self.size_map[TVInfoImageType.person_poster][TVInfoImageSize.medium],
|
||||
character['poster_path'])
|
||||
if character.get('backdrop_path'):
|
||||
ti_show.fanart = '%s%s%s' % \
|
||||
(self.img_base_url,
|
||||
self.size_map[TVInfoImageType.person_poster][TVInfoImageSize.original],
|
||||
character['backdrop_path'])
|
||||
characters.append(
|
||||
TVInfoCharacter(name=clean_data(character.get('character')), ti_show=ti_show, person=[_it_person_obj],
|
||||
episode_count=character.get('episode_count'))
|
||||
)
|
||||
|
||||
_it_person_obj.characters = characters
|
||||
return _it_person_obj
|
||||
|
||||
def _search_person(self, name=None, ids=None):
|
||||
# type: (AnyStr, Dict[integer_types, integer_types]) -> List[TVInfoPerson]
|
||||
"""
|
||||
|
@ -420,7 +455,8 @@ class TmdbIndexer(TVInfoBase):
|
|||
ti_show.id = show_dict.get('id')
|
||||
ti_show.seriesid = ti_show.id
|
||||
ti_show.language = clean_data(show_dict.get('original_language'))
|
||||
ti_show.overview = clean_data(show_dict.get('overview'))
|
||||
ti_show.spoken_languages = [_l['iso_639_1'] for _l in show_dict.get('spoken_languages') or []]
|
||||
ti_show.overview = enforce_type(clean_data(show_dict.get('overview')), str, '')
|
||||
ti_show.status = clean_data(show_dict.get('status', ''))
|
||||
ti_show.show_type = clean_data((show_dict.get('type') and [show_dict['type']]) or [])
|
||||
ti_show.firstaired = clean_data(show_dict.get('first_air_date'))
|
||||
|
@ -467,13 +503,14 @@ class TmdbIndexer(TVInfoBase):
|
|||
(self.img_base_url, self.size_map[TVInfoImageType.fanart][TVInfoImageSize.original],
|
||||
show_dict.get('backdrop_path'))
|
||||
ti_show.ids = TVInfoIDs(tvdb=show_dict.get('external_ids', {}).get('tvdb_id'),
|
||||
tmdb=show_dict['id'],
|
||||
rage=show_dict.get('external_ids', {}).get('tvrage_id'),
|
||||
imdb=show_dict.get('external_ids', {}).get('imdb_id') and
|
||||
try_int(show_dict.get('external_ids', {}).get('imdb_id', '').replace('tt', ''), None))
|
||||
tmdb=show_dict['id'],
|
||||
rage=show_dict.get('external_ids', {}).get('tvrage_id'),
|
||||
imdb=show_dict.get('external_ids', {}).get('imdb_id')
|
||||
and try_int(
|
||||
show_dict.get('external_ids', {}).get('imdb_id', '').replace('tt', ''), None))
|
||||
ti_show.social_ids = TVInfoSocialIDs(twitter=show_dict.get('external_ids', {}).get('twitter_id'),
|
||||
instagram=show_dict.get('external_ids', {}).get('instagram_id'),
|
||||
facebook=show_dict.get('external_ids', {}).get('facebook_id'))
|
||||
instagram=show_dict.get('external_ids', {}).get('instagram_id'),
|
||||
facebook=show_dict.get('external_ids', {}).get('facebook_id'))
|
||||
|
||||
ti_show.poster = image_url
|
||||
ti_show.poster_thumb = thumb_image_url
|
||||
|
@ -498,7 +535,26 @@ class TmdbIndexer(TVInfoBase):
|
|||
pass
|
||||
return result[:result_count]
|
||||
|
||||
def get_similar(self, tvid, result_count=100, **kwargs):
|
||||
# type: (integer_types, int, Any) -> List[TVInfoShow]
|
||||
"""
|
||||
list of similar shows to the provided tv id
|
||||
:param tvid: id to find similar shows for
|
||||
:param result_count: result count to returned
|
||||
"""
|
||||
return self._get_show_list(tmdbsimple.TV(id=tvid).similar, result_count)
|
||||
|
||||
def get_recommended_for_show(self, tvid, result_count=100, **kwargs):
|
||||
# type: (integer_types, int, Any) -> List[TVInfoShow]
|
||||
"""
|
||||
list of recommended shows to the provided tv id
|
||||
:param tvid: id to find recommended shows for
|
||||
:param result_count: result count to returned
|
||||
"""
|
||||
return self._get_show_list(tmdbsimple.TV(id=tvid).recommendations, result_count)
|
||||
|
||||
def get_trending(self, result_count=100, time_window='day', **kwargs):
|
||||
# type: (int, str, Any) -> List[TVInfoShow]
|
||||
"""
|
||||
list of trending tv shows for day or week
|
||||
:param result_count:
|
||||
|
@ -508,12 +564,15 @@ class TmdbIndexer(TVInfoBase):
|
|||
return self._get_show_list(tmdbsimple.Trending(media_type='tv', time_window=t_windows).info, result_count)
|
||||
|
||||
def get_popular(self, result_count=100, **kwargs):
|
||||
# type: (int, Any) -> List[TVInfoShow]
|
||||
return self._get_show_list(tmdbsimple.TV().popular, result_count)
|
||||
|
||||
def get_top_rated(self, result_count=100, **kwargs):
|
||||
# type: (int, Any) -> List[TVInfoShow]
|
||||
return self._get_show_list(tmdbsimple.TV().top_rated, result_count)
|
||||
|
||||
def discover(self, result_count=100, **kwargs):
|
||||
# type: (int, Any) -> List[TVInfoShow]
|
||||
"""
|
||||
Discover TV shows by different types of data like average rating,
|
||||
number of votes, genres, the network they aired on and air dates.
|
||||
|
@ -596,6 +655,12 @@ class TmdbIndexer(TVInfoBase):
|
|||
|
||||
:param result_count:
|
||||
"""
|
||||
if not kwargs:
|
||||
# use default if now kwargs are set = return all future airdate shows with language set to 'en'
|
||||
kwargs.update({'sort_by': 'first_air_date.asc',
|
||||
'first_air_date.gte': datetime.date.today().strftime('%Y-%m-%d'),
|
||||
'with_original_language': 'en',
|
||||
})
|
||||
return self._get_show_list(tmdbsimple.Discover().tv, result_count, **kwargs)
|
||||
|
||||
def _get_show_data(self, sid, language, get_ep_info=False, banners=False, posters=False, seasons=False,
|
||||
|
@ -693,6 +758,7 @@ class TmdbIndexer(TVInfoBase):
|
|||
person=[
|
||||
TVInfoPerson(
|
||||
p_id=person_obj['id'], name=clean_data(person_obj['name']),
|
||||
ids=TVInfoIDs(ids={TVINFO_TMDB: person_obj['id']}),
|
||||
image='%s%s%s' % (
|
||||
self.img_base_url,
|
||||
self.size_map[TVInfoImageType.person_poster][
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
import datetime
|
||||
import logging
|
||||
import re
|
||||
from .exceptions import TraktException
|
||||
from .exceptions import TraktException, TraktAuthException
|
||||
from exceptions_helper import ConnectionSkipException, ex
|
||||
from six import iteritems
|
||||
from .trakt import TraktAPI
|
||||
from lib.tvinfo_base.exceptions import BaseTVinfoShownotfound
|
||||
from lib.tvinfo_base import TVInfoBase, TVINFO_TRAKT, TVINFO_TMDB, TVINFO_TVDB, TVINFO_TVRAGE, TVINFO_IMDB, \
|
||||
TVINFO_SLUG, TVInfoPerson, TVINFO_TWITTER, TVINFO_FACEBOOK, TVINFO_WIKIPEDIA, TVINFO_INSTAGRAM, TVInfoCharacter, TVInfoShow, \
|
||||
TVInfoIDs, TVINFO_TRAKT_SLUG
|
||||
from sg_helpers import try_int
|
||||
TVINFO_SLUG, TVInfoPerson, TVINFO_TWITTER, TVINFO_FACEBOOK, TVINFO_WIKIPEDIA, TVINFO_INSTAGRAM, TVInfoCharacter, \
|
||||
TVInfoShow, TVInfoIDs, TVInfoSocialIDs, TVINFO_TRAKT_SLUG, TVInfoEpisode, TVInfoSeason, RoleTypes
|
||||
from sg_helpers import clean_data, enforce_type, try_int
|
||||
from lib.dateutil.parser import parser
|
||||
|
||||
# noinspection PyUnreachableCode
|
||||
|
@ -33,6 +34,7 @@ log.addHandler(logging.NullHandler())
|
|||
|
||||
|
||||
def _convert_imdb_id(src, s_id):
|
||||
# type: (int, integer_types) -> integer_types
|
||||
if TVINFO_IMDB == src:
|
||||
try:
|
||||
return try_int(re.search(r'(\d+)', s_id).group(1), s_id)
|
||||
|
@ -100,16 +102,29 @@ class TraktIndexer(TVInfoBase):
|
|||
|
||||
@staticmethod
|
||||
def _make_result_obj(shows, results):
|
||||
# type: (List[Dict], List[TVInfoShow]) -> None
|
||||
if shows:
|
||||
try:
|
||||
for s in shows:
|
||||
if s['ids']['trakt'] not in [i['ids'].trakt for i in results]:
|
||||
s['id'] = s['ids']['trakt']
|
||||
s['ids'] = TVInfoIDs(
|
||||
trakt=s['ids']['trakt'], tvdb=s['ids']['tvdb'], tmdb=s['ids']['tmdb'],
|
||||
rage=s['ids']['tvrage'],
|
||||
imdb=s['ids']['imdb'] and try_int(s['ids']['imdb'].replace('tt', ''), None))
|
||||
results.append(s)
|
||||
ti_show = TVInfoShow()
|
||||
countries = clean_data(s['country'])
|
||||
if countries:
|
||||
countries = [countries]
|
||||
else:
|
||||
countries = []
|
||||
ti_show.id, ti_show.seriesname, ti_show.overview, ti_show.firstaired, ti_show.airs_dayofweek, \
|
||||
ti_show.runtime, ti_show.network, ti_show.origin_countries, ti_show.official_site, \
|
||||
ti_show.status, ti_show.rating, ti_show.genre_list, ti_show.ids = s['ids']['trakt'], \
|
||||
clean_data(s['title']), enforce_type(clean_data(s['overview']), str, ''), s['firstaired'], \
|
||||
(isinstance(s['airs'], dict) and s['airs']['day']) or '', \
|
||||
s['runtime'], s['network'], countries, s['homepage'], s['status'], s['rating'], \
|
||||
s['genres_list'], \
|
||||
TVInfoIDs(trakt=s['ids']['trakt'], tvdb=s['ids']['tvdb'], tmdb=s['ids']['tmdb'],
|
||||
rage=s['ids']['tvrage'],
|
||||
imdb=s['ids']['imdb'] and try_int(s['ids']['imdb'].replace('tt', ''), None))
|
||||
ti_show.genre = '|'.join(ti_show.genre_list or [])
|
||||
results.append(ti_show)
|
||||
except (BaseException, Exception) as e:
|
||||
log.debug('Error creating result dict: %s' % ex(e))
|
||||
|
||||
|
@ -119,7 +134,7 @@ class TraktIndexer(TVInfoBase):
|
|||
If a custom_ui UI is configured, it uses this to select the correct
|
||||
series.
|
||||
"""
|
||||
results = []
|
||||
results = [] # type: List[TVInfoShow]
|
||||
if ids:
|
||||
for t, p in iteritems(ids):
|
||||
if t in self.supported_id_searches:
|
||||
|
@ -168,13 +183,13 @@ class TraktIndexer(TVInfoBase):
|
|||
else:
|
||||
self._make_result_obj(all_series, results)
|
||||
|
||||
final_result = []
|
||||
final_result = [] # type: List[TVInfoShow]
|
||||
seen = set()
|
||||
film_type = re.compile(r'(?i)films?\)$')
|
||||
for r in results:
|
||||
if r['id'] not in seen:
|
||||
seen.add(r['id'])
|
||||
title = r.get('title') or ''
|
||||
if r.id not in seen:
|
||||
seen.add(r.id)
|
||||
title = r.seriesname or ''
|
||||
if not film_type.search(title):
|
||||
final_result.append(r)
|
||||
else:
|
||||
|
@ -247,17 +262,19 @@ class TraktIndexer(TVInfoBase):
|
|||
deathdate=deathdate,
|
||||
homepage=person_obj['homepage'],
|
||||
birthplace=person_obj['birthplace'],
|
||||
social_ids={TVINFO_TWITTER: person_obj['social_ids']['twitter'],
|
||||
TVINFO_FACEBOOK: person_obj['social_ids']['facebook'],
|
||||
TVINFO_INSTAGRAM: person_obj['social_ids']['instagram'],
|
||||
TVINFO_WIKIPEDIA: person_obj['social_ids']['wikipedia']
|
||||
},
|
||||
ids={TVINFO_TRAKT: person_obj['ids']['trakt'], TVINFO_SLUG: person_obj['ids']['slug'],
|
||||
TVINFO_IMDB:
|
||||
person_obj['ids']['imdb'] and
|
||||
try_int(person_obj['ids']['imdb'].replace('nm', ''), None),
|
||||
TVINFO_TMDB: person_obj['ids']['tmdb'],
|
||||
TVINFO_TVRAGE: person_obj['ids']['tvrage']})
|
||||
social_ids=TVInfoSocialIDs(
|
||||
ids={TVINFO_TWITTER: person_obj['social_ids']['twitter'],
|
||||
TVINFO_FACEBOOK: person_obj['social_ids']['facebook'],
|
||||
TVINFO_INSTAGRAM: person_obj['social_ids']['instagram'],
|
||||
TVINFO_WIKIPEDIA: person_obj['social_ids']['wikipedia']
|
||||
}),
|
||||
ids=TVInfoIDs(ids={
|
||||
TVINFO_TRAKT: person_obj['ids']['trakt'], TVINFO_SLUG: person_obj['ids']['slug'],
|
||||
TVINFO_IMDB:
|
||||
person_obj['ids']['imdb'] and
|
||||
try_int(person_obj['ids']['imdb'].replace('nm', ''), None),
|
||||
TVINFO_TMDB: person_obj['ids']['tmdb'],
|
||||
TVINFO_TVRAGE: person_obj['ids']['tvrage']}))
|
||||
|
||||
def get_person(self, p_id, get_show_credits=False, get_images=False, **kwargs):
|
||||
# type: (integer_types, bool, bool, Any) -> Optional[TVInfoPerson]
|
||||
|
@ -279,7 +296,7 @@ class TraktIndexer(TVInfoBase):
|
|||
if not urls:
|
||||
return
|
||||
|
||||
result = None
|
||||
result = None # type: Optional[TVInfoPerson]
|
||||
|
||||
for url, show_credits in urls:
|
||||
try:
|
||||
|
@ -296,18 +313,21 @@ class TraktIndexer(TVInfoBase):
|
|||
ti_show.id = c['show']['ids'].get('trakt')
|
||||
ti_show.seriesname = c['show']['title']
|
||||
ti_show.ids = TVInfoIDs(ids={id_map[src]: _convert_imdb_id(id_map[src], sid)
|
||||
for src, sid in iteritems(c['show']['ids']) if src in id_map})
|
||||
for src, sid in iteritems(c['show']['ids']) if src in id_map})
|
||||
ti_show.network = c['show']['network']
|
||||
ti_show.firstaired = c['show']['first_aired']
|
||||
ti_show.overview = c['show']['overview']
|
||||
ti_show.overview = enforce_type(clean_data(c['show']['overview']), str, '')
|
||||
ti_show.status = c['show']['status']
|
||||
ti_show.imdb_id = c['show']['ids'].get('imdb')
|
||||
ti_show.runtime = c['show']['runtime']
|
||||
ti_show.genre_list = c['show']['genres']
|
||||
for ch in c.get('characters') or []:
|
||||
pc.append(
|
||||
TVInfoCharacter(name=ch, regular=c.get('series_regular'), ti_show=ti_show)
|
||||
)
|
||||
_ti_character = TVInfoCharacter(name=ch, regular=c.get('series_regular'),
|
||||
ti_show=ti_show, person=[result],
|
||||
episode_count=c.get('episode_count'))
|
||||
pc.append(_ti_character)
|
||||
ti_show.cast[(RoleTypes.ActorGuest, RoleTypes.ActorMain)[
|
||||
c.get('series_regular', False)]].append(_ti_character)
|
||||
result.characters = pc
|
||||
else:
|
||||
result = self._convert_person_obj(resp)
|
||||
|
@ -353,3 +373,268 @@ class TraktIndexer(TVInfoBase):
|
|||
log.debug('Could not connect to Trakt service: %s' % ex(e))
|
||||
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def _convert_episode(episode_data, show_obj, season_obj):
|
||||
# type: (Dict, TVInfoShow, TVInfoSeason) -> TVInfoEpisode
|
||||
ti_episode = TVInfoEpisode(show=show_obj)
|
||||
ti_episode.season = season_obj
|
||||
ti_episode.id, ti_episode.episodename, ti_episode.seasonnumber, ti_episode.episodenumber, \
|
||||
ti_episode.absolute_number, ti_episode.overview, ti_episode.firstaired, ti_episode.runtime, \
|
||||
ti_episode.rating, ti_episode.vote_count = episode_data.get('ids', {}).get('trakt'), \
|
||||
clean_data(episode_data.get('title')), episode_data.get('season'), episode_data.get('number'), \
|
||||
episode_data.get('number_abs'), enforce_type(clean_data(episode_data.get('overview')), str, ''), \
|
||||
re.sub('T.+$', '', episode_data.get('first_aired') or ''), \
|
||||
episode_data['runtime'], episode_data.get('rating'), episode_data.get('votes')
|
||||
if episode_data.get('available_translations'):
|
||||
ti_episode.language = clean_data(episode_data['available_translations'][0])
|
||||
ti_episode.ids = TVInfoIDs(ids={id_map[src]: _convert_imdb_id(id_map[src], sid)
|
||||
for src, sid in iteritems(episode_data['ids']) if src in id_map})
|
||||
return ti_episode
|
||||
|
||||
@staticmethod
|
||||
def _convert_show(show_data):
|
||||
# type: (Dict) -> TVInfoShow
|
||||
_s_d = (show_data, show_data.get('show'))['show' in show_data]
|
||||
ti_show = TVInfoShow()
|
||||
ti_show.seriesname, ti_show.id, ti_show.firstaired, ti_show.overview, ti_show.runtime, ti_show.network, \
|
||||
ti_show.network_country, ti_show.status, ti_show.genre_list, ti_show.language, ti_show.watcher_count, \
|
||||
ti_show.play_count, ti_show.collected_count, ti_show.collector_count, ti_show.vote_count, \
|
||||
ti_show.vote_average, ti_show.rating, ti_show.contentrating, ti_show.official_site, ti_show.slug = \
|
||||
clean_data(_s_d['title']), _s_d['ids']['trakt'], \
|
||||
re.sub('T.+$', '', _s_d.get('first_aired') or '') or _s_d.get('year'), \
|
||||
enforce_type(clean_data(_s_d.get('overview')), str, ''), _s_d.get('runtime'), _s_d.get('network'), \
|
||||
_s_d.get('country'), _s_d.get('status'), _s_d.get('genres', []), _s_d.get('language'), \
|
||||
show_data.get('watcher_count'), show_data.get('play_count'), show_data.get('collected_count'), \
|
||||
show_data.get('collector_count'), _s_d.get('votes'), _s_d.get('rating'), _s_d.get('rating'), \
|
||||
_s_d.get('certification'), _s_d.get('homepage'), _s_d['ids']['slug']
|
||||
ti_show.ids = TVInfoIDs(ids={id_map[src]: _convert_imdb_id(id_map[src], sid)
|
||||
for src, sid in iteritems(_s_d['ids']) if src in id_map})
|
||||
ti_show.genre = '|'.join(ti_show.genre_list or [])
|
||||
if _s_d.get('trailer'):
|
||||
ti_show.trailers = {'any': _s_d['trailer']}
|
||||
if 'episode' in show_data:
|
||||
ep_data = show_data['episode']
|
||||
ti_show.next_season_airdate = re.sub('T.+$', '', ep_data.get('first_aired') or '')
|
||||
ti_season = TVInfoSeason(show=ti_show)
|
||||
ti_season.number = ep_data['season']
|
||||
ti_season[ep_data['number']] = TraktIndexer._convert_episode(ep_data, ti_show, ti_season)
|
||||
ti_show[ep_data['season']] = ti_season
|
||||
return ti_show
|
||||
|
||||
def _get_show_lists(self, url, account=None):
|
||||
# type: (str, Any) -> List[TVInfoShow]
|
||||
result = []
|
||||
if account:
|
||||
from sickgear import TRAKT_ACCOUNTS
|
||||
if account in TRAKT_ACCOUNTS and TRAKT_ACCOUNTS[account].active:
|
||||
kw = {'send_oauth': account}
|
||||
else:
|
||||
raise TraktAuthException('Account missing or disabled')
|
||||
else:
|
||||
kw = {}
|
||||
resp = TraktAPI().trakt_request(url, **kw)
|
||||
if resp:
|
||||
for _show in resp:
|
||||
result.append(self._convert_show(_show))
|
||||
return result
|
||||
|
||||
def get_most_played(self, result_count=100, period='weekly', **kwargs):
|
||||
# type: (...) -> List[TVInfoShow]
|
||||
"""
|
||||
get most played shows
|
||||
:param period: possible values: 'daily', 'weekly', 'monthly', 'yearly', 'all'
|
||||
:param result_count: how many results are suppose to be returned
|
||||
"""
|
||||
use_period = ('weekly', period)[period in ('daily', 'weekly', 'monthly', 'yearly', 'all')]
|
||||
return self._get_show_lists('shows/played/%s?extended=full&page=%d&limit=%d' % (use_period, 1, result_count))
|
||||
|
||||
def get_most_watched(self, result_count=100, period='weekly', **kwargs):
|
||||
# type: (...) -> List[TVInfoShow]
|
||||
"""
|
||||
get most watched shows
|
||||
:param period: possible values: 'daily', 'weekly', 'monthly', 'yearly', 'all'
|
||||
:param result_count: how many results are suppose to be returned
|
||||
"""
|
||||
use_period = ('weekly', period)[period in ('daily', 'weekly', 'monthly', 'yearly', 'all')]
|
||||
return self._get_show_lists('shows/watched/%s?extended=full&page=%d&limit=%d' % (use_period, 1, result_count))
|
||||
|
||||
def get_most_collected(self, result_count=100, period='weekly', **kwargs):
|
||||
# type: (...) -> List[TVInfoShow]
|
||||
"""
|
||||
get most collected shows
|
||||
:param period: possible values: 'daily', 'weekly', 'monthly', 'yearly', 'all'
|
||||
:param result_count: how many results are suppose to be returned
|
||||
"""
|
||||
use_period = ('weekly', period)[period in ('daily', 'weekly', 'monthly', 'yearly', 'all')]
|
||||
return self._get_show_lists('shows/collected/%s?extended=full&page=%d&limit=%d' % (use_period, 1, result_count))
|
||||
|
||||
def get_recommended(self, result_count=100, period='weekly', **kwargs):
|
||||
# type: (...) -> List[TVInfoShow]
|
||||
"""
|
||||
get most recommended shows
|
||||
:param period: possible values: 'daily', 'weekly', 'monthly', 'yearly', 'all'
|
||||
:param result_count: how many results are suppose to be returned
|
||||
"""
|
||||
use_period = ('weekly', period)[period in ('daily', 'weekly', 'monthly', 'yearly', 'all')]
|
||||
return self._get_show_lists('shows/recommended/%s?extended=full&page=%d&limit=%d' % (use_period, 1, result_count))
|
||||
|
||||
def get_recommended_for_account(self, account, result_count=100, ignore_collected=False, ignore_watchlisted=False,
|
||||
**kwargs):
|
||||
# type: (...) -> List[TVInfoShow]
|
||||
"""
|
||||
get most recommended shows for account
|
||||
:param account: account to get recommendations for
|
||||
:param result_count: how many results are suppose to be returned
|
||||
:param ignore_collected: exclude colleded shows
|
||||
:param ignore_watchlisted: exclude watchlisted shows
|
||||
"""
|
||||
from sickgear import TRAKT_ACCOUNTS
|
||||
if not account or account not in TRAKT_ACCOUNTS or not TRAKT_ACCOUNTS[account].active:
|
||||
raise TraktAuthException('Account missing or disabled')
|
||||
extra_param = []
|
||||
if ignore_collected:
|
||||
extra_param.append('ignore_collected=true')
|
||||
if ignore_watchlisted:
|
||||
extra_param.append('ignore_watchlisted=true')
|
||||
return self._get_show_lists('recommendations/shows?extended=full&page=%d&limit=%d%s' %
|
||||
(1, result_count, ('', '&%s' % '&'.join(extra_param))[0 < len(extra_param)]),
|
||||
account=account)
|
||||
|
||||
def hide_recommended_for_account(self, account, show_ids, **kwargs):
|
||||
# type: (integer_types, List[integer_types], Any) -> List[integer_types]
|
||||
"""
|
||||
hide recommended show for account
|
||||
:param account: account to get recommendations for
|
||||
:param show_ids: list of show_ids to no longer recommend for account
|
||||
:return: list of added ids
|
||||
"""
|
||||
from sickgear import TRAKT_ACCOUNTS
|
||||
if not account or account not in TRAKT_ACCOUNTS or not TRAKT_ACCOUNTS[account].active:
|
||||
raise TraktAuthException('Account missing or disabled')
|
||||
if not isinstance(show_ids, list) or not show_ids or any(not isinstance(_i, int) for _i in show_ids):
|
||||
raise TraktException('list of show_ids (trakt id) required')
|
||||
resp = TraktAPI().trakt_request('users/hidden/recommendations', send_oauth=account,
|
||||
data={'shows': [{'ids': {'trakt': _i}} for _i in show_ids]})
|
||||
if resp and isinstance(resp, dict) and 'added' in resp and 'shows' in resp['added']:
|
||||
if len(show_ids) == resp['added']['shows']:
|
||||
return show_ids
|
||||
if 'not_found' in resp and 'shows' in resp['not_found']:
|
||||
not_found = [_i['ids']['trakt'] for _i in resp['not_found']['shows']]
|
||||
else:
|
||||
not_found = []
|
||||
return [_i for _i in show_ids if _i not in not_found]
|
||||
return []
|
||||
|
||||
def unhide_recommended_for_account(self, account, show_ids, **kwargs):
|
||||
# type: (integer_types, List[integer_types], Any) -> List[integer_types]
|
||||
"""
|
||||
unhide recommended show for account
|
||||
:param account: account to get recommendations for
|
||||
:param show_ids: list of show_ids to be included in possible recommend for account
|
||||
:return: list of removed ids
|
||||
"""
|
||||
from sickgear import TRAKT_ACCOUNTS
|
||||
if not account or account not in TRAKT_ACCOUNTS or not TRAKT_ACCOUNTS[account].active:
|
||||
raise TraktAuthException('Account missing or disabled')
|
||||
if not isinstance(show_ids, list) or not show_ids or any(not isinstance(_i, int) for _i in show_ids):
|
||||
raise TraktException('list of show_ids (trakt id) required')
|
||||
resp = TraktAPI().trakt_request('users/hidden/recommendations/remove', send_oauth=account,
|
||||
data={'shows': [{'ids': {'trakt': _i}} for _i in show_ids]})
|
||||
if resp and isinstance(resp, dict) and 'deleted' in resp and 'shows' in resp['deleted']:
|
||||
if len(show_ids) == resp['deleted']['shows']:
|
||||
return show_ids
|
||||
if 'not_found' in resp and 'shows' in resp['not_found']:
|
||||
not_found = [_i['ids']['trakt'] for _i in resp['not_found']['shows']]
|
||||
else:
|
||||
not_found = []
|
||||
return [_i for _i in show_ids if _i not in not_found]
|
||||
return []
|
||||
|
||||
def list_hidden_recommended_for_account(self, account, **kwargs):
|
||||
# type: (integer_types, Any) -> List[TVInfoShow]
|
||||
"""
|
||||
list hidden recommended show for account
|
||||
:param account: account to get recommendations for
|
||||
:return: list of hidden shows
|
||||
"""
|
||||
from sickgear import TRAKT_ACCOUNTS
|
||||
if not account or account not in TRAKT_ACCOUNTS or not TRAKT_ACCOUNTS[account].active:
|
||||
raise TraktAuthException('Account missing or disabled')
|
||||
return self._get_show_lists('users/hidden/recommendations?type=show', account=account)
|
||||
|
||||
def get_watchlisted_for_account(self, account, result_count=100, sort='rank', **kwargs):
|
||||
# type: (...) -> List[TVInfoShow]
|
||||
"""
|
||||
get watchlisted shows for the account
|
||||
:param account: account to get recommendations for
|
||||
:param result_count: how many results are suppose to be returned
|
||||
:param sort: possible values: 'rank', 'added', 'released', 'title'
|
||||
"""
|
||||
from sickgear import TRAKT_ACCOUNTS
|
||||
if not account or account not in TRAKT_ACCOUNTS or not TRAKT_ACCOUNTS[account].active:
|
||||
raise TraktAuthException('Account missing or disabled')
|
||||
sort = ('rank', sort)[sort in ('rank', 'added', 'released', 'title')]
|
||||
return self._get_show_lists('users/%s/watchlist/shows/%s?extended=full&page=%d&limit=%d' %
|
||||
(TRAKT_ACCOUNTS[account].slug, sort, 1, result_count), account=account)
|
||||
|
||||
def get_anticipated(self, result_count=100, **kwargs):
|
||||
# type: (...) -> List[TVInfoShow]
|
||||
"""
|
||||
get most anticipated shows
|
||||
:param result_count: how many results are suppose to be returned
|
||||
"""
|
||||
return self._get_show_lists('shows/anticipated?extended=full&page=%d&limit=%d' % (1, result_count))
|
||||
|
||||
def get_trending(self, result_count=100, **kwargs):
|
||||
# type: (...) -> List[TVInfoShow]
|
||||
"""
|
||||
get trending shows
|
||||
:param result_count: how many results are suppose to be returned
|
||||
"""
|
||||
return self._get_show_lists('shows/trending?extended=full&page=%d&limit=%d' % (1, result_count))
|
||||
|
||||
def get_popular(self, result_count=100, **kwargs):
|
||||
# type: (...) -> List[TVInfoShow]
|
||||
"""
|
||||
get all popular shows
|
||||
:param result_count: how many results are suppose to be returned
|
||||
"""
|
||||
return self._get_show_lists('shows/popular?extended=full&page=%d&limit=%d' % (1, result_count))
|
||||
|
||||
def get_similar(self, tvid, result_count=100, **kwargs):
|
||||
# type: (integer_types, int, Any) -> List[TVInfoShow]
|
||||
"""
|
||||
return list of similar shows to given id
|
||||
:param tvid: id to give similar shows for
|
||||
:param result_count: count of results requested
|
||||
"""
|
||||
if not isinstance(tvid, int):
|
||||
raise TraktException('tvid/trakt id for show required')
|
||||
return self._get_show_lists('shows/%d/related?extended=full&page=%d&limit=%d' % (tvid, 1, result_count))
|
||||
|
||||
def get_new_shows(self, result_count=100, start_date=None, days=32, **kwargs):
|
||||
# type: (...) -> List[TVInfoShow]
|
||||
"""
|
||||
get new shows
|
||||
:param result_count: how many results are suppose to be returned
|
||||
:param start_date: start date for returned data in format: '2014-09-01'
|
||||
:param days: number of days to return from start date
|
||||
"""
|
||||
if None is start_date:
|
||||
start_date = (datetime.datetime.now() + datetime.timedelta(days=-16)).strftime('%Y-%m-%d')
|
||||
return self._get_show_lists('calendars/all/shows/new/%s/%s?extended=full&page=%d&limit=%d' %
|
||||
(start_date, days, 1, result_count))
|
||||
|
||||
def get_new_seasons(self, result_count=100, start_date=None, days=32, **kwargs):
|
||||
# type: (...) -> List[TVInfoShow]
|
||||
"""
|
||||
get new seasons
|
||||
:param result_count: how many results are suppose to be returned
|
||||
:param start_date: start date for returned data in format: '2014-09-01'
|
||||
:param days: number of days to return from start date
|
||||
"""
|
||||
if None is start_date:
|
||||
start_date = (datetime.datetime.now() + datetime.timedelta(days=-16)).strftime('%Y-%m-%d')
|
||||
return self._get_show_lists('calendars/all/shows/premieres/%s/%s?extended=full&page=%d&limit=%d' %
|
||||
(start_date, days, 1, result_count))
|
||||
|
|
|
@ -33,7 +33,7 @@ from lib.cachecontrol import CacheControl, caches
|
|||
from lib.dateutil.parser import parse
|
||||
from lib.exceptions_helper import ConnectionSkipException
|
||||
from lib.tvinfo_base import CastList, TVInfoCharacter, CrewList, TVInfoPerson, RoleTypes, \
|
||||
TVINFO_TVDB, TVINFO_TVDB_SLUG, TVInfoBase, TVInfoIDs
|
||||
TVINFO_TVDB, TVINFO_TVDB_SLUG, TVInfoBase, TVInfoIDs, TVInfoNetwork, TVInfoShow
|
||||
|
||||
from .tvdb_exceptions import TvdbError, TvdbShownotfound, TvdbTokenexpired
|
||||
from .tvdb_ui import BaseUI, ConsoleUI
|
||||
|
@ -44,7 +44,6 @@ from six import integer_types, iteritems, PY2, string_types
|
|||
if False:
|
||||
# noinspection PyUnresolvedReferences
|
||||
from typing import Any, AnyStr, Dict, List, Optional, Union
|
||||
from lib.tvinfo_base import TVInfoShow
|
||||
|
||||
|
||||
THETVDB_V2_API_TOKEN = {'token': None, 'datetime': datetime.datetime.fromordinal(1)}
|
||||
|
@ -334,13 +333,15 @@ class Tvdb(TVInfoBase):
|
|||
|
||||
def _search_show(self, name=None, ids=None, **kwargs):
|
||||
# type: (AnyStr, Dict[integer_types, integer_types], Optional[Any]) -> List[TVInfoShow]
|
||||
def map_data(data):
|
||||
if not data.get('poster'):
|
||||
data['poster'] = data.get('image')
|
||||
data['ids'] = TVInfoIDs(
|
||||
tvdb=data.get('id'),
|
||||
imdb=data.get('imdb_id') and try_int(data.get('imdb_id', '').replace('tt', ''), None))
|
||||
return data
|
||||
def make_tvinfoshow(data):
|
||||
_ti_show = TVInfoShow()
|
||||
_ti_show.id, _ti_show.banner, _ti_show.firstaired, _ti_show.poster, _ti_show.network, _ti_show.overview, \
|
||||
_ti_show.seriesname, _ti_show.slug, _ti_show.status, _ti_show.aliases, _ti_show.ids = \
|
||||
clean_data(data['id']), clean_data(data.get('banner')), clean_data(data.get('firstaired')), \
|
||||
clean_data(data.get('poster')), clean_data(data.get('network')), clean_data(data.get('overview')), \
|
||||
clean_data(data.get('seriesname')), clean_data(data.get('slug')), clean_data(data.get('status')), \
|
||||
clean_data((data.get('aliases'))), TVInfoIDs(tvdb=try_int(clean_data(data['id'])))
|
||||
return _ti_show
|
||||
|
||||
results = []
|
||||
if ids:
|
||||
|
@ -356,7 +357,7 @@ class Tvdb(TVInfoBase):
|
|||
else:
|
||||
d_m = shows
|
||||
if d_m:
|
||||
results = list(map(map_data, [d_m['data']]))
|
||||
results.append(make_tvinfoshow(d_m['data']))
|
||||
if ids.get(TVINFO_TVDB_SLUG):
|
||||
cache_id_key = 's-id-%s-%s' % (TVINFO_TVDB, ids[TVINFO_TVDB_SLUG])
|
||||
is_none, shows = self._get_cache_entry(cache_id_key)
|
||||
|
@ -371,7 +372,7 @@ class Tvdb(TVInfoBase):
|
|||
if d_m:
|
||||
for r in d_m:
|
||||
if ids.get(TVINFO_TVDB_SLUG) == r['slug']:
|
||||
results = list(map(map_data, [r]))
|
||||
results.append(make_tvinfoshow(r))
|
||||
break
|
||||
if name:
|
||||
for n in ([name], name)[isinstance(name, list)]:
|
||||
|
@ -388,7 +389,7 @@ class Tvdb(TVInfoBase):
|
|||
if r:
|
||||
if not isinstance(r, list):
|
||||
r = [r]
|
||||
results.extend(list(map(map_data, r)))
|
||||
results.extend([make_tvinfoshow(_s) for _s in r])
|
||||
|
||||
seen = set()
|
||||
results = [seen.add(r['id']) or r for r in results if r['id'] not in seen]
|
||||
|
@ -947,10 +948,7 @@ class Tvdb(TVInfoBase):
|
|||
role_image = self._make_image(self.config['url_artworks'], role_image)
|
||||
character_name = n.get('role', '').strip() or alts.get(n['id'], {}).get('role', '')
|
||||
person_name = n.get('name', '').strip() or alts.get(n['id'], {}).get('name', '')
|
||||
try:
|
||||
person_id = try_int(re.search(r'^person/(\d+)/', n.get('image', '')).group(1), None)
|
||||
except (BaseException, Exception):
|
||||
person_id = None
|
||||
person_id = None
|
||||
person_id = person_id or alts.get(n['id'], {}).get('person_id')
|
||||
character_id = n.get('id', None) or alts.get(n['id'], {}).get('rid')
|
||||
a.append({'character': {'id': character_id,
|
||||
|
@ -971,7 +969,7 @@ class Tvdb(TVInfoBase):
|
|||
cast[RoleTypes.ActorMain].append(
|
||||
TVInfoCharacter(
|
||||
p_id=character_id, name=character_name, person=[TVInfoPerson(p_id=person_id, name=person_name)],
|
||||
image=role_image, show=self.shows[sid]))
|
||||
image=role_image, show=self.ti_shows[sid]))
|
||||
except (BaseException, Exception):
|
||||
pass
|
||||
self._set_show_data(sid, 'actors', a)
|
||||
|
@ -1020,9 +1018,9 @@ class Tvdb(TVInfoBase):
|
|||
self.ti_shows[sid].__dict__[loaded_name] = True
|
||||
|
||||
# fallback image thumbnail for none excluded_main_data if artwork is not found
|
||||
if not excluded_main_data and show_data['data'].get(image_type):
|
||||
if not excluded_main_data and show_data.get(image_type):
|
||||
self._set_show_data(sid, f'{image_type}_thumb',
|
||||
re.sub(r'\.jpg$', '_t.jpg', show_data['data'][image_type], flags=re.I))
|
||||
re.sub(r'\.jpg$', '_t.jpg', show_data[image_type], flags=re.I))
|
||||
|
||||
def _get_show_data(self,
|
||||
sid, # type: integer_types
|
||||
|
@ -1044,7 +1042,8 @@ class Tvdb(TVInfoBase):
|
|||
|
||||
# Parse show information
|
||||
url = self.config['url_series_info'] % sid
|
||||
if direct_data or sid not in self.shows or None is self.shows[sid].id or language != self.shows[sid].language:
|
||||
if direct_data or sid not in self.ti_shows or None is self.ti_shows[sid].id or \
|
||||
language != self.ti_shows[sid].language:
|
||||
log.debug('Getting all series data for %s' % sid)
|
||||
show_data = self._getetsrc(url, language=language)
|
||||
if not show_data or not show_data.get('data'):
|
||||
|
@ -1056,13 +1055,34 @@ class Tvdb(TVInfoBase):
|
|||
if not (show_data and 'seriesname' in show_data.get('data', {}) or {}):
|
||||
return False
|
||||
|
||||
for k, v in iteritems(show_data['data']):
|
||||
self._set_show_data(sid, k, v)
|
||||
self._set_show_data(sid, 'ids',
|
||||
TVInfoIDs(
|
||||
tvdb=show_data['data'].get('id'),
|
||||
imdb=show_data['data'].get('imdb_id')
|
||||
and try_int(show_data['data'].get('imdb_id', '').replace('tt', ''), None)))
|
||||
show_data = show_data['data']
|
||||
ti_show = self.ti_shows[sid] # type: TVInfoShow
|
||||
ti_show.banner_loaded = ti_show.poster_loaded = ti_show.fanart_loaded = True
|
||||
ti_show.id = show_data['id']
|
||||
ti_show.seriesname = clean_data(show_data.get('seriesname'))
|
||||
ti_show.slug = clean_data(show_data.get('slug'))
|
||||
ti_show.poster = clean_data(show_data.get('poster'))
|
||||
ti_show.banner = clean_data(show_data.get('banner'))
|
||||
ti_show.fanart = clean_data(show_data.get('fanart'))
|
||||
ti_show.firstaired = clean_data(show_data.get('firstAired'))
|
||||
ti_show.rating = show_data.get('rating')
|
||||
ti_show.contentrating = clean_data(show_data.get('contentRatings'))
|
||||
ti_show.aliases = show_data.get('aliases') or []
|
||||
ti_show.status = clean_data(show_data['status'])
|
||||
if clean_data(show_data.get('network')):
|
||||
ti_show.network = clean_data(show_data['network'])
|
||||
ti_show.networks = [TVInfoNetwork(clean_data(show_data['network']),
|
||||
n_id=clean_data(show_data.get('networkid')))]
|
||||
ti_show.runtime = try_int(show_data.get('runtime'), 0)
|
||||
ti_show.language = clean_data(show_data.get('language'))
|
||||
ti_show.genre = clean_data(show_data.get('genre'))
|
||||
ti_show.genre_list = clean_data(show_data.get('genre_list')) or []
|
||||
ti_show.overview = clean_data(show_data.get('overview'))
|
||||
ti_show.imdb_id = clean_data(show_data.get('imdb_id')) or None
|
||||
ti_show.airs_time = clean_data(show_data.get('airs_time'))
|
||||
ti_show.airs_dayofweek = clean_data(show_data.get('airs_dayofweek'))
|
||||
ti_show.ids = TVInfoIDs(tvdb=ti_show.id, imdb=try_int(ti_show.imdb_id.replace('tt', ''), None))
|
||||
|
||||
else:
|
||||
show_data = {'data': {}}
|
||||
|
||||
|
@ -1225,7 +1245,7 @@ class Tvdb(TVInfoBase):
|
|||
try:
|
||||
for guest in cur_ep.get('gueststars_list', []):
|
||||
cast[RoleTypes.ActorGuest].append(TVInfoCharacter(person=[TVInfoPerson(name=guest)],
|
||||
show=self.shows[sid]))
|
||||
show=self.ti_shows[sid]))
|
||||
except (BaseException, Exception):
|
||||
pass
|
||||
try:
|
||||
|
@ -1258,6 +1278,11 @@ class Tvdb(TVInfoBase):
|
|||
self.corrections.update(dict([(x['seriesname'], int(x['id'])) for x in selected_series]))
|
||||
return sids
|
||||
|
||||
def _get_languages(self):
|
||||
if not Tvdb._supported_languages:
|
||||
Tvdb._supported_languages = [{'id': _l, 'name': None, 'nativeName': None, 'sg_lang': _l}
|
||||
for _l in self.config['valid_languages']]
|
||||
|
||||
|
||||
def main():
|
||||
"""Simple example of using tvdb_api - it just
|
||||
|
|
|
@ -103,7 +103,7 @@ show_map = {
|
|||
# 'siteratingcount': '',
|
||||
# 'lastupdated': '',
|
||||
# 'contentrating': '',
|
||||
'rating': 'rating',
|
||||
# 'rating': 'rating',
|
||||
'status': 'status',
|
||||
'overview': 'summary',
|
||||
# 'poster': 'image',
|
||||
|
@ -152,21 +152,28 @@ class TvMaze(TVInfoBase):
|
|||
if language in cur_locale[1]['name_en'].lower():
|
||||
language_country_code = cur_locale[0].split('_')[1].lower()
|
||||
break
|
||||
return {'seriesname': clean_data(s.name), 'id': s.id, 'firstaired': clean_data(s.premiered),
|
||||
'network': clean_data((s.network and s.network.name) or (s.web_channel and s.web_channel.name)),
|
||||
'genres': clean_data(isinstance(s.genres, list) and '|'.join(g.lower() for g in s.genres) or
|
||||
s.genres),
|
||||
'overview': clean_data(s.summary), 'language': clean_data(s.language),
|
||||
'language_country_code': clean_data(language_country_code),
|
||||
'runtime': s.average_runtime or s.runtime,
|
||||
'type': clean_data(s.type), 'schedule': s.schedule, 'status': clean_data(s.status),
|
||||
'official_site': clean_data(s.official_site),
|
||||
'aliases': [clean_data(a.name) for a in s.akas], 'image': s.image and s.image.get('original'),
|
||||
'poster': s.image and s.image.get('original'),
|
||||
'ids': TVInfoIDs(
|
||||
tvdb=s.externals.get('thetvdb'), rage=s.externals.get('tvrage'), tvmaze=s.id,
|
||||
imdb=clean_data(s.externals.get('imdb') and try_int(s.externals.get('imdb').replace('tt', ''),
|
||||
None)))}
|
||||
ti_show = TVInfoShow()
|
||||
show_type = clean_data(s.type)
|
||||
if show_type:
|
||||
show_type = [show_type]
|
||||
else:
|
||||
show_type = []
|
||||
ti_show.seriesname, ti_show.id, ti_show.firstaired, ti_show.network, ti_show.genre_list, ti_show.overview, \
|
||||
ti_show.language, ti_show.runtime, ti_show.show_type, ti_show.airs_dayofweek, ti_show. status, \
|
||||
ti_show.official_site, ti_show.aliases, ti_show.poster, ti_show.ids = clean_data(s.name), s.id, \
|
||||
clean_data(s.premiered), \
|
||||
clean_data((s.network and s.network.name) or (s.web_channel and s.web_channel.name)), \
|
||||
isinstance(s.genres, list) and [clean_data(g.lower()) for g in s.genres], \
|
||||
enforce_type(clean_data(s.summary), str, ''), clean_data(s.language), \
|
||||
s.average_runtime or s.runtime, show_type, ', '.join(s.schedule['days'] or []), clean_data(s.status), \
|
||||
clean_data(s.official_site), [clean_data(a.name) for a in s.akas], \
|
||||
s.image and s.image.get('original'), \
|
||||
TVInfoIDs(tvdb=s.externals.get('thetvdb'), rage=s.externals.get('tvrage'), tvmaze=s.id,
|
||||
imdb=clean_data(s.externals.get('imdb') and
|
||||
try_int(s.externals.get('imdb').replace('tt', ''), None)))
|
||||
ti_show.genre = '|'.join(ti_show.genre_list or [])
|
||||
return ti_show
|
||||
|
||||
results = []
|
||||
if ids:
|
||||
for t, p in iteritems(ids):
|
||||
|
@ -230,18 +237,24 @@ class TvMaze(TVInfoBase):
|
|||
('episodename', 'title'), ('overview', 'summary'), ('firstaired', 'airdate'),
|
||||
('airtime', 'airtime'), ('runtime', 'runtime'),
|
||||
('seriesid', 'maze_id'), ('id', 'maze_id'), ('is_special', 'special'), ('filename', 'image')):
|
||||
if 'filename' == _k:
|
||||
if 'airtime' == _k:
|
||||
try:
|
||||
airtime = datetime.time.fromisoformat(clean_data(getattr(ep_obj, _s, getattr(empty_ep, _k))))
|
||||
except (BaseException, Exception):
|
||||
airtime = None
|
||||
self._set_item(sid, ep_obj.season_number, ep_obj.episode_number or 0, _k, airtime)
|
||||
elif 'filename' == _k:
|
||||
image = getattr(ep_obj, _s, {}) or {}
|
||||
image = image.get('original') or image.get('medium')
|
||||
self._set_item(sid, ep_obj.season_number, ep_obj.episode_number, _k, image)
|
||||
self._set_item(sid, ep_obj.season_number, ep_obj.episode_number or 0, _k, image)
|
||||
else:
|
||||
self._set_item(sid, ep_obj.season_number, ep_obj.episode_number, _k,
|
||||
self._set_item(sid, ep_obj.season_number, ep_obj.episode_number or 0, _k,
|
||||
clean_data(getattr(ep_obj, _s, getattr(empty_ep, _k))))
|
||||
|
||||
if ep_obj.airstamp:
|
||||
try:
|
||||
at = _datetime_to_timestamp(tz_p.parse(ep_obj.airstamp))
|
||||
self._set_item(sid, ep_obj.season_number, ep_obj.episode_number, 'timestamp', at)
|
||||
self._set_item(sid, ep_obj.season_number, ep_obj.episode_number or 0, 'timestamp', at)
|
||||
except (BaseException, Exception):
|
||||
pass
|
||||
|
||||
|
@ -318,137 +331,12 @@ class TvMaze(TVInfoBase):
|
|||
return False
|
||||
|
||||
ti_show = self.ti_shows[sid] # type: TVInfoShow
|
||||
show_obj = ti_show.__dict__
|
||||
for k, v in iteritems(show_obj):
|
||||
if k not in ('cast', 'crew', 'images', 'aliases'):
|
||||
show_obj[k] = getattr(show_data, show_map.get(k, k), clean_data(show_obj[k]))
|
||||
ti_show.aliases = [clean_data(a.name) for a in show_data.akas]
|
||||
ti_show.runtime = show_data.average_runtime or show_data.runtime
|
||||
p_set = False
|
||||
if show_data.image:
|
||||
p_set = True
|
||||
ti_show.poster = show_data.image.get('original')
|
||||
ti_show.poster_thumb = show_data.image.get('medium')
|
||||
|
||||
if (banners or posters or fanart or
|
||||
any(self.config.get('%s_enabled' % t, False) for t in ('banners', 'posters', 'fanart'))) and \
|
||||
not all(getattr(ti_show, '%s_loaded' % t, False) for t in ('poster', 'banner', 'fanart')):
|
||||
if show_data.images:
|
||||
ti_show.poster_loaded = True
|
||||
ti_show.banner_loaded = True
|
||||
ti_show.fanart_loaded = True
|
||||
self._set_images(ti_show, show_data, p_set)
|
||||
|
||||
if show_data.schedule:
|
||||
if 'time' in show_data.schedule:
|
||||
ti_show.airs_time = show_data.schedule['time']
|
||||
try:
|
||||
h, m = show_data.schedule['time'].split(':')
|
||||
h, m = try_int(h, None), try_int(m, None)
|
||||
if None is not h and None is not m:
|
||||
ti_show.time = datetime.time(hour=h, minute=m)
|
||||
except (BaseException, Exception):
|
||||
pass
|
||||
if 'days' in show_data.schedule:
|
||||
ti_show.airs_dayofweek = ', '.join(show_data.schedule['days'])
|
||||
if show_data.genres:
|
||||
ti_show.genre = '|'.join(show_data.genres).lower()
|
||||
|
||||
if (actors or self.config['actors_enabled']) and not getattr(self.ti_shows.get(sid), 'actors_loaded', False):
|
||||
if show_data.cast:
|
||||
character_person_ids = {}
|
||||
for cur_ch in ti_show.cast[RoleTypes.ActorMain]:
|
||||
character_person_ids.setdefault(cur_ch.id, []).extend([p.id for p in cur_ch.person])
|
||||
for cur_ch in show_data.cast.characters:
|
||||
existing_character = next((c for c in ti_show.cast[RoleTypes.ActorMain] if c.id == cur_ch.id),
|
||||
None) # type: Optional[TVInfoCharacter]
|
||||
person = self._convert_person(cur_ch.person)
|
||||
if existing_character:
|
||||
existing_person = next((p for p in existing_character.person
|
||||
if person.id == p.ids.get(TVINFO_TVMAZE)),
|
||||
None) # type: TVInfoPerson
|
||||
if existing_person:
|
||||
try:
|
||||
character_person_ids[cur_ch.id].remove(existing_person.id)
|
||||
except (BaseException, Exception):
|
||||
print('error')
|
||||
pass
|
||||
(existing_person.p_id, existing_person.name, existing_person.image, existing_person.gender,
|
||||
existing_person.birthdate, existing_person.deathdate, existing_person.country,
|
||||
existing_person.country_code, existing_person.country_timezone, existing_person.thumb_url,
|
||||
existing_person.url, existing_person.ids) = \
|
||||
(cur_ch.person.id, clean_data(cur_ch.person.name),
|
||||
cur_ch.person.image and cur_ch.person.image.get('original'),
|
||||
PersonGenders.named.get(
|
||||
cur_ch.person.gender and cur_ch.person.gender.lower(), PersonGenders.unknown),
|
||||
person.birthdate, person.deathdate,
|
||||
cur_ch.person.country and clean_data(cur_ch.person.country.get('name')),
|
||||
cur_ch.person.country and clean_data(cur_ch.person.country.get('code')),
|
||||
cur_ch.person.country and clean_data(cur_ch.person.country.get('timezone')),
|
||||
cur_ch.person.image and cur_ch.person.image.get('medium'),
|
||||
cur_ch.person.url, {TVINFO_TVMAZE: cur_ch.person.id})
|
||||
else:
|
||||
existing_character.person.append(person)
|
||||
else:
|
||||
ti_show.cast[RoleTypes.ActorMain].append(
|
||||
TVInfoCharacter(image=cur_ch.image and cur_ch.image.get('original'), name=clean_data(cur_ch.name),
|
||||
p_id=cur_ch.id, person=[person], plays_self=cur_ch.plays_self,
|
||||
thumb_url=cur_ch.image and cur_ch.image.get('medium')
|
||||
))
|
||||
|
||||
if character_person_ids:
|
||||
for cur_ch, cur_p_ids in iteritems(character_person_ids):
|
||||
if cur_p_ids:
|
||||
char = next((mc for mc in ti_show.cast[RoleTypes.ActorMain] if mc.id == cur_ch),
|
||||
None) # type: Optional[TVInfoCharacter]
|
||||
if char:
|
||||
char.person = [p for p in char.person if p.id not in cur_p_ids]
|
||||
|
||||
if show_data.cast:
|
||||
ti_show.actors = [
|
||||
{'character': {'id': ch.id,
|
||||
'name': clean_data(ch.name),
|
||||
'url': 'https://www.tvmaze.com/character/view?id=%s' % ch.id,
|
||||
'image': ch.image and ch.image.get('original'),
|
||||
},
|
||||
'person': {'id': ch.person and ch.person.id,
|
||||
'name': ch.person and clean_data(ch.person.name),
|
||||
'url': ch.person and 'https://www.tvmaze.com/person/view?id=%s' % ch.person.id,
|
||||
'image': ch.person and ch.person.image and ch.person.image.get('original'),
|
||||
'birthday': None, # not sure about format
|
||||
'deathday': None, # not sure about format
|
||||
'gender': ch.person and ch.person.gender and ch.person.gender,
|
||||
'country': ch.person and ch.person.country and
|
||||
clean_data(ch.person.country.get('name')),
|
||||
},
|
||||
} for ch in show_data.cast.characters]
|
||||
|
||||
if show_data.crew:
|
||||
for cur_cw in show_data.crew:
|
||||
rt = crew_type_names.get(cur_cw.type.lower(), RoleTypes.CrewOther)
|
||||
ti_show.crew[rt].append(
|
||||
Crew(p_id=cur_cw.person.id, name=clean_data(cur_cw.person.name),
|
||||
image=cur_cw.person.image and cur_cw.person.image.get('original'),
|
||||
gender=cur_cw.person.gender,
|
||||
birthdate=cur_cw.person.birthday, deathdate=cur_cw.person.death_day,
|
||||
country=cur_cw.person.country and cur_cw.person.country.get('name'),
|
||||
country_code=cur_cw.person.country and clean_data(cur_cw.person.country.get('code')),
|
||||
country_timezone=cur_cw.person.country
|
||||
and clean_data(cur_cw.person.country.get('timezone')),
|
||||
crew_type_name=cur_cw.type,
|
||||
)
|
||||
)
|
||||
|
||||
if show_data.externals:
|
||||
ti_show.ids = TVInfoIDs(tvdb=show_data.externals.get('thetvdb'),
|
||||
rage=show_data.externals.get('tvrage'),
|
||||
imdb=clean_data(show_data.externals.get('imdb') and
|
||||
try_int(show_data.externals.get('imdb').replace('tt', ''), None)))
|
||||
|
||||
if show_data.network:
|
||||
self._set_network(ti_show, show_data.network, False)
|
||||
elif show_data.web_channel:
|
||||
self._set_network(ti_show, show_data.web_channel, True)
|
||||
self._show_info_loader(
|
||||
sid, show_data, ti_show,
|
||||
load_images=banners or posters or fanart or
|
||||
any(self.config.get('%s_enabled' % t, False) for t in ('banners', 'posters', 'fanart')),
|
||||
load_actors=(actors or self.config['actors_enabled'])
|
||||
)
|
||||
|
||||
if get_ep_info and not getattr(self.ti_shows.get(sid), 'ep_loaded', False):
|
||||
log.debug('Getting all episodes of %s' % sid)
|
||||
|
@ -509,10 +397,10 @@ class TvMaze(TVInfoBase):
|
|||
# type: (...) -> Dict[integer_types, integer_types]
|
||||
return {sid: v.seconds_since_epoch for sid, v in iteritems(tvmaze.show_updates().updates)}
|
||||
|
||||
@staticmethod
|
||||
def _convert_person(person_obj):
|
||||
def _convert_person(self, tvmaze_person_obj):
|
||||
# type: (tvmaze.Person) -> TVInfoPerson
|
||||
ch = []
|
||||
_dupes = []
|
||||
for c in tvmaze_person_obj.castcredits or []:
|
||||
ti_show = TVInfoShow()
|
||||
ti_show.seriesname = clean_data(c.show.name)
|
||||
|
@ -531,25 +419,240 @@ class TvMaze(TVInfoBase):
|
|||
ti_show.network_is_stream = None is not c.show.web_channel
|
||||
ch.append(TVInfoCharacter(name=clean_data(c.character.name), ti_show=ti_show, episode_count=1))
|
||||
try:
|
||||
birthdate = person_obj.birthday and tz_p.parse(person_obj.birthday).date()
|
||||
birthdate = tvmaze_person_obj.birthday and tz_p.parse(tvmaze_person_obj.birthday).date()
|
||||
except (BaseException, Exception):
|
||||
birthdate = None
|
||||
try:
|
||||
deathdate = person_obj.death_day and tz_p.parse(person_obj.death_day).date()
|
||||
deathdate = tvmaze_person_obj.death_day and tz_p.parse(tvmaze_person_obj.death_day).date()
|
||||
except (BaseException, Exception):
|
||||
deathdate = None
|
||||
return TVInfoPerson(p_id=person_obj.id, name=clean_data(person_obj.name),
|
||||
image=person_obj.image and person_obj.image.get('original'),
|
||||
gender=PersonGenders.named.get(person_obj.gender and person_obj.gender.lower(),
|
||||
PersonGenders.unknown),
|
||||
birthdate=birthdate, deathdate=deathdate,
|
||||
country=person_obj.country and clean_data(person_obj.country.get('name')),
|
||||
country_code=person_obj.country and clean_data(person_obj.country.get('code')),
|
||||
country_timezone=person_obj.country and clean_data(person_obj.country.get('timezone')),
|
||||
thumb_url=person_obj.image and person_obj.image.get('medium'),
|
||||
url=person_obj.url, ids={TVINFO_TVMAZE: person_obj.id}, characters=ch
|
||||
|
||||
_ti_person_obj = TVInfoPerson(
|
||||
p_id=tvmaze_person_obj.id, name=clean_data(tvmaze_person_obj.name),
|
||||
image=tvmaze_person_obj.image and tvmaze_person_obj.image.get('original'),
|
||||
gender=PersonGenders.named.get(tvmaze_person_obj.gender and tvmaze_person_obj.gender.lower(),
|
||||
PersonGenders.unknown),
|
||||
birthdate=birthdate, deathdate=deathdate,
|
||||
country=tvmaze_person_obj.country and clean_data(tvmaze_person_obj.country.get('name')),
|
||||
country_code=tvmaze_person_obj.country and clean_data(tvmaze_person_obj.country.get('code')),
|
||||
country_timezone=tvmaze_person_obj.country and clean_data(tvmaze_person_obj.country.get('timezone')),
|
||||
thumb_url=tvmaze_person_obj.image and tvmaze_person_obj.image.get('medium'),
|
||||
url=tvmaze_person_obj.url, ids=TVInfoIDs(ids={TVINFO_TVMAZE: tvmaze_person_obj.id})
|
||||
)
|
||||
|
||||
for (c_t, regular) in [(tvmaze_person_obj.castcredits or [], True),
|
||||
(tvmaze_person_obj.guestcastcredits or [], False)]:
|
||||
for c in c_t: # type: tvmaze.CastCredit
|
||||
_show = c.show or c.episode.show
|
||||
_clean_char_name = clean_data(c.character.name)
|
||||
ti_show = TVInfoShow()
|
||||
if None is not _show:
|
||||
_clean_show_name = clean_data(_show.name)
|
||||
_clean_show_id = clean_data(_show.id)
|
||||
_cur_dup = (_clean_char_name, _clean_show_id)
|
||||
if _cur_dup in _dupes:
|
||||
_co = next((_c for _c in ch if _clean_show_id == _c.ti_show.id
|
||||
and _c.name == _clean_char_name), None)
|
||||
if None is not _co:
|
||||
ti_show = _co.ti_show
|
||||
_co.episode_count += 1
|
||||
if not regular:
|
||||
ep_no = c.episode.episode_number or 0
|
||||
_co.guest_episodes_numbers.setdefault(c.episode.season_number, []).append(ep_no)
|
||||
if c.episode.season_number not in ti_show:
|
||||
season = TVInfoSeason(show=ti_show, number=c.episode.season_number)
|
||||
ti_show[c.episode.season_number] = season
|
||||
else:
|
||||
season = ti_show[c.episode.season_number]
|
||||
episode = self._make_episode(c.episode, show_obj=ti_show)
|
||||
episode.season = season
|
||||
ti_show[c.episode.season_number][ep_no] = episode
|
||||
continue
|
||||
else:
|
||||
_dupes.append(_cur_dup)
|
||||
ti_show.seriesname = clean_data(_show.name)
|
||||
ti_show.id = _show.id
|
||||
ti_show.firstaired = clean_data(_show.premiered)
|
||||
ti_show.ids = TVInfoIDs(ids={TVINFO_TVMAZE: ti_show.id})
|
||||
ti_show.overview = enforce_type(clean_data(_show.summary), str, '')
|
||||
ti_show.status = clean_data(_show.status)
|
||||
net = _show.network or _show.web_channel
|
||||
if net:
|
||||
ti_show.network = clean_data(net.name)
|
||||
ti_show.network_id = net.maze_id
|
||||
ti_show.network_country = clean_data(net.country)
|
||||
ti_show.network_timezone = clean_data(net.timezone)
|
||||
ti_show.network_country_code = clean_data(net.code)
|
||||
ti_show.network_is_stream = None is not _show.web_channel
|
||||
if c.episode:
|
||||
|
||||
ti_show.show_loaded = False
|
||||
ti_show.load_method = self._show_info_loader
|
||||
season = TVInfoSeason(show=ti_show, number=c.episode.season_number)
|
||||
ti_show[c.episode.season_number] = season
|
||||
episode = self._make_episode(c.episode, show_obj=ti_show)
|
||||
episode.season = season
|
||||
ti_show[c.episode.season_number][c.episode.episode_number or 0] = episode
|
||||
if not regular:
|
||||
_g_kw = {'guest_episodes_numbers': {c.episode.season_number: [c.episode.episode_number or 0]}}
|
||||
else:
|
||||
_g_kw = {}
|
||||
ch.append(TVInfoCharacter(name=_clean_char_name, ti_show=ti_show, regular=regular, episode_count=1,
|
||||
person=[_ti_person_obj], **_g_kw))
|
||||
_ti_person_obj.characters = ch
|
||||
return _ti_person_obj
|
||||
|
||||
def _show_info_loader(self, show_id, show_data=None, show_obj=None, load_images=True, load_actors=True):
|
||||
# type: (int, TVMazeShow, TVInfoShow, bool, bool) -> TVInfoShow
|
||||
try:
|
||||
_s_d = show_data or tvmaze.show_main_info(show_id, embed='cast')
|
||||
if _s_d:
|
||||
if None is not show_obj:
|
||||
_s_o = show_obj
|
||||
else:
|
||||
_s_o = TVInfoShow()
|
||||
show_dict = _s_o.__dict__
|
||||
for k, v in iteritems(show_dict):
|
||||
if k not in ('cast', 'crew', 'images', 'aliases', 'rating'):
|
||||
show_dict[k] = getattr(_s_d, show_map.get(k, k), clean_data(show_dict[k]))
|
||||
_s_o.aliases = [clean_data(a.name) for a in _s_d.akas]
|
||||
_s_o.runtime = _s_d.average_runtime or _s_d.runtime
|
||||
p_set = False
|
||||
if _s_d.image:
|
||||
p_set = True
|
||||
_s_o.poster = _s_d.image.get('original')
|
||||
_s_o.poster_thumb = _s_d.image.get('medium')
|
||||
|
||||
if load_images and \
|
||||
not all(getattr(_s_o, '%s_loaded' % t, False) for t in ('poster', 'banner', 'fanart')):
|
||||
if _s_d.images:
|
||||
_s_o.poster_loaded = True
|
||||
_s_o.banner_loaded = True
|
||||
_s_o.fanart_loaded = True
|
||||
self._set_images(_s_o, _s_d, p_set)
|
||||
|
||||
if _s_d.schedule:
|
||||
if 'time' in _s_d.schedule:
|
||||
_s_o.airs_time = _s_d.schedule['time']
|
||||
try:
|
||||
h, m = _s_d.schedule['time'].split(':')
|
||||
h, m = try_int(h, None), try_int(m, None)
|
||||
if None is not h and None is not m:
|
||||
_s_o.time = datetime.time(hour=h, minute=m)
|
||||
except (BaseException, Exception):
|
||||
pass
|
||||
if 'days' in _s_d.schedule:
|
||||
_s_o.airs_dayofweek = ', '.join(_s_d.schedule['days'])
|
||||
|
||||
if load_actors and not _s_o.actors_loaded:
|
||||
if _s_d.cast:
|
||||
character_person_ids = {}
|
||||
for cur_ch in _s_o.cast[RoleTypes.ActorMain]:
|
||||
character_person_ids.setdefault(cur_ch.id, []).extend([p.id for p in cur_ch.person])
|
||||
for cur_ch in _s_d.cast.characters:
|
||||
existing_character = next(
|
||||
(c for c in _s_o.cast[RoleTypes.ActorMain] if c.id == cur_ch.id),
|
||||
None) # type: Optional[TVInfoCharacter]
|
||||
person = self._convert_person(cur_ch.person)
|
||||
if existing_character:
|
||||
existing_person = next((p for p in existing_character.person
|
||||
if person.id == p.ids.get(TVINFO_TVMAZE)),
|
||||
None) # type: TVInfoPerson
|
||||
if existing_person:
|
||||
try:
|
||||
character_person_ids[cur_ch.id].remove(existing_person.id)
|
||||
except (BaseException, Exception):
|
||||
print('error')
|
||||
pass
|
||||
(existing_person.p_id, existing_person.name, existing_person.image,
|
||||
existing_person.gender,
|
||||
existing_person.birthdate, existing_person.deathdate, existing_person.country,
|
||||
existing_person.country_code, existing_person.country_timezone,
|
||||
existing_person.thumb_url,
|
||||
existing_person.url, existing_person.ids) = \
|
||||
(cur_ch.person.id, clean_data(cur_ch.person.name),
|
||||
cur_ch.person.image and cur_ch.person.image.get('original'),
|
||||
PersonGenders.named.get(
|
||||
cur_ch.person.gender and cur_ch.person.gender.lower(),
|
||||
PersonGenders.unknown),
|
||||
person.birthdate, person.deathdate,
|
||||
cur_ch.person.country and clean_data(cur_ch.person.country.get('name')),
|
||||
cur_ch.person.country and clean_data(cur_ch.person.country.get('code')),
|
||||
cur_ch.person.country and clean_data(cur_ch.person.country.get('timezone')),
|
||||
cur_ch.person.image and cur_ch.person.image.get('medium'),
|
||||
cur_ch.person.url, {TVINFO_TVMAZE: cur_ch.person.id})
|
||||
else:
|
||||
existing_character.person.append(person)
|
||||
else:
|
||||
_s_o.cast[RoleTypes.ActorMain].append(
|
||||
TVInfoCharacter(image=cur_ch.image and cur_ch.image.get('original'),
|
||||
name=clean_data(cur_ch.name),
|
||||
ids=TVInfoIDs({TVINFO_TVMAZE: cur_ch.id}),
|
||||
p_id=cur_ch.id, person=[person], plays_self=cur_ch.plays_self,
|
||||
thumb_url=cur_ch.image and cur_ch.image.get('medium'),
|
||||
ti_show=_s_o
|
||||
))
|
||||
|
||||
if character_person_ids:
|
||||
for cur_ch, cur_p_ids in iteritems(character_person_ids):
|
||||
if cur_p_ids:
|
||||
char = next((mc for mc in _s_o.cast[RoleTypes.ActorMain] if mc.id == cur_ch),
|
||||
None) # type: Optional[TVInfoCharacter]
|
||||
if char:
|
||||
char.person = [p for p in char.person if p.id not in cur_p_ids]
|
||||
|
||||
if _s_d.cast:
|
||||
_s_o.actors = [
|
||||
{'character': {'id': ch.id,
|
||||
'name': clean_data(ch.name),
|
||||
'url': 'https://www.tvmaze.com/character/view?id=%s' % ch.id,
|
||||
'image': ch.image and ch.image.get('original'),
|
||||
},
|
||||
'person': {'id': ch.person and ch.person.id,
|
||||
'name': ch.person and clean_data(ch.person.name),
|
||||
'url': ch.person and 'https://www.tvmaze.com/person/view?id=%s' % ch.person.id,
|
||||
'image': ch.person and ch.person.image and ch.person.image.get('original'),
|
||||
'birthday': None, # not sure about format
|
||||
'deathday': None, # not sure about format
|
||||
'gender': ch.person and ch.person.gender and ch.person.gender,
|
||||
'country': ch.person and ch.person.country and
|
||||
clean_data(ch.person.country.get('name')),
|
||||
},
|
||||
} for ch in _s_d.cast.characters]
|
||||
|
||||
if _s_d.crew:
|
||||
for cur_cw in _s_d.crew:
|
||||
rt = crew_type_names.get(cur_cw.type.lower(), RoleTypes.CrewOther)
|
||||
_s_o.crew[rt].append(
|
||||
Crew(p_id=cur_cw.person.id, name=clean_data(cur_cw.person.name),
|
||||
image=cur_cw.person.image and cur_cw.person.image.get('original'),
|
||||
gender=cur_cw.person.gender,
|
||||
birthdate=cur_cw.person.birthday, deathdate=cur_cw.person.death_day,
|
||||
country=cur_cw.person.country and cur_cw.person.country.get('name'),
|
||||
country_code=cur_cw.person.country and clean_data(
|
||||
cur_cw.person.country.get('code')),
|
||||
country_timezone=cur_cw.person.country
|
||||
and clean_data(cur_cw.person.country.get('timezone')),
|
||||
crew_type_name=cur_cw.type,
|
||||
)
|
||||
)
|
||||
|
||||
if _s_d.externals:
|
||||
_s_o.ids = TVInfoIDs(tvdb=_s_d.externals.get('thetvdb'),
|
||||
rage=_s_d.externals.get('tvrage'),
|
||||
imdb=clean_data(_s_d.externals.get('imdb') and
|
||||
try_int(_s_d.externals.get('imdb').replace('tt', ''),
|
||||
None)))
|
||||
|
||||
if _s_d.network:
|
||||
self._set_network(_s_o, _s_d.network, False)
|
||||
elif _s_d.web_channel:
|
||||
self._set_network(_s_o, _s_d.web_channel, True)
|
||||
|
||||
return _s_o
|
||||
except (BaseException, Exception):
|
||||
pass
|
||||
|
||||
def _search_person(self, name=None, ids=None):
|
||||
# type: (AnyStr, Dict[integer_types, integer_types]) -> List[TVInfoPerson]
|
||||
urls, result, ids = [], [], ids or {}
|
||||
|
@ -597,64 +700,71 @@ class TvMaze(TVInfoBase):
|
|||
return self._convert_person(p)
|
||||
|
||||
def get_premieres(self, **kwargs):
|
||||
# type: (...) -> List[TVInfoEpisode]
|
||||
return self._filtered_schedule(**kwargs).get('premieres')
|
||||
# type: (...) -> List[TVInfoShow]
|
||||
return [_e.show for _e in self._filtered_schedule(**kwargs).get('premieres')]
|
||||
|
||||
def get_returning(self, **kwargs):
|
||||
# type: (...) -> List[TVInfoEpisode]
|
||||
return self._filtered_schedule(**kwargs).get('returning')
|
||||
# type: (...) -> List[TVInfoShow]
|
||||
return [_e.show for _e in self._filtered_schedule(**kwargs).get('returning')]
|
||||
|
||||
def _make_episode(self, episode_data, show_data=None, get_images=False, get_akas=False):
|
||||
# type: (TVMazeEpisode, TVMazeShow, bool, bool) -> TVInfoEpisode
|
||||
def _make_episode(self, episode_data, show_data=None, get_images=False, get_akas=False, show_obj=None):
|
||||
# type: (TVMazeEpisode, TVMazeShow, bool, bool, TVInfoShow) -> TVInfoEpisode
|
||||
"""
|
||||
make out of TVMazeEpisode object and optionally TVMazeShow a TVInfoEpisode
|
||||
"""
|
||||
ti_show = TVInfoShow()
|
||||
ti_show.seriesname = clean_data(show_data.name)
|
||||
ti_show.id = show_data.maze_id
|
||||
ti_show.seriesid = ti_show.id
|
||||
ti_show.language = clean_data(show_data.language)
|
||||
ti_show.overview = clean_data(show_data.summary)
|
||||
ti_show.firstaired = clean_data(show_data.premiered)
|
||||
ti_show.runtime = show_data.average_runtime or show_data.runtime
|
||||
ti_show.vote_average = show_data.rating and show_data.rating.get('average')
|
||||
ti_show.popularity = show_data.weight
|
||||
ti_show.genre_list = clean_data(show_data.genres or [])
|
||||
ti_show.genre = '|'.join(ti_show.genre_list).lower()
|
||||
ti_show.official_site = clean_data(show_data.official_site)
|
||||
ti_show.status = clean_data(show_data.status)
|
||||
ti_show.show_type = clean_data((isinstance(show_data.type, string_types) and [show_data.type.lower()] or
|
||||
isinstance(show_data.type, list) and [x.lower() for x in show_data.type] or []))
|
||||
ti_show.lastupdated = show_data.updated
|
||||
ti_show.poster = show_data.image and show_data.image.get('original')
|
||||
if get_akas:
|
||||
ti_show.aliases = [clean_data(a.name) for a in show_data.akas]
|
||||
if 'days' in show_data.schedule:
|
||||
ti_show.airs_dayofweek = ', '.join(clean_data(show_data.schedule['days']))
|
||||
network = show_data.network or show_data.web_channel
|
||||
if network:
|
||||
ti_show.network_is_stream = None is not show_data.web_channel
|
||||
ti_show.network = clean_data(network.name)
|
||||
ti_show.network_id = network.maze_id
|
||||
ti_show.network_country = clean_data(network.country)
|
||||
ti_show.network_country_code = clean_data(network.code)
|
||||
ti_show.network_timezone = clean_data(network.timezone)
|
||||
if get_images and show_data.images:
|
||||
self._set_images(ti_show, show_data, False)
|
||||
ti_show.ids = TVInfoIDs(
|
||||
tvdb=show_data.externals.get('thetvdb'), rage=show_data.externals.get('tvrage'), tvmaze=show_data.id,
|
||||
imdb=clean_data(show_data.externals.get('imdb') and
|
||||
try_int(show_data.externals.get('imdb').replace('tt', ''), None)))
|
||||
ti_show.imdb_id = clean_data(show_data.externals.get('imdb'))
|
||||
if isinstance(ti_show.imdb_id, integer_types):
|
||||
ti_show.imdb_id = 'tt%07d' % ti_show.imdb_id
|
||||
if None is not show_obj:
|
||||
ti_show = show_obj
|
||||
else:
|
||||
ti_show = TVInfoShow()
|
||||
ti_show.seriesname = clean_data(show_data.name)
|
||||
ti_show.id = show_data.maze_id
|
||||
ti_show.seriesid = ti_show.id
|
||||
ti_show.language = clean_data(show_data.language)
|
||||
ti_show.overview = enforce_type(clean_data(show_data.summary), str, '')
|
||||
ti_show.firstaired = clean_data(show_data.premiered)
|
||||
ti_show.runtime = show_data.average_runtime or show_data.runtime
|
||||
ti_show.vote_average = show_data.rating and show_data.rating.get('average')
|
||||
ti_show.rating = ti_show.vote_average
|
||||
ti_show.popularity = show_data.weight
|
||||
ti_show.genre_list = clean_data(show_data.genres or [])
|
||||
ti_show.genre = '|'.join(ti_show.genre_list).lower()
|
||||
ti_show.official_site = clean_data(show_data.official_site)
|
||||
ti_show.status = clean_data(show_data.status)
|
||||
ti_show.show_type = clean_data((isinstance(show_data.type, string_types) and [show_data.type.lower()] or
|
||||
isinstance(show_data.type, list) and [x.lower() for x in show_data.type] or []))
|
||||
ti_show.lastupdated = show_data.updated
|
||||
ti_show.poster = show_data.image and show_data.image.get('original')
|
||||
if get_akas:
|
||||
ti_show.aliases = [clean_data(a.name) for a in show_data.akas]
|
||||
if show_data.schedule and 'days' in show_data.schedule:
|
||||
ti_show.airs_dayofweek = ', '.join(clean_data(show_data.schedule['days']))
|
||||
network = show_data.network or show_data.web_channel
|
||||
if network:
|
||||
ti_show.network_is_stream = None is not show_data.web_channel
|
||||
ti_show.network = clean_data(network.name)
|
||||
ti_show.network_id = network.maze_id
|
||||
ti_show.network_country = clean_data(network.country)
|
||||
ti_show.network_country_code = clean_data(network.code)
|
||||
ti_show.network_timezone = clean_data(network.timezone)
|
||||
if get_images and show_data.images:
|
||||
self._set_images(ti_show, show_data, False)
|
||||
ti_show.ids = TVInfoIDs(
|
||||
tvdb=show_data.externals.get('thetvdb'), rage=show_data.externals.get('tvrage'), tvmaze=show_data.id,
|
||||
imdb=clean_data(show_data.externals.get('imdb') and
|
||||
try_int(show_data.externals.get('imdb').replace('tt', ''), None)))
|
||||
ti_show.imdb_id = clean_data(show_data.externals.get('imdb'))
|
||||
if isinstance(ti_show.imdb_id, integer_types):
|
||||
ti_show.imdb_id = 'tt%07d' % ti_show.imdb_id
|
||||
|
||||
ti_episode = TVInfoEpisode(show=ti_show)
|
||||
ti_episode.id = episode_data.maze_id
|
||||
ti_episode.seasonnumber = episode_data.season_number
|
||||
ti_episode.episodenumber = episode_data.episode_number
|
||||
ti_episode.episodenumber = episode_data.episode_number or 0
|
||||
ti_episode.episodename = clean_data(episode_data.title)
|
||||
ti_episode.airtime = clean_data(episode_data.airtime)
|
||||
try:
|
||||
ti_episode.airtime = datetime.time.fromisoformat(clean_data(episode_data.airtime))
|
||||
except (BaseException, Exception):
|
||||
ti_episode.airtime = None
|
||||
ti_episode.firstaired = clean_data(episode_data.airdate)
|
||||
if episode_data.airstamp:
|
||||
try:
|
||||
|
@ -665,8 +775,13 @@ class TvMaze(TVInfoBase):
|
|||
ti_episode.filename = episode_data.image and (episode_data.image.get('original') or
|
||||
episode_data.image.get('medium'))
|
||||
ti_episode.is_special = episode_data.is_special()
|
||||
ti_episode.overview = clean_data(episode_data.summary)
|
||||
ti_episode.overview = enforce_type(clean_data(episode_data.summary), str, '')
|
||||
ti_episode.runtime = episode_data.runtime
|
||||
if ti_episode.seasonnumber not in ti_show:
|
||||
season = TVInfoSeason(show=ti_show, number=ti_episode.seasonnumber)
|
||||
ti_show[ti_episode.seasonnumber] = season
|
||||
ti_episode.season = season
|
||||
ti_show[ti_episode.seasonnumber][ti_episode.episodenumber] = ti_episode
|
||||
return ti_episode
|
||||
|
||||
def _filtered_schedule(self, **kwargs):
|
||||
|
|
|
@ -62,7 +62,7 @@ if False:
|
|||
from sickgear import db, notifiers as NOTIFIERS
|
||||
# noinspection PyUnresolvedReferences
|
||||
from typing import Any, AnyStr, Dict, Generator, NoReturn, integer_types, Iterable, Iterator, List, Optional, \
|
||||
Tuple, Union
|
||||
Tuple, Type, Union
|
||||
|
||||
html_convert_fractions = {0: '', 25: '¼', 50: '½', 75: '¾', 100: 1}
|
||||
|
||||
|
@ -1758,3 +1758,16 @@ def is_virtualenv():
|
|||
"""Get base/real prefix, or `sys.prefix` if there is none."""
|
||||
get_base_prefix_compat = getattr(sys, 'base_prefix', None) or getattr(sys, 'real_prefix', None) or sys.prefix
|
||||
return get_base_prefix_compat != sys.prefix
|
||||
|
||||
|
||||
def enforce_type(value, allowed_types, default):
|
||||
# type: (Any, Union[Type, Tuple[Type]], Any) -> Any
|
||||
"""
|
||||
enforces that value is given type(s)
|
||||
:param value: value to check
|
||||
:param allowed_types: type or tuple of types allowed
|
||||
:param default: value to return if other type
|
||||
"""
|
||||
if not isinstance(value, allowed_types):
|
||||
return default
|
||||
return value
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
import copy
|
||||
import datetime
|
||||
import diskcache
|
||||
import itertools
|
||||
import logging
|
||||
import threading
|
||||
import shutil
|
||||
import time
|
||||
from collections import deque
|
||||
from exceptions_helper import ex
|
||||
|
||||
from six import integer_types, iteritems, iterkeys, string_types, text_type
|
||||
from six import PY2 # deprecate after rebase ?
|
||||
from _23 import list_keys, list_items, list_values, izip # deprecate after rebase ?
|
||||
from typing import Callable
|
||||
|
||||
|
||||
from lib.tvinfo_base.exceptions import *
|
||||
from sg_helpers import calc_age, make_path
|
||||
|
@ -44,6 +50,11 @@ TVINFO_INSTAGRAM = 250002
|
|||
TVINFO_WIKIPEDIA = 250003
|
||||
TVINFO_REDDIT = 250004
|
||||
TVINFO_YOUTUBE = 250005
|
||||
TVINFO_WIKIDATA = 250006
|
||||
TVINFO_TIKTOK = 250007
|
||||
TVINFO_LINKEDIN = 25008
|
||||
TVINFO_OFFICIALSITE = 250009
|
||||
TVINFO_FANSITE = 250010
|
||||
|
||||
tv_src_names = {
|
||||
TVINFO_TVDB: 'tvdb',
|
||||
|
@ -64,10 +75,25 @@ tv_src_names = {
|
|||
TVINFO_INSTAGRAM: 'instagram',
|
||||
TVINFO_WIKIPEDIA: 'wikipedia',
|
||||
TVINFO_REDDIT: 'reddit',
|
||||
TVINFO_YOUTUBE: 'youtube'
|
||||
TVINFO_YOUTUBE: 'youtube',
|
||||
TVINFO_WIKIDATA: 'wikidata',
|
||||
TVINFO_TIKTOK: 'tiktok',
|
||||
TVINFO_LINKEDIN: 'linkedin',
|
||||
TVINFO_OFFICIALSITE: 'officialsite',
|
||||
TVINFO_FANSITE: 'fansite'
|
||||
|
||||
}
|
||||
|
||||
TVINFO_MID_SEASON_FINALE = 1
|
||||
TVINFO_SEASON_FINALE = 2
|
||||
TVINFO_SERIES_FINALE = 3
|
||||
|
||||
final_types = {
|
||||
TVINFO_MID_SEASON_FINALE: 'mid-season',
|
||||
TVINFO_SEASON_FINALE: 'season',
|
||||
TVINFO_SERIES_FINALE: 'series'
|
||||
}
|
||||
|
||||
log = logging.getLogger('TVInfo')
|
||||
log.addHandler(logging.NullHandler())
|
||||
TVInfoShowContainer = {} # type: Union[ShowContainer, Dict]
|
||||
|
@ -141,20 +167,42 @@ class TVInfoIDs(object):
|
|||
return {TVINFO_TVDB: self.tvdb, TVINFO_TMDB: self.tmdb, TVINFO_TVMAZE: self.tvmaze,
|
||||
TVINFO_IMDB: self.imdb, TVINFO_TRAKT: self.trakt, TVINFO_TVRAGE: self.rage}.get(key)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self.__dict__[{
|
||||
TVINFO_TVDB: 'tvdb', TVINFO_TMDB: 'tmdb', TVINFO_TVMAZE: 'tvmaze',
|
||||
TVINFO_IMDB: 'imdb', TVINFO_TRAKT: 'trakt', TVINFO_TVRAGE: 'rage'
|
||||
}[key]] = value
|
||||
|
||||
def get(self, key):
|
||||
return self.__getitem__(key)
|
||||
|
||||
def keys(self):
|
||||
for k, v in iter(((TVINFO_TVDB, self.tvdb), (TVINFO_TMDB, self.tmdb), (TVINFO_TVMAZE, self.tvmaze),
|
||||
(TVINFO_IMDB, self.imdb), (TVINFO_TRAKT, self.trakt), (TVINFO_TVRAGE, self.rage))):
|
||||
if None is not v:
|
||||
yield k
|
||||
|
||||
def __iter__(self):
|
||||
for s, v in [(TVINFO_TVDB, self.tvdb), (TVINFO_TMDB, self.tmdb), (TVINFO_TVMAZE, self.tvmaze),
|
||||
(TVINFO_IMDB, self.imdb), (TVINFO_TRAKT, self.trakt), (TVINFO_TVRAGE, self.rage)]:
|
||||
yield s, v
|
||||
for s, v in iter(((TVINFO_TVDB, self.tvdb), (TVINFO_TMDB, self.tmdb), (TVINFO_TVMAZE, self.tvmaze),
|
||||
(TVINFO_IMDB, self.imdb), (TVINFO_TRAKT, self.trakt), (TVINFO_TVRAGE, self.rage))):
|
||||
if None is not v:
|
||||
yield s, v
|
||||
|
||||
def __len__(self):
|
||||
counter = itertools.count()
|
||||
deque(izip(self.__iter__(), counter), maxlen=0) # (consume at C speed)
|
||||
return next(counter)
|
||||
|
||||
def __str__(self):
|
||||
return ', '.join('%s: %s' % (tv_src_names.get(k, k), v) for k, v in self.__iter__())
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.__dict__ == other.__dict__
|
||||
|
||||
__repr__ = __str__
|
||||
iteritems = __iter__
|
||||
items = __iter__
|
||||
iterkeys = keys
|
||||
|
||||
|
||||
class TVInfoSocialIDs(object):
|
||||
|
@ -166,7 +214,11 @@ class TVInfoSocialIDs(object):
|
|||
wikipedia=None, # type: str_int
|
||||
ids=None, # type: Dict[int, str_int]
|
||||
reddit=None, # type: str_int
|
||||
youtube=None # type: AnyStr
|
||||
youtube=None, # type: AnyStr
|
||||
wikidata=None, # type: AnyStr
|
||||
tiktok=None, # type: AnyStr
|
||||
linkedin=None, # type: AnyStr
|
||||
fansite=None # type: AnyStr
|
||||
):
|
||||
ids = ids or {}
|
||||
self.twitter = twitter or ids.get(TVINFO_TWITTER)
|
||||
|
@ -175,23 +227,60 @@ class TVInfoSocialIDs(object):
|
|||
self.wikipedia = wikipedia or ids.get(TVINFO_WIKIPEDIA)
|
||||
self.reddit = reddit or ids.get(TVINFO_REDDIT)
|
||||
self.youtube = youtube or ids.get(TVINFO_YOUTUBE)
|
||||
self.wikidata = wikidata or ids.get(TVINFO_WIKIDATA)
|
||||
self.tiktok = tiktok or ids.get(TVINFO_TIKTOK)
|
||||
self.linkedin = linkedin or ids.get(TVINFO_LINKEDIN)
|
||||
self.fansite = fansite or ids.get(TVINFO_FANSITE)
|
||||
|
||||
def __getitem__(self, key):
|
||||
return {TVINFO_TWITTER: self.twitter, TVINFO_INSTAGRAM: self.instagram, TVINFO_FACEBOOK: self.facebook,
|
||||
TVINFO_WIKIPEDIA: self.wikipedia, TVINFO_REDDIT: self.reddit, TVINFO_YOUTUBE: self.youtube}.get(key)
|
||||
TVINFO_WIKIDATA: self.wikidata, TVINFO_WIKIPEDIA: self.wikipedia, TVINFO_REDDIT: self.reddit,
|
||||
TVINFO_TIKTOK: self.tiktok, TVINFO_LINKEDIN: self.linkedin, TVINFO_FANSITE: self.fansite,
|
||||
TVINFO_YOUTUBE: self.youtube}.get(key)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self.__dict__[{
|
||||
TVINFO_TWITTER: 'twitter', TVINFO_INSTAGRAM: 'instagram', TVINFO_FACEBOOK: 'facebook',
|
||||
TVINFO_WIKIPEDIA: 'wikipedia', TVINFO_REDDIT: 'reddit', TVINFO_YOUTUBE: 'youtube',
|
||||
TVINFO_WIKIDATA: 'wikidata', TVINFO_TIKTOK: 'tiktok', TVINFO_LINKEDIN: 'linkedin', TVINFO_FANSITE: 'fansite'
|
||||
}[key]] = value
|
||||
|
||||
def get(self, key):
|
||||
return self.__getitem__(key)
|
||||
|
||||
def keys(self):
|
||||
for k, v in iter(((TVINFO_TWITTER, self.twitter), (TVINFO_INSTAGRAM, self.instagram),
|
||||
(TVINFO_FACEBOOK, self.facebook), (TVINFO_TIKTOK, self.tiktok),
|
||||
(TVINFO_WIKIPEDIA, self.wikipedia), (TVINFO_WIKIDATA, self.wikidata),
|
||||
(TVINFO_REDDIT, self.reddit), (TVINFO_YOUTUBE, self.youtube),
|
||||
(TVINFO_LINKEDIN, self.linkedin), (TVINFO_FANSITE, self.fansite))):
|
||||
if None is not v:
|
||||
yield k
|
||||
|
||||
def __iter__(self):
|
||||
for s, v in [(TVINFO_TWITTER, self.twitter), (TVINFO_INSTAGRAM, self.instagram),
|
||||
(TVINFO_FACEBOOK, self.facebook), (TVINFO_WIKIPEDIA, self.wikipedia),
|
||||
(TVINFO_REDDIT, self.reddit), (TVINFO_YOUTUBE, self.youtube)]:
|
||||
yield s, v
|
||||
for s, v in iter(((TVINFO_TWITTER, self.twitter), (TVINFO_INSTAGRAM, self.instagram),
|
||||
(TVINFO_FACEBOOK, self.facebook), (TVINFO_TIKTOK, self.tiktok),
|
||||
(TVINFO_WIKIPEDIA, self.wikipedia), (TVINFO_WIKIDATA, self.wikidata),
|
||||
(TVINFO_REDDIT, self.reddit), (TVINFO_YOUTUBE, self.youtube),
|
||||
(TVINFO_LINKEDIN, self.linkedin), (TVINFO_FANSITE, self.fansite))):
|
||||
if None is not v:
|
||||
yield s, v
|
||||
|
||||
def __len__(self):
|
||||
counter = itertools.count()
|
||||
deque(izip(self.__iter__(), counter), maxlen=0) # (consume at C speed)
|
||||
return next(counter)
|
||||
|
||||
def __str__(self):
|
||||
return ', '.join('%s: %s' % (tv_src_names.get(k, k), v) for k, v in self.__iter__())
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.__dict__ == other.__dict__
|
||||
|
||||
__repr__ = __str__
|
||||
iteritems = __iter__
|
||||
items = __iter__
|
||||
iterkeys = keys
|
||||
|
||||
|
||||
class TVInfoImageType(object):
|
||||
|
@ -242,7 +331,7 @@ class TVInfoImageSize(object):
|
|||
|
||||
class TVInfoImage(object):
|
||||
def __init__(self, image_type, sizes, img_id=None, main_image=False, type_str='', rating=None, votes=None,
|
||||
lang=None, height=None, width=None, aspect_ratio=None):
|
||||
lang=None, height=None, width=None, aspect_ratio=None, updated_at=None):
|
||||
self.img_id = img_id # type: Optional[integer_types]
|
||||
self.image_type = image_type # type: integer_types
|
||||
self.sizes = sizes # type: Union[TVInfoImageSize, Dict]
|
||||
|
@ -254,6 +343,10 @@ class TVInfoImage(object):
|
|||
self.height = height # type: Optional[integer_types]
|
||||
self.width = width # type: Optional[integer_types]
|
||||
self.aspect_ratio = aspect_ratio # type: Optional[Union[float, integer_types]]
|
||||
self.updated_at = updated_at # type: Optional[integer_types]
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.__dict__ == other.__dict__
|
||||
|
||||
def __str__(self):
|
||||
return '<TVInfoImage %s [%s]>' % (TVInfoImageType.reverse_str.get(self.image_type, 'unknown'),
|
||||
|
@ -263,13 +356,20 @@ class TVInfoImage(object):
|
|||
|
||||
|
||||
class TVInfoNetwork(object):
|
||||
def __init__(self, name, n_id=None, country=None, country_code=None, timezone=None, stream=None):
|
||||
def __init__(self, name, n_id=None, country=None, country_code=None, timezone=None, stream=None, active_date=None,
|
||||
inactive_date=None):
|
||||
# type: (AnyStr, integer_types, AnyStr, AnyStr, AnyStr, bool, AnyStr, AnyStr) -> None
|
||||
self.name = name # type: AnyStr
|
||||
self.id = n_id # type: Optional[integer_types]
|
||||
self.country = country # type: Optional[AnyStr]
|
||||
self.country_code = country_code # type: Optional[AnyStr]
|
||||
self.timezone = timezone # type: Optional[AnyStr]
|
||||
self.stream = stream # type: Optional[bool]
|
||||
self.active_date = active_date # type: Optional[AnyStr]
|
||||
self.inactive_date = inactive_date # type: Optional[AnyStr]
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.__dict__ == other.__dict__
|
||||
|
||||
def __str__(self):
|
||||
return '<Network (%s)>' % ', '.join('%s' % s for s in [self.name, self.id, self.country, self.country_code,
|
||||
|
@ -282,7 +382,7 @@ class TVInfoShow(dict):
|
|||
"""Holds a dict of seasons, and show data.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, show_loaded=True):
|
||||
dict.__init__(self)
|
||||
self.lock = threading.RLock()
|
||||
self.data = {} # type: Dict
|
||||
|
@ -298,7 +398,6 @@ class TVInfoShow(dict):
|
|||
self.ids = TVInfoIDs() # type: TVInfoIDs
|
||||
self.social_ids = TVInfoSocialIDs() # type: TVInfoSocialIDs
|
||||
self.slug = None # type: Optional[AnyStr]
|
||||
self.seriesid = None # type: integer_types
|
||||
self.seriesname = None # type: Optional[AnyStr]
|
||||
self.aliases = [] # type: List[AnyStr]
|
||||
self.season = None # type: integer_types
|
||||
|
@ -318,6 +417,7 @@ class TVInfoShow(dict):
|
|||
self.network_is_stream = None # type: Optional[bool]
|
||||
self.runtime = None # type: integer_types
|
||||
self.language = None # type: Optional[AnyStr]
|
||||
self.spoken_languages = [] # type: List[string_types]
|
||||
self.official_site = None # type: Optional[AnyStr]
|
||||
self.imdb_id = None # type: Optional[AnyStr]
|
||||
self.zap2itid = None # type: Optional[AnyStr]
|
||||
|
@ -332,7 +432,7 @@ class TVInfoShow(dict):
|
|||
self.contentrating = None # type: Optional[AnyStr]
|
||||
self.rating = None # type: Union[integer_types, float]
|
||||
self.status = None # type: Optional[AnyStr]
|
||||
self.overview = None # type: Optional[AnyStr]
|
||||
self.overview = '' # type: AnyStr
|
||||
self.poster = None # type: Optional[AnyStr]
|
||||
self.poster_thumb = None # type: Optional[AnyStr]
|
||||
self.banner = None # type: Optional[AnyStr]
|
||||
|
@ -347,6 +447,33 @@ class TVInfoShow(dict):
|
|||
self.vote_average = None # type: Optional[Union[integer_types, float]]
|
||||
self.origin_countries = [] # type: List[AnyStr]
|
||||
self.requested_language = '' # type: AnyStr
|
||||
self.alt_ep_numbering = {} # type: Dict[Any, Dict[integer_types, Dict[integer_types, TVInfoEpisode]]]
|
||||
self.watcher_count = None # type: integer_types
|
||||
self.play_count = None # type: integer_types
|
||||
self.collected_count = None # type: integer_types
|
||||
self.collector_count = None # type: integer_types
|
||||
self.next_season_airdate = None # type: Optional[string_types]
|
||||
# trailers dict containing: {language: trailer url} , 'any' for unknown langauge
|
||||
self.trailers = {} # type: Dict[string_types, string_types]
|
||||
self.show_loaded = show_loaded # type: bool
|
||||
self.load_method = None # type: Optional[Callable]
|
||||
|
||||
def load_data(self):
|
||||
if not self.show_loaded and self.id and isinstance(self.load_method, Callable):
|
||||
_new_show_data = self.load_method(self.id, load_actors=False)
|
||||
if isinstance(_new_show_data, TVInfoShow):
|
||||
self.__dict__.update(_new_show_data.__dict__)
|
||||
self.show_loaded = True
|
||||
|
||||
@property
|
||||
def seriesid(self):
|
||||
# type: (...) -> integer_types
|
||||
return self.id
|
||||
|
||||
@seriesid.setter
|
||||
def seriesid(self, val):
|
||||
# type: (integer_types) -> None
|
||||
self.id = val
|
||||
|
||||
def __str__(self):
|
||||
nr_seasons = len(self)
|
||||
|
@ -363,7 +490,7 @@ class TVInfoShow(dict):
|
|||
|
||||
raise AttributeError
|
||||
|
||||
def __getitem__(self, key, raise_error=True):
|
||||
def __getitem__(self, key):
|
||||
if isinstance(key, string_types) and key in self.__dict__:
|
||||
return self.__dict__[key]
|
||||
|
||||
|
@ -375,18 +502,21 @@ class TVInfoShow(dict):
|
|||
# Non-numeric request is for show-data
|
||||
return dict.__getitem__(self.data, key)
|
||||
|
||||
if raise_error:
|
||||
# Data wasn't found, raise appropriate error
|
||||
if isinstance(key, integer_types) or isinstance(key, string_types) and key.isdigit():
|
||||
# Episode number x was not found
|
||||
raise BaseTVinfoSeasonnotfound('Could not find season %s' % (repr(key)))
|
||||
else:
|
||||
# If it's not numeric, it must be an attribute name, which
|
||||
# doesn't exist, so attribute error.
|
||||
raise BaseTVinfoAttributenotfound('Cannot find attribute %s' % (repr(key)))
|
||||
# Data wasn't found, raise appropriate error
|
||||
if isinstance(key, integer_types) or isinstance(key, string_types) and key.isdigit():
|
||||
# Episode number x was not found
|
||||
raise BaseTVinfoSeasonnotfound('Could not find season %s' % (repr(key)))
|
||||
else:
|
||||
# If it's not numeric, it must be an attribute name, which
|
||||
# doesn't exist, so attribute error.
|
||||
raise BaseTVinfoAttributenotfound('Cannot find attribute %s' % (repr(key)))
|
||||
|
||||
def get(self, __key, __default=None):
|
||||
return self.__getitem__(__key, raise_error=None is __default) or __default
|
||||
def get(self, __key, *args):
|
||||
try:
|
||||
return self.__getitem__(__key)
|
||||
except (BaseException, Exception):
|
||||
if 0 != len(args):
|
||||
return args[0]
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
cls = self.__class__
|
||||
|
@ -395,6 +525,8 @@ class TVInfoShow(dict):
|
|||
for k, v in self.__dict__.items():
|
||||
if 'lock' == k:
|
||||
setattr(result, k, threading.RLock())
|
||||
elif 'load_method' == k:
|
||||
setattr(result, k, None)
|
||||
else:
|
||||
setattr(result, k, copy.deepcopy(v, memo))
|
||||
for k, v in self.items():
|
||||
|
@ -434,33 +566,35 @@ class TVInfoShow(dict):
|
|||
|
||||
def __getstate__(self):
|
||||
d = dict(self.__dict__)
|
||||
try:
|
||||
del d['lock']
|
||||
except (BaseException, Exception):
|
||||
pass
|
||||
for d_a in ('lock', 'load_method'):
|
||||
try:
|
||||
del d[d_a]
|
||||
except (BaseException, Exception):
|
||||
pass
|
||||
return d
|
||||
|
||||
def __setstate__(self, d):
|
||||
self.__dict__ = d
|
||||
self.lock = threading.RLock()
|
||||
self.load_method = None
|
||||
|
||||
__repr__ = __str__
|
||||
__nonzero__ = __bool__
|
||||
|
||||
|
||||
class TVInfoSeason(dict):
|
||||
def __init__(self, show=None, **kwargs):
|
||||
def __init__(self, show=None, number=None, **kwargs):
|
||||
"""The show attribute points to the parent show
|
||||
"""
|
||||
super(TVInfoSeason, self).__init__(**kwargs)
|
||||
self.show = show # type: TVInfoShow
|
||||
self.id = None # type: integer_types
|
||||
self.number = None # type: integer_types
|
||||
self.number = number # type: integer_types
|
||||
self.name = None # type: Optional[AnyStr]
|
||||
self.actors = [] # type: List[Dict]
|
||||
self.cast = CastList() # type: Dict[integer_types, TVInfoCharacter]
|
||||
self.network = None # type: Optional[AnyStr]
|
||||
self.network_id = None # type: integer_types
|
||||
self.network_id = None # type: Optional[integer_types]
|
||||
self.network_timezone = None # type: Optional[AnyStr]
|
||||
self.network_country = None # type: Optional[AnyStr]
|
||||
self.network_country_code = None # type: Optional[AnyStr]
|
||||
|
@ -536,7 +670,7 @@ class TVInfoEpisode(dict):
|
|||
self.writers = [] # type: List[AnyStr]
|
||||
self.crew = CrewList() # type: CrewList
|
||||
self.episodename = None # type: Optional[AnyStr]
|
||||
self.overview = None # type: Optional[AnyStr]
|
||||
self.overview = '' # type: AnyStr
|
||||
self.language = {'episodeName': None, 'overview': None} # type: Dict[AnyStr, Optional[AnyStr]]
|
||||
self.productioncode = None # type: Optional[AnyStr]
|
||||
self.showurl = None # type: Optional[AnyStr]
|
||||
|
@ -564,17 +698,21 @@ class TVInfoEpisode(dict):
|
|||
self.contentrating = None # type: Optional[AnyStr]
|
||||
self.thumbadded = None # type: Optional[AnyStr]
|
||||
self.rating = None # type: Union[integer_types, float]
|
||||
self.vote_count = None # type: integer_types
|
||||
self.siteratingcount = None # type: integer_types
|
||||
self.show = show # type: Optional[TVInfoShow]
|
||||
self.alt_nums = {} # type: Dict[AnyStr, Dict[integer_types, integer_types]]
|
||||
self.finale_type = None # type: Optional[integer_types]
|
||||
|
||||
def __str__(self):
|
||||
show_name = (self.show and self.show.seriesname and '<Show %s> - ' % self.show.seriesname) or ''
|
||||
seasno, epno = int(getattr(self, 'seasonnumber', 0)), int(getattr(self, 'episodenumber', 0))
|
||||
seasno, epno = int(getattr(self, 'seasonnumber', 0) or 0), int(getattr(self, 'episodenumber', 0) or 0)
|
||||
epname = getattr(self, 'episodename', '')
|
||||
finale_str = (self.finale_type and ' (%s finale)' % final_types.get(self.finale_type).capitalize()) or ''
|
||||
if None is not epname:
|
||||
return '%s<Episode %02dx%02d - %r>' % (show_name, seasno, epno, epname)
|
||||
return '%s<Episode %02dx%02d - %r%s>' % (show_name, seasno, epno, epname, finale_str)
|
||||
else:
|
||||
return '%s<Episode %02dx%02d>' % (show_name, seasno, epno)
|
||||
return '%s<Episode %02dx%02d%s>' % (show_name, seasno, epno, finale_str)
|
||||
|
||||
def __getattr__(self, key):
|
||||
if key in self:
|
||||
|
@ -696,7 +834,7 @@ class PersonBase(dict):
|
|||
country=None, # type: AnyStr
|
||||
country_code=None, # type: AnyStr
|
||||
country_timezone=None, # type: AnyStr
|
||||
ids=None, # type: Dict
|
||||
ids=None, # type: TVInfoIDs
|
||||
thumb_url=None, # type: AnyStr
|
||||
**kwargs # type: Dict
|
||||
):
|
||||
|
@ -713,7 +851,7 @@ class PersonBase(dict):
|
|||
self.country = country # type: Optional[AnyStr]
|
||||
self.country_code = country_code # type: Optional[AnyStr]
|
||||
self.country_timezone = country_timezone # type: Optional[AnyStr]
|
||||
self.ids = ids or {} # type: Dict[int, integer_types]
|
||||
self.ids = ids or TVInfoIDs() # type: TVInfoIDs
|
||||
|
||||
def calc_age(self, date=None):
|
||||
# type: (Optional[datetime.date]) -> Optional[int]
|
||||
|
@ -778,9 +916,9 @@ class TVInfoPerson(PersonBase):
|
|||
country=None, # type: AnyStr
|
||||
country_code=None, # type: AnyStr
|
||||
country_timezone=None, # type: AnyStr
|
||||
ids=None, # type: Dict
|
||||
ids=None, # type: TVInfoIDs
|
||||
homepage=None, # type: Optional[AnyStr]
|
||||
social_ids=None, # type: Dict
|
||||
social_ids=None, # type: TVInfoSocialIDs
|
||||
birthplace=None, # type: AnyStr
|
||||
deathplace=None, # type: AnyStr
|
||||
url=None, # type: AnyStr
|
||||
|
@ -797,7 +935,7 @@ class TVInfoPerson(PersonBase):
|
|||
country_code=country_code, country_timezone=country_timezone, ids=ids, **kwargs)
|
||||
self.credits = [] # type: List
|
||||
self.homepage = homepage # type: Optional[AnyStr]
|
||||
self.social_ids = social_ids or {} # type: Dict
|
||||
self.social_ids = social_ids or TVInfoSocialIDs() # type: TVInfoSocialIDs
|
||||
self.birthplace = birthplace # type: Optional[AnyStr]
|
||||
self.deathplace = deathplace # type: Optional[AnyStr]
|
||||
self.nicknames = nicknames or set() # type: Set[AnyStr]
|
||||
|
@ -815,9 +953,9 @@ class TVInfoPerson(PersonBase):
|
|||
|
||||
class TVInfoCharacter(PersonBase):
|
||||
def __init__(self, person=None, voice=None, plays_self=None, regular=None, ti_show=None, start_year=None,
|
||||
end_year=None, **kwargs):
|
||||
# type: (List[TVInfoPerson], bool, bool, bool, TVInfoShow, int, int, Dict) -> None
|
||||
super(TVInfoCharacter, self).__init__(**kwargs)
|
||||
end_year=None, ids=None, name=None, episode_count=None, guest_episodes_numbers=None, **kwargs):
|
||||
# type: (List[TVInfoPerson], bool, bool, bool, TVInfoShow, int, int, TVInfoIDs, AnyStr, int, Dict[int, List[int]], ...) -> None
|
||||
super(TVInfoCharacter, self).__init__(ids=ids, **kwargs)
|
||||
self.person = person # type: List[TVInfoPerson]
|
||||
self.voice = voice # type: Optional[bool]
|
||||
self.plays_self = plays_self # type: Optional[bool]
|
||||
|
@ -825,14 +963,19 @@ class TVInfoCharacter(PersonBase):
|
|||
self.ti_show = ti_show # type: Optional[TVInfoShow]
|
||||
self.start_year = start_year # type: Optional[integer_types]
|
||||
self.end_year = end_year # type: Optional[integer_types]
|
||||
self.name = name # type: Optional[AnyStr]
|
||||
self.episode_count = episode_count # type: Optional[int]
|
||||
self.guest_episodes_numbers = guest_episodes_numbers or {} # type: Dict[int, List[int]]
|
||||
|
||||
def __str__(self):
|
||||
pn = []
|
||||
char_type = ('', ' [Guest]')[False is self.regular]
|
||||
char_show = None is not self.ti_show and ' [%s]' % self.ti_show.seriesname
|
||||
if None is not self.person:
|
||||
for p in self.person:
|
||||
if getattr(p, 'name', None):
|
||||
pn.append(p.name)
|
||||
return '<Character "%s%s">' % (self.name, ('', ' - (%s)' % ', '.join(pn))[bool(pn)])
|
||||
return '<Character%s "%s%s%s">' % (char_type, self.name, ('', ' - (%s)' % ', '.join(pn))[bool(pn)], char_show)
|
||||
|
||||
__repr__ = __str__
|
||||
|
||||
|
@ -889,6 +1032,12 @@ class RoleTypes(object):
|
|||
crew_type_names = {c.lower(): v for v, c in iteritems(RoleTypes.reverse) if v >= RoleTypes.crew_limit}
|
||||
|
||||
|
||||
class TVInfoSeasonTypes(object):
|
||||
default = 'default'
|
||||
official = 'official'
|
||||
dvd = 'dvd'
|
||||
|
||||
|
||||
class TVInfoBase(object):
|
||||
supported_id_searches = []
|
||||
supported_person_id_searches = []
|
||||
|
@ -900,7 +1049,7 @@ class TVInfoBase(object):
|
|||
reverse_map_languages = {v: k for k, v in iteritems(map_languages)}
|
||||
|
||||
def __init__(self, banners=False, posters=False, seasons=False, seasonwides=False, fanart=False, actors=False,
|
||||
*args, **kwargs):
|
||||
dvdorder=False, *args, **kwargs):
|
||||
global TVInfoShowContainer
|
||||
if self.__class__.__name__ not in TVInfoShowContainer:
|
||||
TVInfoShowContainer[self.__class__.__name__] = ShowContainer()
|
||||
|
@ -934,6 +1083,7 @@ class TVInfoBase(object):
|
|||
'fanart_enabled': fanart,
|
||||
'actors_enabled': actors,
|
||||
'cache_search': kwargs.get('cache_search'),
|
||||
'dvdorder': dvdorder,
|
||||
} # type: Dict[AnyStr, Any]
|
||||
|
||||
def _must_load_data(self, sid, load_episodes, banners, posters, seasons, seasonwides, fanart, actors, lang):
|
||||
|
@ -1148,7 +1298,22 @@ class TVInfoBase(object):
|
|||
if None is self.ti_shows[show_id].id:
|
||||
with self.ti_shows.lock:
|
||||
del self.ti_shows[show_id]
|
||||
return None if show_id not in self.ti_shows else copy.deepcopy(self.ti_shows[show_id])
|
||||
if show_id not in self.ti_shows:
|
||||
return None
|
||||
else:
|
||||
show_copy = copy.deepcopy(self.ti_shows[show_id]) # type: TVInfoShow
|
||||
# provide old call compatibility for dvd order
|
||||
if self.config.get('dvdorder') and TVInfoSeasonTypes.dvd in show_copy.alt_ep_numbering:
|
||||
org_seasons, dvd_seasons = list_keys(show_copy), \
|
||||
list_keys(show_copy.alt_ep_numbering[TVInfoSeasonTypes.dvd])
|
||||
for r_season in set(org_seasons) - set(dvd_seasons):
|
||||
try:
|
||||
del show_copy[r_season]
|
||||
except (BaseException, Exception):
|
||||
continue
|
||||
for ti_season in dvd_seasons:
|
||||
show_copy[ti_season] = show_copy.alt_ep_numbering[TVInfoSeasonTypes.dvd][ti_season]
|
||||
return show_copy
|
||||
finally:
|
||||
try:
|
||||
if None is self.ti_shows[show_id].id:
|
||||
|
@ -1166,12 +1331,13 @@ class TVInfoBase(object):
|
|||
self._old_config = None
|
||||
|
||||
# noinspection PyMethodMayBeStatic
|
||||
def _search_show(self, name=None, ids=None, **kwargs):
|
||||
# type: (Union[AnyStr, List[AnyStr]], Dict[integer_types, integer_types], Optional[Any]) -> List[Dict]
|
||||
def _search_show(self, name=None, ids=None, lang=None, **kwargs):
|
||||
# type: (Union[AnyStr, List[AnyStr]], Dict[integer_types, integer_types], Optional[string_types], Optional[Any]) -> List[Dict]
|
||||
"""
|
||||
internal search function to find shows, should be overwritten in class
|
||||
:param name: name to search for
|
||||
:param ids: dict of ids {tvid: prodid} to search for
|
||||
:param lang: language code
|
||||
"""
|
||||
return []
|
||||
|
||||
|
@ -1190,6 +1356,7 @@ class TVInfoBase(object):
|
|||
self,
|
||||
name=None, # type: Union[AnyStr, List[AnyStr]]
|
||||
ids=None, # type: Dict[integer_types, integer_types]
|
||||
lang=None, # type: Optional[string_types]
|
||||
# **kwargs # type: Optional[Any]
|
||||
):
|
||||
# type: (...) -> List[Dict]
|
||||
|
@ -1198,8 +1365,13 @@ class TVInfoBase(object):
|
|||
|
||||
:param name: series name or list of names to search for
|
||||
:param ids: dict of ids {tvid: prodid} to search for
|
||||
:param lang: language code
|
||||
:return: combined list of series results
|
||||
"""
|
||||
if None is lang:
|
||||
if self.config.get('language'):
|
||||
lang = self.config['language']
|
||||
lang = self.map_languages.get(lang, lang)
|
||||
if not name and not ids:
|
||||
log.debug('Nothing to search')
|
||||
raise BaseTVinfoShownotfound('Nothing to search')
|
||||
|
@ -1208,14 +1380,15 @@ class TVInfoBase(object):
|
|||
if not name and not any(1 for i in ids if i in self.supported_id_searches):
|
||||
log.debug('Id type not supported')
|
||||
raise BaseTVinfoShownotfound('Id type not supported')
|
||||
selected_series = self._search_show(name=name, ids=ids)
|
||||
selected_series = self._search_show(name=name, ids=ids, lang=lang)
|
||||
elif name:
|
||||
selected_series = self._search_show(name)
|
||||
selected_series = self._search_show(name, lang=lang)
|
||||
if isinstance(selected_series, dict):
|
||||
selected_series = [selected_series]
|
||||
if not isinstance(selected_series, list) or 0 == len(selected_series):
|
||||
log.debug('Series result returned zero')
|
||||
raise BaseTVinfoShownotfound('Show-name search returned zero results (cannot find show on TVDB)')
|
||||
raise BaseTVinfoShownotfound('Show-name search returned zero results (cannot find show on %s)' %
|
||||
self.__class__.__name__)
|
||||
return selected_series
|
||||
|
||||
def _set_item(self, sid, seas, ep, attrib, value):
|
||||
|
@ -1278,6 +1451,24 @@ class TVInfoBase(object):
|
|||
"""
|
||||
return {}
|
||||
|
||||
def get_similar(self, tvid, result_count=100, **kwargs):
|
||||
# type: (integer_types, int, Any) -> List[TVInfoShow]
|
||||
"""
|
||||
return list of similar shows to given id
|
||||
:param tvid: id to give similar shows for
|
||||
:param result_count: count of results requested
|
||||
"""
|
||||
return []
|
||||
|
||||
def get_recommended_for_show(self, tvid, result_count=100, **kwargs):
|
||||
# type: (integer_types, int, Any) -> List[TVInfoShow]
|
||||
"""
|
||||
list of recommended shows to the provided tv id
|
||||
:param tvid: id to find recommended shows for
|
||||
:param result_count: result count to returned
|
||||
"""
|
||||
return []
|
||||
|
||||
def get_trending(self, result_count=100, **kwargs):
|
||||
# type: (...) -> List[TVInfoShow]
|
||||
"""
|
||||
|
@ -1296,16 +1487,30 @@ class TVInfoBase(object):
|
|||
def get_top_rated(self, result_count=100, **kwargs):
|
||||
# type: (...) -> List[TVInfoShow]
|
||||
"""
|
||||
get all latest shows
|
||||
get top rated shows
|
||||
"""
|
||||
return []
|
||||
|
||||
def get_new_shows(self, result_count=100, **kwargs):
|
||||
# type: (...) -> List[TVInfoShow]
|
||||
"""
|
||||
get new shows
|
||||
"""
|
||||
return []
|
||||
|
||||
def get_new_seasons(self, result_count=100, **kwargs):
|
||||
# type: (...) -> List[TVInfoShow]
|
||||
"""
|
||||
get new seasons
|
||||
"""
|
||||
return []
|
||||
|
||||
def discover(self, result_count=100, get_extra_images=False, **kwargs):
|
||||
# type: (...) -> List[TVInfoEpisode]
|
||||
# type: (...) -> List[TVInfoShow]
|
||||
return []
|
||||
|
||||
def get_premieres(self, **kwargs):
|
||||
# type: (...) -> List[TVInfoEpisode]
|
||||
# type: (...) -> List[TVInfoShow]
|
||||
"""
|
||||
get all premiering shows
|
||||
"""
|
||||
|
@ -1318,6 +1523,93 @@ class TVInfoBase(object):
|
|||
"""
|
||||
return []
|
||||
|
||||
def get_most_played(self, result_count=100, **kwargs):
|
||||
# type: (...) -> List[TVInfoShow]
|
||||
"""
|
||||
get most played shows
|
||||
:param result_count: how many results are suppose to be returned
|
||||
"""
|
||||
return []
|
||||
|
||||
def get_most_watched(self, result_count=100, **kwargs):
|
||||
# type: (...) -> List[TVInfoShow]
|
||||
"""
|
||||
get most watched shows
|
||||
:param result_count: how many results are suppose to be returned
|
||||
"""
|
||||
return []
|
||||
|
||||
def get_most_collected(self, result_count=100, **kwargs):
|
||||
# type: (...) -> List[TVInfoShow]
|
||||
"""
|
||||
get most collected shows
|
||||
:param result_count: how many results are suppose to be returned
|
||||
"""
|
||||
return []
|
||||
|
||||
def get_recommended(self, result_count=100, **kwargs):
|
||||
# type: (...) -> List[TVInfoShow]
|
||||
"""
|
||||
get most recommended shows
|
||||
:param result_count: how many results are suppose to be returned
|
||||
"""
|
||||
return []
|
||||
|
||||
def get_recommended_for_account(self, account, result_count=100, **kwargs):
|
||||
# type: (...) -> List[TVInfoShow]
|
||||
"""
|
||||
get recommended shows for account
|
||||
:param account: account to get recommendations for
|
||||
:param result_count: how many results are suppose to be returned
|
||||
"""
|
||||
return []
|
||||
|
||||
def hide_recommended_for_account(self, account, show_ids, **kwargs):
|
||||
# type: (integer_types, List[integer_types], Any) -> List[integer_types]
|
||||
"""
|
||||
hide recommended show for account
|
||||
:param account: account to get recommendations for
|
||||
:param show_ids: list of show_ids to no longer recommend for account
|
||||
:return: list of added ids
|
||||
"""
|
||||
return []
|
||||
|
||||
def unhide_recommended_for_account(self, account, show_ids, **kwargs):
|
||||
# type: (integer_types, List[integer_types], Any) -> List[integer_types]
|
||||
"""
|
||||
unhide recommended show for account
|
||||
:param account: account to get recommendations for
|
||||
:param show_ids: list of show_ids to be included in possible recommend for account
|
||||
:return: list of removed ids
|
||||
"""
|
||||
return []
|
||||
|
||||
def list_hidden_recommended_for_account(self, account, **kwargs):
|
||||
# type: (integer_types, Any) -> List[TVInfoShow]
|
||||
"""
|
||||
list hidden recommended show for account
|
||||
:param account: account to get recommendations for
|
||||
:return: list of hidden shows
|
||||
"""
|
||||
return []
|
||||
|
||||
def get_watchlisted_for_account(self, account, result_count=100, **kwargs):
|
||||
# type: (...) -> List[TVInfoShow]
|
||||
"""
|
||||
get most watchlisted shows for account
|
||||
:param account: account to get recommendations for
|
||||
:param result_count: how many results are suppose to be returned
|
||||
"""
|
||||
return []
|
||||
|
||||
def get_anticipated(self, result_count=100, **kwargs):
|
||||
# type: (...) -> List[TVInfoShow]
|
||||
"""
|
||||
get anticipated shows
|
||||
:param result_count: how many results are suppose to be returned
|
||||
"""
|
||||
return []
|
||||
|
||||
def __getitem__(self, item):
|
||||
# type: (Union[AnyStr, integer_types, Tuple[integer_types, bool]]) -> Union[TVInfoShow, List[Dict], None]
|
||||
"""Legacy handler (use get_show or search_show instead)
|
||||
|
|
|
@ -33,7 +33,7 @@ class BaseTVinfoShownotfound(BaseTVinfoError):
|
|||
pass
|
||||
|
||||
|
||||
class BaseTVinfoSeasonnotfound(BaseTVinfoError):
|
||||
class BaseTVinfoSeasonnotfound(BaseTVinfoError, AttributeError, KeyError):
|
||||
"""Season cannot be found
|
||||
"""
|
||||
pass
|
||||
|
@ -45,7 +45,7 @@ class BaseTVinfoEpisodenotfound(BaseTVinfoError):
|
|||
pass
|
||||
|
||||
|
||||
class BaseTVinfoAttributenotfound(BaseTVinfoError):
|
||||
class BaseTVinfoAttributenotfound(BaseTVinfoError, AttributeError, KeyError):
|
||||
"""Raised if an episode does not have the requested
|
||||
attribute (such as a episode name)
|
||||
"""
|
||||
|
|
|
@ -290,7 +290,10 @@ def search_infosrc_for_show_id(reg_show_name, tvid=None, prodid=None, ui=None):
|
|||
logger.debug('Trying to find %s on %s' % (cur_name, sickgear.TVInfoAPI(cur_tvid).name))
|
||||
|
||||
try:
|
||||
show_info_list = t[prodid] if prodid else t[cur_name]
|
||||
if prodid:
|
||||
show_info_list = t.get_show(prodid)
|
||||
else:
|
||||
show_info_list = t.search_show(cur_name)
|
||||
show_info_list = show_info_list if isinstance(show_info_list, list) else [show_info_list]
|
||||
except (BaseException, Exception):
|
||||
continue
|
||||
|
@ -989,7 +992,7 @@ def validate_show(show_obj, season=None, episode=None):
|
|||
if season is None and episode is None:
|
||||
return t
|
||||
|
||||
return t[show_obj.prodid][season][episode]
|
||||
return t.get_show(show_obj.prodid, language=show_obj.lang)[season][episode]
|
||||
except (BaseTVinfoEpisodenotfound, BaseTVinfoSeasonnotfound, TypeError):
|
||||
pass
|
||||
|
||||
|
|
|
@ -7,7 +7,8 @@ from lib.api_imdb.imdb_api import IMDbIndexer
|
|||
from lib.tvinfo_base import (
|
||||
TVINFO_FACEBOOK, TVINFO_INSTAGRAM, TVINFO_TWITTER, TVINFO_WIKIPEDIA,
|
||||
TVINFO_IMDB, TVINFO_TMDB, TVINFO_TRAKT, TVINFO_TVDB, TVINFO_TVMAZE, TVINFO_TVRAGE,
|
||||
TVINFO_TRAKT_SLUG, TVINFO_TVDB_SLUG
|
||||
TVINFO_TRAKT_SLUG, TVINFO_TVDB_SLUG, TVINFO_TIKTOK, TVINFO_WIKIDATA, TVINFO_LINKEDIN, TVINFO_FANSITE,
|
||||
TVINFO_REDDIT, TVINFO_YOUTUBE
|
||||
)
|
||||
|
||||
init_config = {
|
||||
|
@ -134,6 +135,72 @@ tvinfo_config = {
|
|||
show_url=None,
|
||||
people_only=True,
|
||||
icon='wikipedia16.png'
|
||||
),
|
||||
TVINFO_TIKTOK: dict(
|
||||
id=TVINFO_TIKTOK,
|
||||
name='TikTok',
|
||||
module=None,
|
||||
active=False,
|
||||
mapped_only=True,
|
||||
people_url='https://www.tiktok.com/@%s',
|
||||
show_url=None,
|
||||
people_only=True,
|
||||
icon='tiktok16.png'
|
||||
),
|
||||
TVINFO_WIKIDATA: dict(
|
||||
id=TVINFO_WIKIDATA,
|
||||
name='Wikidata',
|
||||
module=None,
|
||||
active=False,
|
||||
mapped_only=True,
|
||||
people_url='https://www.wikidata.org/wiki/%s',
|
||||
show_url=None,
|
||||
people_only=True,
|
||||
icon='wikidata16.png'
|
||||
),
|
||||
TVINFO_REDDIT: dict(
|
||||
id=TVINFO_REDDIT,
|
||||
name='Reddit',
|
||||
module=None,
|
||||
active=False,
|
||||
mapped_only=True,
|
||||
people_url='http://www.reddit.com/r/%s',
|
||||
show_url=None,
|
||||
people_only=True,
|
||||
icon='reddit16.png'
|
||||
),
|
||||
TVINFO_YOUTUBE: dict(
|
||||
id=TVINFO_YOUTUBE,
|
||||
name='Reddit',
|
||||
module=None,
|
||||
active=False,
|
||||
mapped_only=True,
|
||||
people_url='https://www.youtube.com/c/%s',
|
||||
show_url=None,
|
||||
people_only=True,
|
||||
icon='youtube16.png'
|
||||
),
|
||||
TVINFO_FANSITE: dict(
|
||||
id=TVINFO_FANSITE,
|
||||
name='Fansite',
|
||||
module=None,
|
||||
active=False,
|
||||
mapped_only=True,
|
||||
people_url='%s',
|
||||
show_url=None,
|
||||
people_only=True,
|
||||
icon='fansite16.png'
|
||||
),
|
||||
TVINFO_LINKEDIN: dict(
|
||||
id=TVINFO_LINKEDIN,
|
||||
name='Linkedin',
|
||||
module=None,
|
||||
active=False,
|
||||
mapped_only=True,
|
||||
people_url='https://www.linkedin.com/in/%s',
|
||||
show_url=None,
|
||||
people_only=True,
|
||||
icon='linkedin16.png'
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -123,7 +123,7 @@ class KODIMetadata(generic.GenericMetadata):
|
|||
tv_node = etree.Element('tvshow')
|
||||
|
||||
try:
|
||||
show_info = t.get_show(show_obj.prodid, language=show_obj.lang)
|
||||
show_info = t.get_show(show_id, language=show_obj.lang)
|
||||
except BaseTVinfoShownotfound as e:
|
||||
logger.error(f'Unable to find show with id {show_id} on {sickgear.TVInfoAPI(show_obj.tvid).name},'
|
||||
f' skipping it')
|
||||
|
|
|
@ -375,7 +375,8 @@ class NameParser(object):
|
|||
|
||||
t = sickgear.TVInfoAPI(show_obj.tvid).setup(**tvinfo_config)
|
||||
|
||||
ep_obj = t[show_obj.prodid].aired_on(best_result.air_date)[0]
|
||||
ep_obj = t.get_show(show_obj.prodid, language=show_obj.lang).aired_on(
|
||||
best_result.air_date)[0]
|
||||
|
||||
season_number = int(ep_obj['seasonnumber'])
|
||||
episode_numbers = [int(ep_obj['episodenumber'])]
|
||||
|
|
|
@ -57,11 +57,12 @@ from lib import imdbpie, subliminal
|
|||
from lib.dateutil import tz
|
||||
from lib.dateutil.parser import parser as du_parser
|
||||
from lib.fuzzywuzzy import fuzz
|
||||
from lib.tvinfo_base import RoleTypes, TVINFO_FACEBOOK, TVINFO_INSTAGRAM, TVINFO_SLUG, TVINFO_TWITTER, TVINFO_WIKIPEDIA
|
||||
from lib.tvinfo_base import RoleTypes, TVINFO_FACEBOOK, TVINFO_INSTAGRAM, TVINFO_SLUG, TVINFO_TWITTER, \
|
||||
TVINFO_WIKIPEDIA, TVINFO_TIKTOK, TVINFO_FANSITE, TVINFO_YOUTUBE, TVINFO_REDDIT, TVINFO_LINKEDIN, TVINFO_WIKIDATA
|
||||
from lib.tvinfo_base.exceptions import *
|
||||
from sg_helpers import calc_age, int_to_time, remove_file_perm, time_to_int
|
||||
|
||||
from six import integer_types, iteritems, itervalues, moves, string_types
|
||||
from six import integer_types, iteritems, iterkeys, itervalues, moves, string_types
|
||||
|
||||
# noinspection PyUnreachableCode
|
||||
if False:
|
||||
|
@ -622,10 +623,12 @@ class Person(Referential):
|
|||
|
||||
p_ids = {}
|
||||
for cur_ids in (cur_person['p_ids'] and cur_person['p_ids'].split(';;;')) or []:
|
||||
k, v = cur_ids.split(':')
|
||||
k, v = cur_ids.split(':', 1)
|
||||
k = try_int(k, None)
|
||||
if v and None is not k:
|
||||
p_ids[k] = v if k in (TVINFO_FACEBOOK, TVINFO_INSTAGRAM, TVINFO_TWITTER, TVINFO_WIKIPEDIA) \
|
||||
p_ids[k] = v if k in (TVINFO_FACEBOOK, TVINFO_INSTAGRAM, TVINFO_TWITTER, TVINFO_WIKIPEDIA,
|
||||
TVINFO_TIKTOK, TVINFO_FANSITE, TVINFO_YOUTUBE, TVINFO_REDDIT,
|
||||
TVINFO_LINKEDIN, TVINFO_WIKIDATA) \
|
||||
else try_int(v, None)
|
||||
|
||||
(self._data_failure, self._data_fetched,
|
||||
|
@ -657,7 +660,8 @@ class Person(Referential):
|
|||
if person_obj.characters and char_obj and char_obj.show_obj \
|
||||
and char_obj.show_obj.ids.get(TVINFO_IMDB, {}).get('id'):
|
||||
p_char = [_pc for _pc in person_obj.characters
|
||||
if _pc.ti_show.ids.imdb == char_obj.show_obj.ids.get(TVINFO_IMDB, {}).get('id')]
|
||||
if _pc.ti_show and
|
||||
_pc.ti_show.ids.imdb == char_obj.show_obj.ids.get(TVINFO_IMDB, {}).get('id')]
|
||||
p_count = len(p_char)
|
||||
if 1 == p_count:
|
||||
if (p_char[0].start_year or p_char[0].end_year) and getattr(self, 'id', None):
|
||||
|
@ -717,7 +721,7 @@ class Person(Referential):
|
|||
self._data_fetched = True
|
||||
tvsrc_result, found_persons, found_on_src, search_sources, \
|
||||
found_ids, ids_to_check, imdb_confirmed, source_confirmed = \
|
||||
None, {}, set(), [TVINFO_TRAKT, TVINFO_TMDB, TVINFO_IMDB], \
|
||||
None, {}, set(), [TVINFO_TRAKT, TVINFO_TMDB, TVINFO_IMDB, TVINFO_TVDB], \
|
||||
set([_k for _k, _v in iteritems(self.ids) if _v] + ['text']), {}, False, {}
|
||||
# confirmed_character = False
|
||||
max_search_src = len(search_sources)
|
||||
|
@ -768,8 +772,10 @@ class Person(Referential):
|
|||
if show_obj and None is not pd and pd.characters:
|
||||
clean_show_name = indexermapper.clean_show_name(show_obj.name.lower())
|
||||
for cur_ch in pd.characters or []: # type: TVInfoCharacter
|
||||
if clean_show_name == indexermapper.clean_show_name(
|
||||
cur_ch.ti_show.seriesname.lower()):
|
||||
if not cur_ch.ti_show:
|
||||
continue
|
||||
if cur_ch.ti_show.seriesname and clean_show_name == indexermapper.clean_show_name(
|
||||
cur_ch.ti_show.seriesname and cur_ch.ti_show.seriesname.lower()):
|
||||
rp = pd
|
||||
confirmed_on_src = True
|
||||
# confirmed_character = True
|
||||
|
@ -807,7 +813,9 @@ class Person(Referential):
|
|||
found_ids.add(cur_i)
|
||||
self.dirty_ids = True
|
||||
|
||||
for cur_i in (TVINFO_INSTAGRAM, TVINFO_TWITTER, TVINFO_FACEBOOK, TVINFO_WIKIPEDIA):
|
||||
for cur_i in (TVINFO_INSTAGRAM, TVINFO_TWITTER, TVINFO_FACEBOOK, TVINFO_WIKIPEDIA,
|
||||
TVINFO_TIKTOK, TVINFO_FANSITE, TVINFO_YOUTUBE, TVINFO_REDDIT, TVINFO_LINKEDIN,
|
||||
TVINFO_WIKIDATA):
|
||||
if not rp.social_ids.get(cur_i):
|
||||
continue
|
||||
if rp.social_ids.get(cur_i) and not self.ids.get(cur_i) or \
|
||||
|
@ -1137,7 +1145,7 @@ class Character(Referential):
|
|||
for cur_row in (sql_result or []):
|
||||
c_ids = {}
|
||||
for cur_ids in (cur_row['c_ids'] and cur_row['c_ids'].split(';;;')) or []:
|
||||
k, v = cur_ids.split(':')
|
||||
k, v = cur_ids.split(':', 1)
|
||||
v = try_int(v, None)
|
||||
if v:
|
||||
c_ids[int(k)] = try_int(v, None)
|
||||
|
@ -1846,21 +1854,22 @@ class TVShow(TVShowBase):
|
|||
deathdate = deathdate and datetime.date.fromordinal(cur_row['deathdate'])
|
||||
p_years = {}
|
||||
for cur_p in (cur_row['p_years'] and cur_row['p_years'].split(';;;')) or []:
|
||||
p_id, py = cur_p.split(':')
|
||||
start, end = py.split('-')
|
||||
p_id, py = cur_p.split(':', 1)
|
||||
start, end = py.split('-', 1)
|
||||
p_years[int(p_id)] = {'start': try_int(start, None), 'end': try_int(end, None)}
|
||||
|
||||
p_ids, c_ids = {}, {}
|
||||
for cur_i in (cur_row['p_ids'] and cur_row['p_ids'].split(';;;')) or []:
|
||||
k, v = cur_i.split(':')
|
||||
k, v = cur_i.split(':', 1)
|
||||
k = try_int(k, None)
|
||||
if v:
|
||||
if k in (TVINFO_INSTAGRAM, TVINFO_TWITTER, TVINFO_FACEBOOK, TVINFO_WIKIPEDIA):
|
||||
if k in (TVINFO_INSTAGRAM, TVINFO_TWITTER, TVINFO_FACEBOOK, TVINFO_WIKIPEDIA, TVINFO_TIKTOK,
|
||||
TVINFO_FANSITE, TVINFO_YOUTUBE, TVINFO_REDDIT, TVINFO_LINKEDIN, TVINFO_WIKIDATA):
|
||||
p_ids[k] = v
|
||||
else:
|
||||
p_ids[k] = try_int(v, None)
|
||||
for cur_i in (cur_row['c_ids'] and cur_row['c_ids'].split(';;;')) or []:
|
||||
k, v = cur_i.split(':')
|
||||
k, v = cur_i.split(':', 1)
|
||||
v = try_int(v, None)
|
||||
if v:
|
||||
c_ids[int(k)] = try_int(v, None)
|
||||
|
@ -2735,6 +2744,8 @@ class TVShow(TVShowBase):
|
|||
|
||||
def _make_airtime(self, airtime=None):
|
||||
# type: (Optional[integer_types]) -> Optional[datetime.time]
|
||||
if isinstance(airtime, datetime.time):
|
||||
return airtime
|
||||
if isinstance(airtime, integer_types):
|
||||
return int_to_time(airtime)
|
||||
if self._airs:
|
||||
|
@ -2935,7 +2946,7 @@ class TVShow(TVShowBase):
|
|||
mc = next((_c for _c in cast_list or []
|
||||
if (None is not cur_cast.id and _c.ids.get(self.tvid) == cur_cast.id)
|
||||
or (unique_name and cur_cast.name and _c.name == cur_cast.name)
|
||||
or any(_c.ids.get(_src) == cur_cast.ids.get(_src) for _src in cur_cast.ids or {})),
|
||||
or any(_c.ids.get(_src) == cur_cast.ids.get(_src) for _src in iterkeys(cur_cast.ids) or {})),
|
||||
None) # type: Optional[Character]
|
||||
if not mc:
|
||||
unique_person = not any(1 for _cp in
|
||||
|
|
|
@ -3365,7 +3365,7 @@ class CMD_SickGearShowAddExisting(ApiCall):
|
|||
t = sickgear.TVInfoAPI(self.tvid).setup(**lINDEXER_API_PARMS)
|
||||
|
||||
try:
|
||||
myShow = t[int(self.prodid), False]
|
||||
myShow = t.get_show(self.prodid, load_episodes=False)
|
||||
except BaseTVinfoError as e:
|
||||
self.log(f'Unable to find show with id {self.tvid}', logger.WARNING)
|
||||
return _responds(RESULT_FAILURE, msg="Unable to retrieve information from indexer")
|
||||
|
@ -3528,7 +3528,7 @@ class CMD_SickGearShowAddNew(ApiCall):
|
|||
t = sickgear.TVInfoAPI(self.tvid).setup(**lINDEXER_API_PARMS)
|
||||
|
||||
try:
|
||||
myShow = t[int(self.prodid), False]
|
||||
myShow = t.get_show(self.prodid, load_episodes=False)
|
||||
except BaseTVinfoError as e:
|
||||
self.log(f'Unable to find show with id {self.tvid}', logger.WARNING)
|
||||
return _responds(RESULT_FAILURE, msg="Unable to retrieve information from indexer")
|
||||
|
|
|
@ -2177,6 +2177,17 @@ class Home(MainHandler):
|
|||
|
||||
return json_dumps({'success': t.respond()})
|
||||
|
||||
@staticmethod
|
||||
def fix_show_obj_db_data(show_obj):
|
||||
# adjust show_obj db data
|
||||
if 'genres' not in show_obj.imdb_info or None is show_obj.imdb_info.get('genres'):
|
||||
show_obj.imdb_info['genres'] = ''
|
||||
if show_obj.genre and not show_obj.genre[1:-1]:
|
||||
show_obj.genre = ''
|
||||
if 'country_codes' not in show_obj.imdb_info or None is show_obj.imdb_info.get('country_codes'):
|
||||
show_obj.imdb_info['country_codes'] = ''
|
||||
return show_obj
|
||||
|
||||
def view_show(self, tvid_prodid=None):
|
||||
|
||||
if None is tvid_prodid:
|
||||
|
@ -2186,6 +2197,8 @@ class Home(MainHandler):
|
|||
if None is show_obj:
|
||||
return self._generic_message('Error', 'Show not in show list')
|
||||
|
||||
show_obj = self.fix_show_obj_db_data(show_obj)
|
||||
|
||||
t = PageTemplate(web_handler=self, file='displayShow.tmpl')
|
||||
t.submenu = [{'title': 'Edit', 'path': 'home/edit-show?tvid_prodid=%s' % tvid_prodid}]
|
||||
|
||||
|
@ -3579,7 +3592,7 @@ class Home(MainHandler):
|
|||
# type: (AnyStr) -> AnyStr
|
||||
"""Return a text list of items separated by comma instead of '/' """
|
||||
|
||||
return re.sub(r'\b\s?/\s?\b', ', ', text)
|
||||
return (isinstance(text, string_types) and re.sub(r'\b\s?/\s?\b', ', ', text)) or text
|
||||
|
||||
def role(self, rid, tvid_prodid, **kwargs):
|
||||
_ = kwargs.get('oid') # suppress pyc non used var highlight, oid (original id) is a visual ui key
|
||||
|
@ -3609,7 +3622,7 @@ class Home(MainHandler):
|
|||
known = {}
|
||||
main_role_known = False
|
||||
rc_clean = re.compile(r'[^a-z0-9]')
|
||||
char_name = rc_clean.sub('', character.name.lower())
|
||||
char_name = rc_clean.sub('', (character.name or 'unknown name').lower())
|
||||
for cur_person in (character.person or []):
|
||||
person, roles, msg = self.cast(cur_person.id)
|
||||
if not msg:
|
||||
|
@ -3704,7 +3717,7 @@ class Home(MainHandler):
|
|||
ref_id = cur_ref_id
|
||||
|
||||
roles.append({
|
||||
'character_name': self.csv_items(cur_char['name']),
|
||||
'character_name': self.csv_items(cur_char['name']) or 'unknown name',
|
||||
'character_id': cur_char['id'],
|
||||
'character_rid': ref_id,
|
||||
'show_obj': helpers.find_show_by_id({cur_char['c_tvid']: cur_char['c_prodid']}),
|
||||
|
@ -4105,7 +4118,8 @@ class AddShows(Home):
|
|||
t = sickgear.TVInfoAPI(cur_tvid).setup(**tvinfo_config)
|
||||
results.setdefault(cur_tvid, {})
|
||||
try:
|
||||
for cur_result in t.search_show(list(used_search_term), ids=ids_search_used): # type: TVInfoShow
|
||||
for cur_result in t.search_show(list(used_search_term), ids=ids_search_used,
|
||||
lang=lang): # type: TVInfoShow
|
||||
if TVINFO_TRAKT == cur_tvid and not cur_result['ids'].tvdb:
|
||||
continue
|
||||
tv_src_id = int(cur_result['id'])
|
||||
|
@ -4262,18 +4276,11 @@ class AddShows(Home):
|
|||
img_url = ''
|
||||
if TVINFO_TRAKT == iid:
|
||||
img_url = 'imagecache?path=browse/thumb/trakt&filename=%s&trans=0&tmdbid=%s&tvdbid=%s' % \
|
||||
('%s.jpg' % show_info['ids'].trakt, show_info.get('tmdb_id'), show_info['ids'].tvdb)
|
||||
elif TVINFO_TVDB == iid and 'poster' in show_info and show_info['poster']:
|
||||
img_url = 'imagecache?path=browse/thumb/tvdb&filename=%s&trans=0&source=%s' % \
|
||||
('%s.jpg' % show_info['id'], show_info['poster'])
|
||||
sickgear.CACHE_IMAGE_URL_LIST.add_url(show_info['poster'])
|
||||
elif TVINFO_TVMAZE == iid and show_info.get('image'):
|
||||
img_url = 'imagecache?path=browse/thumb/tvmaze&filename=%s&trans=0&source=%s' % \
|
||||
('%s.jpg' % show_info['id'], show_info['image'])
|
||||
sickgear.CACHE_IMAGE_URL_LIST.add_url(show_info['image'])
|
||||
elif TVINFO_TMDB == iid and 'poster' in show_info and show_info['poster']:
|
||||
img_url = 'imagecache?path=browse/thumb/tmdb&filename=%s&trans=0&source=%s' % \
|
||||
('%s.jpg' % show_info['id'], show_info['poster'])
|
||||
('%s.jpg' % show_info['ids'].trakt, show_info['ids'].tmdb, show_info['ids'].tvdb)
|
||||
elif iid in (TVINFO_TVDB, TVINFO_TVMAZE, TVINFO_TMDB) and 'poster' in show_info and show_info['poster']:
|
||||
img_url = 'imagecache?path=browse/thumb/%s&filename=%s&trans=0&source=%s' % \
|
||||
({TVINFO_TVDB: 'tvdb', TVINFO_TVMAZE: 'tvmaze', TVINFO_TMDB: 'tmdb'}[iid],
|
||||
'%s.jpg' % show_info['id'], show_info['poster'])
|
||||
sickgear.CACHE_IMAGE_URL_LIST.add_url(show_info['poster'])
|
||||
return img_url
|
||||
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue