Add try_date for use in attempting conversion of unspecified date format types birthdate, deathdate, aired.

Change tweaks, remove one warn, order imports.
Change tidy up of other warnings, mismatched types, typos, a strange arg issue with deepcopy, and PEP8.
Bug fix, bad key in get_item for TVInfoSocialIDs.

Fix ambiguities of `show` used in sg versus external uses.

Change add cast, crew type mappings
Change only take Main Actors, Hosts, Interviewers, Presenters.
This commit is contained in:
JackDandy 2021-09-05 20:10:58 +01:00
parent 0fbf88651d
commit 5ff18a8652
10 changed files with 519 additions and 460 deletions

View file

@ -13,12 +13,13 @@ import re
from bs4_parser import BS4Parser
from exceptions_helper import ex
from lib import imdbpie
# from lib.tvinfo_base.exceptions import BaseTVinfoShownotfound
from lib.tvinfo_base import PersonGenders, TVInfoBase, TVInfoIDs, TVInfoCharacter, TVInfoPerson, TVInfoShow, \
TVINFO_IMDB
# , TVINFO_TMDB, TVINFO_TRAKT, TVINFO_TVDB, TVINFO_TVRAGE, \
# TVINFO_FACEBOOK, TVINFO_INSTAGRAM, TVINFO_TWITTER, TVINFO_WIKIPEDIA
from lib.dateutil.parser import parser
# from lib.tvinfo_base.exceptions import BaseTVinfoShownotfound
from lib.tvinfo_base import (
TVInfoCharacter, TVInfoPerson, PersonGenders, TVINFO_IMDB,
# 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 six import iteritems
@ -67,12 +68,13 @@ class IMDbIndexer(TVInfoBase):
"""
def _make_result_dict(s):
imdb_id = try_int(re.search(r'tt(\d+)', s.get('id') or s.get('imdb_id')).group(1), None)
tvs = TVInfoShow()
tvs.seriesname, tvs.id, tvs.firstaired, tvs.genre_list, tvs.overview, tvs.poster, tvs.ids = \
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)
return tvs
return ti_show
results = []
if ids:
@ -112,14 +114,14 @@ class IMDbIndexer(TVInfoBase):
if known_for['titleType'] not in ('tvSeries', 'tvMiniSeries'):
continue
for character in known_for.get('characters') or []:
show = TVInfoShow()
show.id = try_int(re.search(r'(\d+)', known_for.get('id')).group(1))
show.ids.imdb = show.id
show.seriesname = known_for.get('title')
show.firstaired = known_for.get('year')
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
ti_show.seriesname = known_for.get('title')
ti_show.firstaired = known_for.get('year')
characters.append(
TVInfoCharacter(name=character, show=show,
start_year=known_for.get('startYear'), end_year=known_for.get('endYear'))
TVInfoCharacter(name=character, ti_show=ti_show, start_year=known_for.get('startYear'),
end_year=known_for.get('endYear'))
)
try:
birthdate = person_obj['base']['birthDate'] and tz_p.parse(person_obj['base']['birthDate']).date()
@ -175,7 +177,8 @@ class IMDbIndexer(TVInfoBase):
results.append(self._convert_person(cp))
return results
def _get_bio(self, p_id):
@staticmethod
def _get_bio(p_id):
try:
bio = get_url('https://www.imdb.com/name/nm%07d/bio' % p_id, headers={'Accept-Language': 'en'})
if not bio:
@ -217,4 +220,3 @@ class IMDbIndexer(TVInfoBase):
self._set_cache_entry(cache_credits_key, fg)
if p:
return self._convert_person(p, filmography=fg, bio=bio)

View file

@ -184,9 +184,9 @@ class TmdbIndexer(TVInfoBase):
"""This searches TMDB for the series name,
"""
def _make_result_dict(s):
tvs = TVInfoShow()
tvs.seriesname, tvs.id, tvs.seriesid, tvs.firstaired, tvs.genre_list, tvs.overview, tvs.poster, tvs.ids, \
tvs.language, tvs.popularity, tvs.rating = \
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' % (
@ -197,8 +197,8 @@ class TmdbIndexer(TVInfoBase):
imdb=s.get('external_ids') and s['external_ids'].get('imdb_id') and
try_int(s['external_ids'].get('imdb_id', '').replace('tt', ''), None)), \
clean_data(s.get('original_language')), s.get('popularity'), s.get('vote_average')
tvs.genre = '|'.join(tvs.genre_list or [])
return tvs
ti_show.genre = '|'.join(ti_show.genre_list or [])
return ti_show
results = []
if ids:
@ -267,14 +267,14 @@ class TmdbIndexer(TVInfoBase):
characters = []
for character in cast or []:
show = TVInfoShow()
show.id = character.get('id')
show.ids = TVInfoIDs(ids={TVINFO_TMDB: show.id})
show.seriesname = clean_data(character.get('original_name'))
show.overview = clean_data(character.get('overview'))
show.firstaired = clean_data(character.get('first_air_date'))
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=show)
TVInfoCharacter(name=clean_data(character.get('character')), show=ti_show)
)
pi = person_obj.get('images')
@ -408,31 +408,32 @@ class TmdbIndexer(TVInfoBase):
def _convert_show(self, show_dict, show_obj=None):
# type: (Dict, TVInfoShow) -> TVInfoShow
if None is show_obj:
tv_s = TVInfoShow()
ti_show = TVInfoShow()
else:
tv_s = show_obj
ti_show = show_obj
if show_dict:
tv_s.seriesname = clean_data(show_dict.get('name') or show_dict.get('original_name')
ti_show.seriesname = clean_data(show_dict.get('name') or show_dict.get('original_name')
or show_dict.get('original_title'))
org_title = clean_data(show_dict.get('original_name') or show_dict.get('original_title'))
if org_title != tv_s.seriesname:
tv_s.aliases = [org_title]
tv_s.id = show_dict.get('id')
tv_s.seriesid = tv_s.id
tv_s.language = clean_data(show_dict.get('original_language'))
tv_s.overview = clean_data(show_dict.get('overview'))
tv_s.status = clean_data(show_dict.get('status', ''))
tv_s.show_type = clean_data((show_dict.get('type') and [show_dict['type']]) or [])
tv_s.firstaired = clean_data(show_dict.get('first_air_date'))
tv_s.vote_count = show_dict.get('vote_count')
tv_s.vote_average = show_dict.get('vote_average')
tv_s.popularity = show_dict.get('popularity')
tv_s.origin_countries = clean_data(show_dict.get('origin_country') or [])
tv_s.genre_list = []
if org_title != ti_show.seriesname:
ti_show.aliases = [org_title]
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.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'))
ti_show.popularity = show_dict.get('popularity')
ti_show.vote_count = show_dict.get('vote_count')
ti_show.vote_average = show_dict.get('vote_average')
ti_show.origin_countries = show_dict.get('origin_country') or []
ti_show.genre_list = []
ti_show.origin_countries = clean_data(show_dict.get('origin_country') or [])
for g in show_dict.get('genre_ids') or []:
if g in self.tv_genres:
tv_s.genre_list.append(self.tv_genres.get(g))
tv_s.genre = '|'.join(tv_s.genre_list)
ti_show.genre_list.append(self.tv_genres.get(g))
ti_show.genre = '|'.join(ti_show.genre_list)
runtime = None
for r in sorted(show_dict.get('episode_run_time') or [], reverse=True):
if 40 < r < 50:
@ -443,18 +444,18 @@ class TmdbIndexer(TVInfoBase):
break
if not runtime and show_dict.get('episode_run_time'):
runtime = max(show_dict.get('episode_run_time') or [0]) or None
tv_s.runtime = runtime
ti_show.runtime = runtime
tv_s.networks = [
ti_show.networks = [
TVInfoNetwork(name=clean_data(n.get('name')), n_id=n.get('id'),
country_code=clean_data(n.get('origin_country')))
for n in reversed(show_dict.get('networks') or [])
]
if show_dict.get('networks'):
tv_s.network = clean_data(show_dict['networks'][-1]['name'])
tv_s.network_id = show_dict['networks'][-1].get('id')
tv_s.network_country_code = clean_data(show_dict['networks'][-1].get('origin_country'))
ti_show.network = clean_data(show_dict['networks'][-1]['name'])
ti_show.network_id = show_dict['networks'][-1].get('id')
ti_show.network_country_code = clean_data(show_dict['networks'][-1].get('origin_country'))
image_url = show_dict.get('poster_path') and '%s%s%s' % \
(self.img_base_url, self.size_map[TVInfoImageType.poster][TVInfoImageSize.original],
@ -465,19 +466,19 @@ class TmdbIndexer(TVInfoBase):
backdrop_url = show_dict.get('backdrop_path') and '%s%s%s' % \
(self.img_base_url, self.size_map[TVInfoImageType.fanart][TVInfoImageSize.original],
show_dict.get('backdrop_path'))
tv_s.ids = TVInfoIDs(tvdb=show_dict.get('external_ids', {}).get('tvdb_id'),
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))
tv_s.social_ids = TVInfoSocialIDs(twitter=show_dict.get('external_ids', {}).get('twitter_id'),
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'))
tv_s.poster = image_url
tv_s.poster_thumb = thumb_image_url
tv_s.fanart = backdrop_url
return tv_s
ti_show.poster = image_url
ti_show.poster_thumb = thumb_image_url
ti_show.fanart = backdrop_url
return ti_show
def _get_show_list(self, src_method, result_count, **kwargs):
result = []
@ -606,9 +607,9 @@ class TmdbIndexer(TVInfoBase):
tmdb_lang = ('en-US', language)[language in self._tmdb_supported_lang_list]
if any((banners, posters, seasons, seasonwides, fanart)):
to_append.append('images')
if (actors or self.config['actors_enabled']) and not getattr(self.shows.get(sid), 'actors_loaded', False):
if (actors or self.config['actors_enabled']) and not getattr(self.ti_shows.get(sid), 'actors_loaded', False):
to_append.append('aggregate_credits')
if get_ep_info and not getattr(self.shows.get(sid), 'ep_loaded', False):
if get_ep_info and not getattr(self.ti_shows.get(sid), 'ep_loaded', False):
to_append.append('episode_groups')
try:
tmdb = tmdbsimple.TV(sid)
@ -624,7 +625,7 @@ class TmdbIndexer(TVInfoBase):
self.show_not_found = True
return False
show_obj = self.shows[sid]
show_obj = self.ti_shows[sid]
self._convert_show(show_data, show_obj)
@ -656,7 +657,7 @@ class TmdbIndexer(TVInfoBase):
)
season_cast_objs = {}
if (actors or self.config['actors_enabled']) and not getattr(self.shows.get(sid), 'actors_loaded', False):
if (actors or self.config['actors_enabled']) and not getattr(self.ti_shows.get(sid), 'actors_loaded', False):
cast, show_obj.actors_loaded = CastList(), True
if isinstance(show_data.get('aggregate_credits'), dict) and 'cast' in show_data['aggregate_credits'] and\
isinstance(show_data['aggregate_credits']['cast'], list):
@ -723,7 +724,7 @@ class TmdbIndexer(TVInfoBase):
},
} for ch in cast[RoleTypes.ActorMain]]
if get_ep_info and not getattr(self.shows.get(sid), 'ep_loaded', False):
if get_ep_info and not getattr(self.ti_shows.get(sid), 'ep_loaded', False):
show_obj.ep_loaded = True
seasons = ['season/%d' % s['season_number'] for s in show_data.get('seasons') or []]
# call limited to 20 seasons per call

View file

@ -292,24 +292,21 @@ class TraktIndexer(TVInfoBase):
if show_credits:
pc = []
for c in resp.get('cast') or []:
show = TVInfoShow()
show.id = c['show']['ids'].get('trakt')
show.seriesname = c['show']['title']
show.ids = TVInfoIDs(ids={id_map[src]: _convert_imdb_id(id_map[src], sid)
ti_show = TVInfoShow()
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})
show.network = c['show']['network']
show.firstaired = c['show']['first_aired']
show.overview = c['show']['overview']
show.status = c['show']['status']
show.imdb_id = c['show']['ids'].get('imdb')
show.runtime = c['show']['runtime']
show.genre_list = c['show']['genres']
ti_show.network = c['show']['network']
ti_show.firstaired = c['show']['first_aired']
ti_show.overview = c['show']['overview']
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'),
show=show
)
TVInfoCharacter(name=ch, regular=c.get('series_regular'), ti_show=ti_show)
)
result.characters = pc
else:

View file

@ -52,7 +52,7 @@ log = logging.getLogger('tvdb.api')
log.addHandler(logging.NullHandler())
# noinspection PyUnusedLocal
# noinspection HttpUrlsUsage,PyUnusedLocal
def _record_hook(r, *args, **kwargs):
r.hook_called = True
if 301 == r.status_code and isinstance(r.headers.get('Location'), string_types) \
@ -64,8 +64,8 @@ def _record_hook(r, *args, **kwargs):
def retry(exception_to_check, tries=4, delay=3, backoff=2):
"""Retry calling the decorated function using an exponential backoff.
http://www.saltycrane.com/blog/2009/11/trying-out-retry-decorator-python/
original from: http://wiki.python.org/moin/PythonDecoratorLibrary#Retry
www.saltycrane.com/blog/2009/11/trying-out-retry-decorator-python/
original from: wiki.python.org/moin/PythonDecoratorLibrary#Retry
:param exception_to_check: the exception to check. may be a tuple of
exceptions to check
@ -222,7 +222,7 @@ class Tvdb(TVInfoBase):
tvdb_api's own key (fine for small scripts), but you can use your
own key if desired - this is recommended if you are embedding
tvdb_api in a larger application)
See http://thetvdb.com/?tab=apiregister to get your own key
See thetvdb.com/?tab=apiregister to get your own key
"""
@ -976,7 +976,7 @@ class Tvdb(TVInfoBase):
pass
self._set_show_data(sid, 'actors', a)
self._set_show_data(sid, 'cast', cast)
self.shows[sid].actors_loaded = True
self.ti_shows[sid].actors_loaded = True
def get_episode_data(self, epid):
# Parse episode information
@ -1004,7 +1004,7 @@ class Tvdb(TVInfoBase):
mapped_img_types = {'banner': 'series'}
excluded_main_data = enabled_type in ['seasons_enabled', 'seasonwides_enabled']
loaded_name = '%s_loaded' % image_type
if (type_bool or self.config[enabled_type]) and not getattr(self.shows.get(sid), loaded_name, False):
if (type_bool or self.config[enabled_type]) and not getattr(self.ti_shows.get(sid), loaded_name, False):
image_data = self._getetsrc(self.config['url_series_images'] %
(sid, mapped_img_types.get(image_type, image_type)), language=language)
if image_data and 0 < len(image_data.get('data', '') or ''):
@ -1017,7 +1017,7 @@ class Tvdb(TVInfoBase):
self._set_show_data(sid, f'{image_type}_thumb', url_thumb)
excluded_main_data = True # artwork found so prevent fallback
self._parse_banners(sid, image_data['data'])
self.shows[sid].__dict__[loaded_name] = True
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):
@ -1073,13 +1073,13 @@ class Tvdb(TVInfoBase):
('seasonwide', 'seasonwides_enabled', seasonwides)]:
self._parse_images(sid, language, show_data, img_type, en_type, p_type)
if (actors or self.config['actors_enabled']) and not getattr(self.shows.get(sid), 'actors_loaded', False):
if (actors or self.config['actors_enabled']) and not getattr(self.ti_shows.get(sid), 'actors_loaded', False):
actor_data = self._getetsrc(self.config['url_actors_info'] % sid, language=language)
actor_data_alt = self._getetsrc(self.config['url_series_people'] % sid, language=language)
if actor_data and 0 < len(actor_data.get('data', '') or '') or actor_data_alt and actor_data_alt['data']:
self._parse_actors(sid, actor_data and actor_data.get('data', ''), actor_data_alt and actor_data_alt['data'])
if get_ep_info and not getattr(self.shows.get(sid), 'ep_loaded', False):
if get_ep_info and not getattr(self.ti_shows.get(sid), 'ep_loaded', False):
# Parse episode data
log.debug('Getting all episodes of %s' % sid)
@ -1200,7 +1200,7 @@ class Tvdb(TVInfoBase):
ep_no = int(float(elem_epno))
if not cur_ep.get('network'):
cur_ep['network'] = self.shows[sid].network
cur_ep['network'] = self.ti_shows[sid].network
for k, v in iteritems(cur_ep):
k = k.lower()
@ -1236,7 +1236,7 @@ class Tvdb(TVInfoBase):
self._set_item(sid, seas_no, ep_no, 'crew', crew)
self._set_item(sid, seas_no, ep_no, 'cast', cast)
self.shows[sid].ep_loaded = True
self.ti_shows[sid].ep_loaded = True
return True

View file

@ -11,7 +11,7 @@ __author__ = 'dbr/Ben'
__version__ = '1.9'
__all__ = ['TvdbException', 'TvdbError', 'TvdbUserabort', 'TvdbShownotfound',
'TvdbSeasonnotfound', 'TvdbEpisodenotfound', 'TvdbAttributenotfound', 'TvdbTokenexpired', 'TvdbTokenFailre']
'TvdbSeasonnotfound', 'TvdbEpisodenotfound', 'TvdbAttributenotfound', 'TvdbTokenexpired', 'TvdbTokenFailure']
from lib.tvinfo_base.exceptions import *
@ -66,8 +66,7 @@ class TvdbTokenexpired(BaseTVinfoAuthenticationerror, TvdbError):
pass
class TvdbTokenFailre(BaseTVinfoAuthenticationerror, TvdbError):
class TvdbTokenFailure(BaseTVinfoAuthenticationerror, TvdbError):
"""getting token failed
"""
pass

View file

@ -16,7 +16,7 @@ from requests.adapters import HTTPAdapter
from tornado._locale_data import LOCALE_NAMES
from urllib3.util.retry import Retry
from sg_helpers import clean_data, get_url, try_int
from sg_helpers import clean_data, enforce_type, get_url, try_int
from lib.dateutil.parser import parser
# noinspection PyProtectedMember
from lib.dateutil.tz.tz import _datetime_to_timestamp
@ -317,7 +317,7 @@ class TvMaze(TVInfoBase):
if not show_data:
return False
ti_show = self.shows[sid] # type: TVInfoShow
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'):
@ -354,7 +354,7 @@ class TvMaze(TVInfoBase):
if show_data.genres:
ti_show.genre = '|'.join(show_data.genres).lower()
if (actors or self.config['actors_enabled']) and not getattr(self.shows.get(sid), 'actors_loaded', False):
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]:
@ -450,7 +450,7 @@ class TvMaze(TVInfoBase):
elif show_data.web_channel:
self._set_network(ti_show, show_data.web_channel, True)
if get_ep_info and not getattr(self.shows.get(sid), 'ep_loaded', False):
if get_ep_info and not getattr(self.ti_shows.get(sid), 'ep_loaded', False):
log.debug('Getting all episodes of %s' % sid)
if None is show_data:
show_data = self._get_tvm_show(sid, get_ep_info)
@ -513,23 +513,23 @@ class TvMaze(TVInfoBase):
def _convert_person(person_obj):
# type: (tvmaze.Person) -> TVInfoPerson
ch = []
for c in person_obj.castcredits or []:
show = TVInfoShow()
show.seriesname = clean_data(c.show.name)
show.id = c.show.id
show.firstaired = clean_data(c.show.premiered)
show.ids = TVInfoIDs(ids={TVINFO_TVMAZE: show.id})
show.overview = clean_data(c.show.summary)
show.status = clean_data(c.show.status)
for c in tvmaze_person_obj.castcredits or []:
ti_show = TVInfoShow()
ti_show.seriesname = clean_data(c.show.name)
ti_show.id = c.show.id
ti_show.firstaired = clean_data(c.show.premiered)
ti_show.ids = TVInfoIDs(ids={TVINFO_TVMAZE: ti_show.id})
ti_show.overview = clean_data(c.show.summary)
ti_show.status = clean_data(c.show.status)
net = c.show.network or c.show.web_channel
if net:
show.network = clean_data(net.name)
show.network_id = net.maze_id
show.network_country = clean_data(net.country)
show.network_timezone = clean_data(net.timezone)
show.network_country_code = clean_data(net.code)
show.network_is_stream = None is not c.show.web_channel
ch.append(TVInfoCharacter(name=clean_data(c.character.name), show=show))
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_country_code = clean_data(net.code)
ti_show.network_timezone = clean_data(net.timezone)
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()
except (BaseException, Exception):

View file

@ -25,6 +25,7 @@ import unicodedata
from exceptions_helper import ex, ConnectionSkipException
from json_helper import json_loads
from cachecontrol import CacheControl, caches
from lib.dateutil.parser import parser
# from lib.tmdbsimple.configuration import Configuration
# from lib.tmdbsimple.genres import Genres
from cfscrape import CloudflareScraper
@ -41,6 +42,7 @@ from six import integer_types, iteritems, iterkeys, itervalues, moves, PY2, stri
import zipfile
# py7z hardwired removed, see comment below
py7zr = None
tz_p = parser()
# noinspection PyUnreachableCode
if False:
@ -634,6 +636,21 @@ def try_int(s, s_default=0):
return s_default
def try_date(s, s_default=None):
# type: (AnyStr, Any) -> Optional[AnyStr]
"""
Convert string to a standard UTC date string
:param s:
:param s_default:
:return:
"""
try:
parse = tz_p.parse(clean_data(s))
return '%04d-%02d-%02d' % (parse.year, parse.month, parse.day)
except(BaseException, Exception):
return s_default
def _maybe_request_url(e, def_url=''):
return hasattr(e, 'request') and hasattr(e.request, 'url') and ' ' + e.request.url or def_url
@ -644,6 +661,7 @@ def clean_data(data):
Issues corrected:
- Replaces &amp; with &
- Replace multiple spaces with one space
- Trailing whitespace
- Decode html entities
:param data: data
@ -659,7 +677,7 @@ def clean_data(data):
if isinstance(data, dict):
return {k: clean_data(v) for k, v in iteritems(data)}
if isinstance(data, string_types):
return unicodedata.normalize('NFKD', html_unescape(data).strip().replace('&amp;', '&'))
return unicodedata.normalize('NFKD', re.sub(r' {2,}', ' ', html_unescape(data).strip().replace('&amp;', '&')))
return data

View file

@ -70,7 +70,7 @@ tv_src_names = {
log = logging.getLogger('TVInfo')
log.addHandler(logging.NullHandler())
TVInfoShowContainer = {} # type: Dict[str, ShowContainer]
TVInfoShowContainer = {} # type: Union[ShowContainer, Dict]
class ShowContainer(dict):
@ -158,9 +158,16 @@ class TVInfoIDs(object):
class TVInfoSocialIDs(object):
def __init__(self, twitter=None, instagram=None, facebook=None, wikipedia=None, ids=None, reddit=None,
youtube=None):
# type: (str_int, str_int, str_int, str_int, Dict[int, str_int], str_int, str) -> None
def __init__(
self,
twitter=None, # type: str_int
instagram=None, # type: str_int
facebook=None, # type: str_int
wikipedia=None, # type: str_int
ids=None, # type: Dict[int, str_int]
reddit=None, # type: str_int
youtube=None # type: AnyStr
):
ids = ids or {}
self.twitter = twitter or ids.get(TVINFO_TWITTER)
self.instagram = instagram or ids.get(TVINFO_INSTAGRAM)
@ -171,7 +178,7 @@ class TVInfoSocialIDs(object):
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_TWITTER: self.youtube}.get(key)
TVINFO_WIKIPEDIA: self.wikipedia, TVINFO_REDDIT: self.reddit, TVINFO_YOUTUBE: self.youtube}.get(key)
def __iter__(self):
for s, v in [(TVINFO_TWITTER, self.twitter), (TVINFO_INSTAGRAM, self.instagram),
@ -238,7 +245,7 @@ class TVInfoImage(object):
lang=None, height=None, width=None, aspect_ratio=None):
self.img_id = img_id # type: Optional[integer_types]
self.image_type = image_type # type: integer_types
self.sizes = sizes # type: Dict[int, AnyStr]
self.sizes = sizes # type: Union[TVInfoImageSize, Dict]
self.type_str = type_str # type: AnyStr
self.main_image = main_image # type: bool
self.rating = rating # type: Optional[Union[float, integer_types]]
@ -392,6 +399,8 @@ class TVInfoShow(dict):
setattr(result, k, copy.deepcopy(v, memo))
for k, v in self.items():
result[k] = copy.deepcopy(v, memo)
if isinstance(k, integer_types):
setattr(result[k], 'show', result)
return result
def __bool__(self):
@ -484,9 +493,12 @@ class TVInfoSeason(dict):
result = cls.__new__(cls)
memo[id(self)] = result
for k, v in self.__dict__.items():
# noinspection PyArgumentList
setattr(result, k, copy.deepcopy(v, memo))
for k, v in self.items():
result[k] = copy.deepcopy(v, memo)
if isinstance(k, integer_types):
setattr(result[k], 'season', result)
return result
def search(self, term=None, key=None):
@ -580,6 +592,7 @@ class TVInfoEpisode(dict):
result = cls.__new__(cls)
memo[id(self)] = result
for k, v in self.__dict__.items():
# noinspection PyArgumentList
setattr(result, k, copy.deepcopy(v, memo))
for k, v in self.items():
result[k] = copy.deepcopy(v, memo)
@ -671,12 +684,12 @@ class PersonBase(dict):
sortorder
"""
def __init__(
self, # type:
self,
p_id=None, # type: integer_types
name=None, # type: AnyStr
image=None, # type: AnyStr
images=None, # type: List[TVInfoImage]
gender=None, # type: int
gender=None, # type: integer_types
bio=None, # type: AnyStr
birthdate=None, # type: datetime.date
deathdate=None, # type: datetime.date
@ -758,7 +771,7 @@ class TVInfoPerson(PersonBase):
image=None, # type: Optional[AnyStr]
images=None, # type: List[TVInfoImage]
thumb_url=None, # type: AnyStr
gender=None, # type: int
gender=None, # type: integer_types
bio=None, # type: AnyStr
birthdate=None, # type: datetime.date
deathdate=None, # type: datetime.date
@ -766,13 +779,13 @@ class TVInfoPerson(PersonBase):
country_code=None, # type: AnyStr
country_timezone=None, # type: AnyStr
ids=None, # type: Dict
homepage=None, # type: AnyStr
homepage=None, # type: Optional[AnyStr]
social_ids=None, # type: Dict
birthplace=None, # type: AnyStr
deathplace=None, # type: AnyStr
url=None, # type: AnyStr
characters=None, # type: List[TVInfoCharacter]
height=None, # type: Union[integer_types, float]
deathplace=None, # type: AnyStr
nicknames=None, # type: Set[AnyStr]
real_name=None, # type: AnyStr
akas=None, # type: Set[AnyStr]
@ -801,7 +814,7 @@ class TVInfoPerson(PersonBase):
class TVInfoCharacter(PersonBase):
def __init__(self, person=None, voice=None, plays_self=None, regular=None, show=None, start_year=None,
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)
@ -809,7 +822,7 @@ class TVInfoCharacter(PersonBase):
self.voice = voice # type: Optional[bool]
self.plays_self = plays_self # type: Optional[bool]
self.regular = regular # type: Optional[bool]
self.show = show # type: Optional[TVInfoShow]
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]
@ -857,12 +870,21 @@ class RoleTypes(object):
CrewShowrunner = 72
CrewOther = 100
reverse = {1: 'Main', 2: 'Recurring', 3: 'Guest', 4: 'Special Guest', 50: 'Director', 51: 'Writer', 52: 'Producer',
53: 'Executive Producer', 60: 'Creator', 61: 'Editor', 62: 'Camera', 63: 'Music', 64: 'Stylist',
65: 'Makeup', 66: 'Photography', 67: 'Sound', 68: 'Designer', 69: 'Developer', 70: 'Animation',
71: 'Visual Effects', 100: 'Other'}
reverse = {1: 'Main', 2: 'Recurring', 3: 'Guest', 4: 'Special Guest', 10: 'Host', 11: 'Host Guest',
12: 'Presenter', 13: 'Presenter Guest', 14: 'Interviewer', 15: 'Interviewer Guest',
16: 'Musical Guest', 50: 'Director', 51: 'Writer', 52: 'Producer', 53: 'Executive Producer',
60: 'Creator', 61: 'Editor', 62: 'Camera', 63: 'Music', 64: 'Stylist', 65: 'Makeup',
66: 'Photography', 67: 'Sound', 68: 'Designer', 69: 'Developer', 70: 'Animation',
71: 'Visual Effects', 72: 'Showrunner', 100: 'Other'}
crew_limit = 50
# just a helper to generate the reverse data
# def __init__(self):
# import re
# {value: re.sub(r'([a-z])([A-Z])', r'\1 \2', name.replace('Actor', '').replace('Crew', ''))
# for name, value in iteritems(vars(RoleTypes)) if not name.startswith('_')
# and name not in ('reverse', 'crew_limit')}
crew_type_names = {c.lower(): v for v, c in iteritems(RoleTypes.reverse) if v >= RoleTypes.crew_limit}
@ -882,8 +904,8 @@ class TVInfoBase(object):
global TVInfoShowContainer
if self.__class__.__name__ not in TVInfoShowContainer:
TVInfoShowContainer[self.__class__.__name__] = ShowContainer()
self.shows = TVInfoShowContainer[self.__class__.__name__] # type: ShowContainer[integer_types, TVInfoShow]
self.shows.cleanup_old()
self.ti_shows = TVInfoShowContainer[self.__class__.__name__] # type: ShowContainer[integer_types, TVInfoShow]
self.ti_shows.cleanup_old()
self.lang = None # type: Optional[AnyStr]
self.corrections = {} # type: Dict
self.show_not_found = False # type: bool
@ -929,10 +951,10 @@ class TVInfoBase(object):
:param actors: should load actors
:param lang: requested language
"""
if sid not in self.shows or None is self.shows[sid].id or \
(load_episodes and not getattr(self.shows[sid], 'ep_loaded', False)):
if sid not in self.ti_shows or None is self.ti_shows[sid].id or \
(load_episodes and not getattr(self.ti_shows[sid], 'ep_loaded', False)):
return True
_show = self.shows[sid] # type: TVInfoShow
_show = self.ti_shows[sid] # type: TVInfoShow
if _show.requested_language != lang:
_show.ep_loaded = _show.poster_loaded = _show.banner_loaded = _show.actors_loaded = _show.fanart_loaded = \
_show.seasonwide_images_loaded = _show.season_images_loaded = False
@ -1088,8 +1110,9 @@ class TVInfoBase(object):
actors=False, # type: bool
old_call=False, # type: bool
language=None, # type: AnyStr
**kwargs # type: Optional[Any]
): # type: (...) -> Optional[TVInfoShow]
# **kwargs # type: dict
):
# type: (...) -> Optional[TVInfoShow]
"""
get data for show id
:param show_id: id of show
@ -1109,33 +1132,33 @@ class TVInfoBase(object):
self.config.update({'banners_enabled': banners, 'posters_enabled': posters, 'seasons_enabled': seasons,
'seasonwides_enabled': seasonwides, 'fanart_enabled': fanart, 'actors_enabled': actors,
'language': language or 'en'})
self.shows.lock.acquire()
self.ti_shows.lock.acquire()
try:
if show_id not in self.shows:
self.shows[show_id] = TVInfoShow() # type: TVInfoShow
with self.shows[show_id].lock:
self.shows.lock.release()
if show_id not in self.ti_shows:
self.ti_shows[show_id] = TVInfoShow() # type: TVInfoShow
with self.ti_shows[show_id].lock:
self.ti_shows.lock.release()
try:
if self._must_load_data(show_id, load_episodes, banners, posters, seasons, seasonwides, fanart,
actors, self.config['language']):
self.shows[show_id].requested_language = self.config['language']
self.ti_shows[show_id].requested_language = self.config['language']
self._get_show_data(show_id, self.map_languages.get(self.config['language'],
self.config['language']),
load_episodes, banners, posters, seasons, seasonwides, fanart, actors)
if None is self.shows[show_id].id:
with self.shows.lock:
del self.shows[show_id]
return None if show_id not in self.shows else copy.deepcopy(self.shows[show_id])
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])
finally:
try:
if None is self.shows[show_id].id:
with self.shows.lock:
del self.shows[show_id]
if None is self.ti_shows[show_id].id:
with self.ti_shows.lock:
del self.ti_shows[show_id]
except (BaseException, Exception):
pass
finally:
try:
self.shows.lock.release()
self.ti_shows.lock.release()
except RuntimeError:
pass
if not old_call and None is not self._old_config:
@ -1163,8 +1186,13 @@ class TVInfoBase(object):
return names
return name
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, # type: Union[AnyStr, List[AnyStr]]
ids=None, # type: Dict[integer_types, integer_types]
# **kwargs # type: Optional[Any]
):
# type: (...) -> List[Dict]
"""
search for series with name(s) or ids
@ -1206,41 +1234,41 @@ class TVInfoBase(object):
calls __getitem__ on tvinfo[1], there is no way to check if
tvinfo.__dict__ should have a key "1" before we auto-create it
"""
# if sid not in self.shows:
# self.shows[sid] = TVInfoShow()
if seas not in self.shows[sid]:
self.shows[sid][seas] = TVInfoSeason(show=self.shows[sid])
self.shows[sid][seas].number = seas
if ep not in self.shows[sid][seas]:
self.shows[sid][seas][ep] = TVInfoEpisode(season=self.shows[sid][seas], show=self.shows[sid])
# if sid not in self.ti_shows:
# self.ti_shows[sid] = TVInfoShow()
if seas not in self.ti_shows[sid]:
self.ti_shows[sid][seas] = TVInfoSeason(show=self.ti_shows[sid])
self.ti_shows[sid][seas].number = seas
if ep not in self.ti_shows[sid][seas]:
self.ti_shows[sid][seas][ep] = TVInfoEpisode(season=self.ti_shows[sid][seas], show=self.ti_shows[sid])
if attrib not in ('cast', 'crew'):
self.shows[sid][seas][ep][attrib] = value
self.shows[sid][seas][ep].__dict__[attrib] = value
self.ti_shows[sid][seas][ep][attrib] = value
self.ti_shows[sid][seas][ep].__dict__[attrib] = value
def _set_show_data(self, sid, key, value, add=False):
# type: (integer_types, Any, Any, bool) -> None
"""Sets self.shows[sid] to a new Show instance, or sets the data
"""Sets self.ti_shows[sid] to a new Show instance, or sets the data
"""
# if sid not in self.shows:
# self.shows[sid] = TVInfoShow()
# if sid not in self.ti_shows:
# self.ti_shows[sid] = TVInfoShow()
if key not in ('cast', 'crew'):
if add and isinstance(self.shows[sid].data, dict) and key in self.shows[sid].data:
self.shows[sid].data[key].update(value)
if add and isinstance(self.ti_shows[sid].data, dict) and key in self.ti_shows[sid].data:
self.ti_shows[sid].data[key].update(value)
else:
self.shows[sid].data[key] = value
self.ti_shows[sid].data[key] = value
if '_banners' == key:
p_key = 'banners'
else:
p_key = key
if add and key in self.shows[sid].__dict__ and isinstance(self.shows[sid].__dict__[p_key], dict):
self.shows[sid].__dict__[p_key].update(self.shows[sid].data[key])
if add and key in self.ti_shows[sid].__dict__ and isinstance(self.ti_shows[sid].__dict__[p_key], dict):
self.ti_shows[sid].__dict__[p_key].update(self.ti_shows[sid].data[key])
else:
self.shows[sid].__dict__[p_key] = self.shows[sid].data[key]
self.ti_shows[sid].__dict__[p_key] = self.ti_shows[sid].data[key]
else:
if add and key in self.shows[sid].__dict__ and isinstance(self.shows[sid].__dict__[key], dict):
self.shows[sid].__dict__[key].update(value)
if add and key in self.ti_shows[sid].__dict__ and isinstance(self.ti_shows[sid].__dict__[key], dict):
self.ti_shows[sid].__dict__[key].update(value)
else:
self.shows[sid].__dict__[key] = value
self.ti_shows[sid].__dict__[key] = value
def get_updated_shows(self):
# type: (...) -> Dict[integer_types, integer_types]
@ -1331,6 +1359,7 @@ class TVInfoBase(object):
msg_success = 'Treating image as %s with extracted aspect ratio'
# most posters are around 0.68 width/height ratio (eg. 680/1000)
# noinspection DuplicatedCode
if 0.55 <= img_ratio <= 0.8:
log.debug(msg_success % 'poster')
return TVInfoImageType.poster
@ -1368,6 +1397,6 @@ class TVInfoBase(object):
return self._supported_languages or []
def __str__(self):
return '<TVInfo(%s) (containing: %s)>' % (self.__class__.__name__, text_type(self.shows))
return '<TVInfo(%s) (containing: %s)>' % (self.__class__.__name__, text_type(self.ti_shows))
__repr__ = __str__

File diff suppressed because it is too large Load diff

View file

@ -4173,7 +4173,7 @@ class AddShows(Home):
+ ('', '&lid=%s' % sickgear.TVInfoAPI().config.get('langabbv_to_id', {}).get(lang, lang))[TVINFO_TVDB == tvid],
int(show['id']),
show['seriesname'], helpers.xhtml_escape(show['seriesname']), show['firstaired'],
(isinstance(show['firstaired'], string_types)
(isinstance(show['firstaired'], string_types) and show['firstaired']
and SGDatetime.sbfdate(_parse_date(show['firstaired'])) or ''),
show.get('network', '') or '', # 11
(show.get('genres', '') or show.get('genre', '') or '').replace('|', ', '), # 12