def GetMetadata(AniDBid, MALid): Log.Info("=== AniList.GetMetadata() ===".ljust(157, '=')) AniList_dict = {} # Try to match the AniDB id to an AniList id as it has a higher chance of being correct ALid = Dict(common.LoadFile(filename=AniDBid + '.json', relativeDirectory=os.path.join( 'AniList', 'json', 'AniDBid'), url=ARM_SERVER_URL.format(id=AniDBid)), "anilist", default=None) Log.Info("AniDBid={}, MALid={}, ALid={}".format(AniDBid, MALid, ALid)) if not MALid or not MALid.isdigit(): return AniList_dict Log.Info("--- series ---".ljust(157, "-")) # Use the AniList id if we got one, but fall back to the MAL id variables = {} if ALid is not None: SaveDict(ALid, variables, "id") else: SaveDict(int(MALid), variables, "malId") # Fetch data data = MakeGraphqlQuery(ANIME_DATA_DOCUMENT, variables) if data: Log.Info("--- images ---".ljust(157, "-")) posterUrl = Dict(data, "anime", "coverImage", "url") if posterUrl: Log.Info("[ ] poster: {}".format(posterUrl)) SaveDict((os.path.join('AniList', 'poster', os.path.basename(posterUrl)), common.poster_rank('AniList', 'posters'), None), AniList_dict, 'posters', posterUrl) bannerUrl = Dict(data, "anime", "bannerImage") if bannerUrl: Log.Info("[ ] banner: {}".format(bannerUrl)) SaveDict((os.path.join('AniList', 'banners', os.path.basename(bannerUrl)), common.poster_rank('AniList', 'banners'), None), AniList_dict, 'banners', bannerUrl) Log.Info("--- return ---".ljust(157, '-')) Log.Info("AniList_dict: {}".format(DictString(AniList_dict, 4))) return AniList_dict
def GetMetadata(media, movie, error_log, lang, metadata_source, AniDBid, TVDBid, IMDbid, mappingList): ''' TVDB - Load serie JSON ''' Log.Info("=== TheTVDB.GetMetadata() ===".ljust(157, '=')) TheTVDB_dict = {} max_season = 0 anidb_numbering = metadata_source == "anidb" and ( movie or max(map(int, media.seasons.keys())) <= 1) anidb_prefered = anidb_numbering and Dict(mappingList, 'defaulttvdbseason') != '1' language_series = [ language.strip() for language in Prefs['SerieLanguagePriority'].split(',') if language.strip() not in ('x-jat', 'zh-Hans', 'zh-Hant', 'zh-x-yue', 'zh-x-cmn', 'zh-x-nan', 'main') ] language_episodes = [ language.strip() for language in Prefs['EpisodeLanguagePriority'].split(',') if language.strip() not in ('x-jat', 'zh-Hans', 'zh-Hant', 'zh-x-yue', 'zh-x-cmn', 'zh-x-nan', 'main') ] Log.Info( "TVDBid: '{}', IMDbid: '{}', language_series : {}, language_episodes: {}" .format(TVDBid, IMDbid, language_series, language_episodes)) if not TVDBid.isdigit(): Log.Info('TVDBid non-digit') return TheTVDB_dict, IMDbid ### TVDB Series JSON ### Log.Info("--- series ---".ljust(157, '-')) json = {} if lang not in language_series: language_series.insert( 0, lang) #for summary in lang (library setting) language if 'en' not in language_series: language_series.insert(0, 'en') #for failover title if lang not in language_episodes: language_episodes.append( lang) #for summary in lang (library setting) language if 'en' not in language_episodes: language_episodes.append('en') #for failover title for language in language_series: json[language] = Dict( LoadFileTVDB(id=TVDBid, filename='series_{}.json'.format(language), url=TVDB_SERIES_URL.format(id=TVDBid) + '?' + language, headers={'Accept-Language': language}), 'data') if Dict(json[language], 'seriesName'): # and not Dict(TheTVDB_dict, 'language_rank'): SaveDict( language_series.index(language) if not anidb_prefered else len(language_series), TheTVDB_dict, 'language_rank') Log.Info("[ ] language_rank: {}".format( Dict(TheTVDB_dict, 'language_rank'))) Log.Info("[ ] title: {}".format( SaveDict( Dict(json[language], 'seriesName') or Dict(serie2_json, 'seriesName'), TheTVDB_dict, 'title'))) Log.Info("[ ] original_title: {}".format( SaveDict( Dict(json[language], 'seriesName') or Dict(serie2_json, 'seriesName'), TheTVDB_dict, 'original_title'))) if Dict(json, lang) and (Dict(json, lang, 'overview') or Dict(TheTVDB_dict, 'language_rank')): break #only need json in lang for summary, in 'en' for most things if not anidb_prefered: SaveDict( Dict(json, lang, 'overview').strip(" \n\r") or Dict(json, 'en', 'overview').strip(" \n\r"), TheTVDB_dict, 'summary') if Dict(json, lang): #format: { "id","seriesId", "airsDayOfWeek", "imdbId", "zap2itId", "added", "addedBy", "lastUpdated", "seriesName", "aliases", "banner", "status", # "firstAired", "network", "networkId", "runtime", "genre, "overview", "airsTime", "rating" , "siteRating", "siteRatingCount" } Log.Info("[ ] IMDbid: {}".format( SaveDict(Dict(json[lang], 'imdbId' or IMDbid), TheTVDB_dict, 'IMDbid'))) Log.Info("[ ] zap2itId: {}".format( SaveDict(Dict(json[lang], 'zap2it_id'), TheTVDB_dict, 'zap2itId'))) Log.Info("[ ] content_rating: {}".format( SaveDict(Dict(json[lang], 'rating'), TheTVDB_dict, 'content_rating'))) Log.Info("[ ] originally_available_at: {}".format( SaveDict(Dict(json[lang], 'firstAired'), TheTVDB_dict, 'originally_available_at'))) Log.Info("[ ] studio: {}".format( SaveDict(Dict(json[lang], 'network'), TheTVDB_dict, 'studio'))) Log.Info("[ ] rating: {}".format( SaveDict(Dict(json[lang], 'siteRating'), TheTVDB_dict, 'rating'))) Log.Info("[ ] status: {}".format( SaveDict(Dict(json[lang], 'status'), TheTVDB_dict, 'status'))) Log.Info("[ ] genres: {}".format( SaveDict(sorted(Dict(json[lang], 'genre')), TheTVDB_dict, 'genres'))) if Dict(json[lang], 'runtime').isdigit(): Log.Info('[ ] duration: {}'.format( SaveDict( int(Dict(json[lang], 'runtime')) * 60 * 1000, TheTVDB_dict, 'duration'))) #in ms in plex series_images = { # Pull the primary images used for the series entry 'poster': Dict(json[language], 'poster'), 'banner': Dict(json[language], 'banner'), 'fanart': Dict(json[language], 'fanart'), 'seasonwide': Dict(json[language], 'seasonwide'), 'series': Dict(json[language], 'series') } ### TVDB Series Actors JSON ### Log.Info("--- actors ---".ljust(157, '-')) actor_json = Dict(LoadFileTVDB(id=TVDBid, filename='actors_{}.json'.format(lang), url=TVDB_ACTORS_URL.format(id=TVDBid), headers={'Accept-Language': lang}), 'data', default=[]) if actor_json: #JSON format: 'data': [{"seriesId", "name", "image", "lastUpdated", "imageAuthor", "role", "sortOrder", "id", "imageAdded", },...] for role in actor_json: try: role_dict = { 'role': Dict(role, 'role'), 'name': Dict(role, 'name'), 'photo': TVDB_IMG_ROOT + role['image'] if Dict(role, 'image') else '' } SaveDict([role_dict], TheTVDB_dict, 'roles') Log.Info( "[ ] role: {:<50}, name: {:<20}, photo: {}".format( role_dict['role'], role_dict['name'], role_dict['photo'])) except Exception as e: Log.Info(" role: {}, error: '{}'".format( str(role), str(e))) #common.DisplayDict(actor_json, ['role', 'name', 'image']) ### Load pages of episodes ### Log.Info("--- episodes ---".ljust(157, '-')) episodes_json, sorted_episodes_json, next_page = [], {}, 1 while next_page not in (None, '', 'null'): episodes_json_page = LoadFileTVDB( id=TVDBid, filename='episodes_page{}_{}.json'.format(next_page, lang), url=TVDB_EPISODE_PAGE_URL.format(id=TVDBid, page=next_page), headers={'Accept-Language': lang}) next_page = Dict(episodes_json_page, 'links', 'next') episodes_json.extend(Dict(episodes_json_page, 'data')) for episode_json in episodes_json: sorted_episodes_json['s{:02d}e{:03d}'.format( Dict(episode_json, 'airedSeason'), Dict(episode_json, 'airedEpisodeNumber'))] = episode_json ### Build list_abs_eps for tvdb 3/4/5 ### list_abs_eps, list_sp_eps = {}, [] if metadata_source in ('tvdb3', 'tvdb4'): for s in media.seasons: for e in media.seasons[s].episodes: if s == '0': list_sp_eps.append(e) else: list_abs_eps[e] = s Log.Info('Present abs eps: {}'.format(list_abs_eps)) ### episode loop ### tvdb_special_missing, summary_missing_special, summary_missing, summary_present, episode_missing, episode_missing_season, episode_missing_season_all, abs_number, ep_count = [], [], [], [], [], [], True, 0, 0 for key in sorted(sorted_episodes_json): # Episode and Absolute number calculation engine, episode translation episode_json = sorted_episodes_json[key] episode = str(Dict(episode_json, 'airedEpisodeNumber')) season = str(Dict(episode_json, 'airedSeason')) numbering = "s{}e{}".format(season, episode) # Replace all the individual episodes reported as missing with a single season 'sX' entry if episode == "1": if not episode_missing_season_all: episode_missing.extend(episode_missing_season) elif episode_missing_season: first_entry, last_entry = episode_missing_season[ 0], episode_missing_season[-1] fm = re.match( r'((?P<abs>\d+) \()?s(?P<s>\d+)e(?P<e>\d+)\)?', first_entry).groupdict() lm = re.match( r'((?P<abs>\d+) \()?s(?P<s>\d+)e(?P<e>\d+)\)?', last_entry).groupdict() episode_missing.append( "s{}e{}-{}".format(fm['s'], fm['e'], lm['e']) if fm['abs'] is None else "{}-{} (s{}e{}-{})".format( fm['abs'], lm['abs'], fm['s'], fm['e'], lm['e'])) episode_missing_season, episode_missing_season_all = [], True # Get the max season number from TVDB API if int(season) > max_season: max_season = int(season) ### ep translation [Thetvdb absolute numbering followed, including in specials to stay accurate with scudlee's mapping] anidbid = "" abs_number = Dict(episode_json, 'absoluteNumber', default=0 if season == '0' else abs_number + 1) if anidb_numbering: if Dict(mappingList, 'defaulttvdbseason_a'): season, episode = '1', str(abs_number) else: season, episode, anidbid = AnimeLists.anidb_ep( mappingList, season, episode) elif metadata_source in ('tvdb3', 'tvdb4'): for s in media.seasons: #if abs id exists on disk, leave specials with no translation if str(abs_number) in list_abs_eps and str( abs_number ) in media.seasons[s].episodes and s != "0": season, episode = s, str(abs_number) break elif metadata_source == 'tvdb5': if abs_number: season, episode = '1', str(abs_number) # Record absolute number mapping for AniDB metadata pull if metadata_source == 'tvdb3': SaveDict((str(Dict(episode_json, 'airedSeason')), str(Dict(episode_json, 'airedEpisodeNumber'))), mappingList, 'absolute_map', str(abs_number)) ### Missing summaries logs ### if Dict(episode_json, 'overview'): summary_present.append(numbering) elif season != '0': summary_missing.append(numbering) else: summary_missing_special.append(numbering) ### Check for Missing Episodes ### is_missing = False if not(str(Dict(episode_json, 'airedSeason'))=='0' and str(Dict(episode_json, 'airedEpisodeNumber')) in list_sp_eps) and \ not(metadata_source in ('tvdb3', 'tvdb4') and str(abs_number) in list_abs_eps) and \ not(not movie and season in media.seasons and episode in media.seasons[season].episodes): is_missing = True Log.Info( '[ ] {:>7} s{:0>2}e{:0>3} anidbid: {:>7} air_date: {}'. format(numbering, season, episode, anidbid, Dict(episode_json, 'firstAired'))) air_date = Dict(episode_json, 'firstAired') air_date = int(air_date.replace( '-', '')) if air_date.replace('-', '').isdigit() and int( air_date.replace('-', '')) > 10000000 else 99999999 if int(time.strftime("%Y%m%d")) <= air_date + 1: pass #Log.Info("TVDB - Episode '{}' missing but not aired/missing '{}'".format(numbering, air_date)) elif season == '0': tvdb_special_missing.append(episode) elif metadata_source != 'tvdb6': episode_missing_season.append( str(abs_number) + " (" + numbering + ")" if metadata_source in ('tvdb3', 'tvdb4') else numbering) ### File present on disk if not is_missing or Dict( mappingList, 'possible_anidb3' ) or metadata_source == "tvdb6": # Only pull all if anidb3(tvdb)/anidb4(tvdb6) usage for tvdb ep/season adjustments episode_missing_season_all = False if not is_missing: Log.Info( '[X] {:>7} s{:0>2}e{:0>3} anidbid: {:>7} air_date: {} abs_number: {}, title: {}' .format(numbering, season, episode, anidbid, Dict(episode_json, 'firstAired'), abs_number, Dict(episode_json, 'episodeName'))) if not anidb_numbering: SaveDict(abs_number, TheTVDB_dict, 'seasons', season, 'episodes', episode, 'absolute_index') SaveDict(Dict(json[lang], 'rating'), TheTVDB_dict, 'seasons', season, 'episodes', episode, 'content_rating') SaveDict(Dict(TheTVDB_dict, 'duration'), TheTVDB_dict, 'seasons', season, 'episodes', episode, 'duration') SaveDict(Dict(episode_json, 'firstAired'), TheTVDB_dict, 'seasons', season, 'episodes', episode, 'originally_available_at') # Title from serie page rank, title = len(language_episodes) + 1, '' if Dict(episode_json, 'episodeName'): rank = language_episodes.index( lang) if lang in language_episodes else len( language_episodes) title = Dict(episode_json, 'episodeName') Log.Info(" - [1] title: [{}] {}".format( language_episodes[rank], title)) #Summary from serie page if Dict(episode_json, 'overview').strip(" \n\r"): SaveDict( Dict(episode_json, 'overview').strip(" \n\r"), TheTVDB_dict, 'seasons', season, 'episodes', episode, 'summary') Log.Info(' - [1] summary: [{}] {}'.format( lang, Dict(TheTVDB_dict, 'seasons', season, 'episodes', episode, 'summary'))) ### Ep advance information ### ep_count += 1 lang2 = 'en' if len( language_episodes) <= 1 else language_episodes[1] episode_details_json = Dict( LoadFileTVDB(id=TVDBid, filename='episode_{}_{}.json'.format( Dict(episode_json, 'id'), lang2), url=TVDB_EPISODE_URL.format( id=str(Dict(episode_json, 'id'))), headers={'Accept-Language': lang2}), 'data') if episode_details_json: # Std ep info loaded for Library language ten details for 1st language, loading other languages if needed if lang2 in language_episodes and language_episodes.index( lang2) < rank and Dict( episode_details_json, 'language', 'episodeName') == lang2 and Dict( episode_details_json, 'episodeName'): rank = language_episodes.index(lang2) title = Dict(episode_details_json, 'episodeName') Log.Info(" - [2] title: [{}] {}".format( language_episodes[rank], title)) #Summary if not Dict(TheTVDB_dict, 'seasons', season, 'episodes', episode, 'summary') and Dict( episode_details_json, 'overview'): SaveDict( Dict(episode_details_json, 'overview').strip(" \n\r"), TheTVDB_dict, 'seasons', season, 'episodes', episode, 'summary') Log.Info(' - [2] summary: [{}] {}'.format( lang2, Dict(TheTVDB_dict, 'seasons', season, 'episodes', episode, 'summary'))) SaveDict(Dict(episode_details_json, 'writers'), TheTVDB_dict, 'seasons', season, 'episodes', episode, 'writers') SaveDict(Dict(episode_details_json, 'directors'), TheTVDB_dict, 'seasons', season, 'episodes', episode, 'directors') SaveDict(Dict(episode_details_json, 'siteRating'), TheTVDB_dict, 'seasons', season, 'episodes', episode, 'rating') #SaveDict( Dict(episode_details_json, 'guestStars' ), TheTVDB_dict, 'seasons', season, 'episodes', episode, 'guest_stars') # Episode screenshot/Thumbnail if Dict(episode_details_json, 'filename'): SaveDict((os.path.join( "TheTVDB", "episodes", os.path.basename( Dict(episode_details_json, 'filename'))), 1, None), TheTVDB_dict, 'seasons', season, 'episodes', episode, 'thumbs', str(TVDB_IMG_ROOT + Dict(episode_details_json, 'filename'))) Log.Info(' - [ ] thumb: {}'.format( TVDB_IMG_ROOT + Dict(episode_details_json, 'filename') if Dict( episode_details_json, 'filename') else '')) #Ep title fallback (first lang title come from ep list, second from ep details) for lang_rank, language in enumerate( language_episodes[2:rank - 1] if len(language_episodes) > 1 and rank >= 2 and not title else []): if not language: continue episode_details_json = Dict(LoadFileTVDB( id=TVDBid, filename='episode_{}_{}.json'.format( Dict(episode_json, 'id'), language), url=TVDB_EPISODE_URL.format( id=str(Dict(episode_json, 'id'))), headers={'Accept-Language': lang}), 'data', default={}) if Dict(episode_details_json, 'episodeName'): title = Dict(episode_details_json, 'episodeName') rank = lang_rank Log.Info(" - [3] title: [{}] {}".format( language_episodes[rank], title)) if not Dict(TheTVDB_dict, 'seasons', season, 'episodes', episode, 'summary') and Dict( episode_details_json, 'overview'): SaveDict( Dict(episode_details_json, 'overview')[:160].strip(" \n\r"), TheTVDB_dict, 'seasons', season, 'episodes', episode, 'summary') Log.Info(' - [3] summary: [{}] {}'.format( language_episodes[lang_rank], Dict(TheTVDB_dict, 'seasons', season, 'episodes', episode, 'summary'))) if title and Dict(TheTVDB_dict, 'seasons', season, 'episodes', episode, 'summary'): break SaveDict(title, TheTVDB_dict, 'seasons', season, 'episodes', episode, 'title') SaveDict(rank, TheTVDB_dict, 'seasons', season, 'episodes', episode, 'language_rank') # (last season) Replace all the individual episodes reported as missing with a single season 'sX' entry if not episode_missing_season_all: episode_missing.extend(episode_missing_season) elif episode_missing_season: first_entry, last_entry = episode_missing_season[ 0], episode_missing_season[-1] fm = re.match(r'((?P<abs>\d+) \()?s(?P<s>\d+)e(?P<e>\d+)\)?', first_entry).groupdict() lm = re.match(r'((?P<abs>\d+) \()?s(?P<s>\d+)e(?P<e>\d+)\)?', last_entry).groupdict() episode_missing.append( "s{}e{}-{}".format(fm['s'], fm['e'], lm['e']) if fm['abs'] is None else "{}-{} (s{}e{}-{})". format(fm['abs'], lm['abs'], fm['s'], fm['e'], lm['e'])) # Set the min/max season to ints & update max value to the next min-1 to handle multi tvdb season anidb entries map_min_values = [ int(Dict(mappingList, 'season_map')[x]['min']) for x in Dict(mappingList, 'season_map', default={}) for y in Dict(mappingList, 'season_map')[x] if y == 'min' ] for entry in Dict(mappingList, 'season_map', default={}): entry_min, entry_max = int( mappingList['season_map'][entry]['min']), int( mappingList['season_map'][entry]['max']) while entry_min != 0 and entry_max + 1 not in map_min_values and entry_max < max_season: entry_max += 1 mappingList['season_map'][entry] = { 'min': entry_min, 'max': entry_max } SaveDict(max_season, mappingList, 'season_map', 'max_season') ### Logging ### if not movie: if summary_missing: error_log['Missing Episode Summaries'].append( "TVDBid: %s | Title: '%s' | Missing Episode Summaries: %s" % (common.WEB_LINK % (common.TVDB_SERIE_URL + TVDBid, TVDBid), Dict(TheTVDB_dict, 'title'), str(summary_missing))) if summary_missing_special: error_log['Missing Special Summaries'].append( "TVDBid: %s | Title: '%s' | Missing Special Summaries: %s" % (common.WEB_LINK % (common.TVDB_SERIE_URL + TVDBid, TVDBid), Dict(TheTVDB_dict, 'title'), str(summary_missing_special))) if metadata_source.startswith("tvdb") or metadata_source.startswith( "anidb") and not movie and max(map(int, media.seasons.keys())) > 1: if episode_missing: error_log['Missing Episodes'].append( "TVDBid: %s | Title: '%s' | Missing Episodes: %s" % (common.WEB_LINK % (common.TVDB_SERIE_URL + TVDBid, TVDBid), Dict(TheTVDB_dict, 'title'), str(episode_missing))) if tvdb_special_missing: error_log['Missing Specials'].append( "TVDBid: %s | Title: '%s' | Missing Specials: %s" % (common.WEB_LINK % (common.TVDB_SERIE_URL + TVDBid, TVDBid), Dict(TheTVDB_dict, 'title'), str(tvdb_special_missing))) #Log.Debug("Episodes without Summary: " + str(sorted(summary_missing, key=common.natural_sort_key))) ### Picture types JSON download ### Log.Info("--- images ---".ljust(157, '-')) languages = [ language.strip() for language in Prefs['PosterLanguagePriority'].split(',') ] Log.Info('languages: {}'.format(languages)) for language in languages: try: bannerTypes = Dict(LoadFileTVDB( id=TVDBid, filename='images_{}.json'.format(language), url=TVDB_SERIES_IMG_INFO_URL.format(id=TVDBid), headers={'Accept-Language': language}), 'data', default={}) except: Log.Info("Invalid image JSON from url: " + TVDB_SERIES_IMG_INFO_URL % TVDBid) else: #JSON format = {"fanart", "poster", "season", "seasonwide", "series"} metanames = { 'fanart': "art", 'poster': "posters", 'series': "banners", 'season': "seasons", 'seasonwide': 'seasonwide' } count_valid = {key: 0 for key in metanames} Log.Info("bannerTypes: {}".format(bannerTypes)) #Loop per banner type ("fanart", "poster", "season", "series") skip 'seasonwide' - Load bannerType images list JSON for bannerType in bannerTypes: if bannerTypes[ bannerType] == 0 or bannerType == 'seasonwide' or movie and not bannerType in ( 'fanart', 'poster'): continue #Loop if no images #if anidb_numbering and Dict(mappingList, 'defaulttvdbseason') != '1' and bannerType=='poster': continue #skip if anidb numbered serie mapping to season 0 or 2+ Log.Info( ("--- images.%s ---" % bannerType).ljust(157, '-')) try: images = Dict(LoadFileTVDB( id=TVDBid, filename='images_{}_{}.json'.format( bannerType, language), url=TVDB_SERIES_IMG_QUERY_URL.format( id=TVDBid, type=bannerType), headers={'Accept-Language': language}), 'data', default={}) except: images = {} Log.Info( "Bad image type query data for TVDB id: %s (bannerType: %s)" % (TVDBid, bannerType)) else: images = sorted( images, key=lambda x: Dict( x, "ratingsInfo", "average", default=0), reverse=True) for image in images: #JSON format = {"data": [{"id", "keyType", "subKey"(season/graphical/text), "fileName", "resolution", "ratingsInfo": {"average", "count"}, "thumbnail"}]} if not Dict(image, 'fileName'): continue #Avod "IOError: [Errno 21] Is a directory: u'/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Plug-in Support/Data/com.plexapp.agents.hama/DataItems/TheTVDB'" if filename empty count_valid[ bannerType] = count_valid[bannerType] + 1 ### Adding picture ### rank = common.poster_rank( 'TheTVDB', metanames[bannerType], language, 0 if Dict(image, 'fileName') == Dict( series_images, bannerType) else count_valid[bannerType]) fileNamePath = os.path.join( 'TheTVDB', Dict(image, 'fileName').replace('/', os.sep)) fileNameURL = TVDB_IMG_ROOT + Dict( image, 'fileName') thumbnail = TVDB_IMG_ROOT + Dict( image, 'thumbnail') if Dict( image, 'thumbnail') else None subKey = str(Dict( image, 'subKey')) # Convert to string once if bannerType == 'season': #tvdb season posters or anidb specials and defaulttvdb season ## season 0 et empty+ season ==defaulttvdbseason(a=1) if not anidb_numbering: SaveDict((fileNamePath, rank, thumbnail), TheTVDB_dict, 'seasons', subKey, 'posters', fileNameURL) else: if subKey == Dict( mappingList, 'defaulttvdbseason' ): # If the TVDB season is the AniDB default season, add season poster as series poster SaveDict( (fileNamePath, rank, thumbnail), TheTVDB_dict, 'posters', fileNameURL) if subKey in [ '0', Dict(mappingList, 'defaulttvdbseason') ]: # If the TVDB season is the season 0 OR AniDB default season, add season poster SaveDict( (fileNamePath, rank, thumbnail), TheTVDB_dict, 'seasons', '0' if subKey == '0' else '1', 'posters', fileNameURL) else: if anidb_numbering and Dict( mappingList, 'defaulttvdbseason') != '1': rank = rank + 10 SaveDict((fileNamePath, rank, thumbnail), TheTVDB_dict, metanames[bannerType], fileNameURL) Log.Info( "[!] bannerType: {:>7} subKey: {:>9} rank: {:>3} filename: {} thumbnail: {} resolution: {} average: {} count: {}" .format(metanames[bannerType], subKey, rank, fileNameURL, thumbnail, Dict(image, 'resolution'), Dict(image, 'ratingsInfo', 'average'), Dict(image, 'ratingsInfo', 'count'))) #Log.Info("Posters : {}/{}, Season posters: {}/{}, Art: {}/{}".format(count_valid['poster'], Dict(bannerTypes, 'poster'), count_valid['season'], Dict(bannerTypes, 'season') or 0, count_valid['fanart'], Dict(bannerTypes, 'fanart'))) if not Dict(bannerTypes, 'poster'): error_log['TVDB posters missing'].append( "TVDBid: %s | Title: '%s'" % (common.WEB_LINK % (common.TVDB_SERIE_URL + TVDBid, TVDBid), Dict(TheTVDB_dict, 'title'))) if not Dict(bannerTypes, 'season'): error_log['TVDB season posters missing'].append( "TVDBid: %s | Title: '%s'" % (common.WEB_LINK % (common.TVDB_SERIE_URL + TVDBid, TVDBid), Dict(TheTVDB_dict, 'title'))) Log.Info("--- final summary info ---".ljust(157, '-')) Log.Info( "url: '{}', IMDbid: {}, Present episodes: {}, Missing: {}".format( TVDB_SERIES_URL.format(id=TVDBid), IMDbid, ep_count, sorted(episode_missing, key=common.natural_sort_key))) Log.Info("--- return ---".ljust(157, '-')) Log.Info("absolute_map: {}".format( DictString(Dict(mappingList, 'absolute_map', default={}), 0))) Log.Info("season_map: {}".format( DictString(Dict(mappingList, 'season_map', default={}), 0))) Log.Info("TheTVDB_dict: {}".format(DictString(TheTVDB_dict, 4))) return TheTVDB_dict, IMDbid
def GetMetadata(movie, IMDbid): # return 200 but not downloaded correctly - IMDB has a single poster, downloading through OMDB xml, prefered by mapping file Log.Info("=== OMDb.GetMetadata() ===".ljust(157, '=')) url = OMDB_HTTP_API_URL.format(api_key=Prefs['OMDbApiKey']) #' OMDb_dict = {} if Prefs['OMDbApiKey'] in ('None', '', 'N/A'): Log.Info("No api key found - Prefs['OMDbApiKey']: '{}'".format(Prefs['OMDbApiKey'])); return OMDb_dict Log.Info("IMDbid: '%s'" % IMDbid) for imdbid_single in IMDbid.split(",") if IMDbid.startswith("tt") else []: Log.Info(("--- %s.series ---" % imdbid_single).ljust(157, '-')) json = common.LoadFile(filename=imdbid_single+".json", relativeDirectory=os.path.join('OMDb', 'json'), url=url + imdbid_single) if json: Log.Info("[ ] title: {}" .format(SaveDict( Dict(json,'title') , OMDb_dict, 'title' ))) Log.Info("[ ] summary: {}" .format(SaveDict( Dict(json,'Plot') , OMDb_dict, 'summary' ))) Log.Info("[ ] originally_available_at: {}".format(SaveDict( Dict(json,'Released') , OMDb_dict, 'originally_available_at'))) Log.Info("[ ] countries: {}" .format(SaveDict( Dict(json,'Country') , OMDb_dict, 'countries' ))) Log.Info("[ ] directors: {}" .format(SaveDict( Dict(json,'Director') , OMDb_dict, 'directors' ))) Log.Info("[ ] genres: {}" .format(SaveDict( sorted([x.strip() for x in Dict(json,'Genre').split(',')]), OMDb_dict, 'genres'))) Log.Info("[ ] writers: {}" .format(SaveDict( Dict(json,'Writer') , OMDb_dict, 'writers' ))) SaveDict( Dict(json,'imdbRating'), OMDb_dict, 'rating') if Dict(json,'Metascore').isdigit() and not Dict(OMDb_dict,'rating'): SaveDict( float(json['Metascore'])/10, OMDb_dict, 'rating') Log.Info("[ ] rating: {}".format(Dict(OMDb_dict,'rating'))) if SaveDict( Dict(json,'Rated'), OMDb_dict, 'content_rating') in common.Movie_to_Serie_US_rating and not movie and Dict(json,'Type')=="movie": Log.Info("[ ] content_rating: {}".format(SaveDict(common.Movie_to_Serie_US_rating[json['Rated']], OMDb_dict, 'content_rating'))) if Dict(json,'Poster'): Log.Info("[ ] poster: {}".format(json['Poster'])); SaveDict((os.path.join('OMDb', 'poster', imdbid_single+'.jpg'), common.poster_rank('OMDb', 'posters'), None), OMDb_dict, 'posters', json['Poster']) try: Log.Info("[ ] duration: {}".format(SaveDict( int(Dict(json,'Runtime').replace(' min','')) * 60 * 1000, OMDb_dict, 'duration'))) # Plex save duration in millisecs except: pass Log.Info("--- return ---".ljust(157, '-')) Log.Info("OMDb_dict: {}".format(DictString(OMDb_dict, 4))) return OMDb_dict
def GetMetadata(media, movie, error_log, source, AniDBid, TVDBid, AniDBMovieSets, mappingList): ''' Download metadata to dict_AniDB, ANNid, MALid ''' Log.Info("=== AniDB.GetMetadata() ===".ljust(157, '=')) AniDB_dict, ANNid, MALid = {}, "", "" original = AniDBid anidb_numbering = source == "anidb" and ( movie or max(map(int, media.seasons.keys())) <= 1) ### Build the list of anidbids for files present #### if source.startswith("tvdb") or source.startswith( "anidb") and not movie and max(map(int, media.seasons.keys( ))) > 1: #multi anidbid required only for tvdb numbering full_array = [ anidbid for season in Dict(mappingList, 'TVDB', default=[]) for anidbid in Dict(mappingList, 'TVDB', season) if season and 'e' not in season and anidbid.isdigit() ] AniDB_array = { AniDBid: [] } if Dict(mappingList, 'defaulttvdbseason') == '1' and source != 'tvdb4' else {} for season in sorted( media.seasons, key=common.natural_sort_key ) if not movie else []: # For each season, media, then use metadata['season'][season]... for episode in sorted(media.seasons[season].episodes, key=common.natural_sort_key): if int(episode) > 99: continue # AniDB non-normal special (op/ed/t/o) that is not mapable if source == 'tvdb3' and season != 0: new_season, new_episode, anidbid = AnimeLists.anidb_ep( mappingList, season, Dict(mappingList, 'absolute_map', episode, default=(None, episode)) [1]) # Pull absolute number then try to map elif source == 'tvdb4' and season != 0: new_season, new_episode = Dict(mappingList, 'absolute_map', episode, default=(season, episode)) anidbid = 'UNKN' # Not TVDB mapping. Use custom ASS mapping to pull season/episode else: new_season, new_episode, anidbid = AnimeLists.anidb_ep( mappingList, season, episode) # Try to map numbering = 's{}e{}'.format(season, episode) + ( '(s{}e{})'.format(new_season, new_episode) if season != new_season and episode != new_episode else '') if anidbid and not (new_season == '0' and new_episode == '0'): SaveDict([numbering], AniDB_array, anidbid) else: continue elif source.startswith('anidb') and AniDBid != "": full_array, AniDB_array = [AniDBid], {AniDBid: []} else: full_array, AniDB_array = [], {} active_array = full_array if Dict( mappingList, 'possible_anidb3' ) or source in ("tvdb4", "tvdb6") else AniDB_array.keys( ) # anidb3(tvdb)/anidb4(tvdb6) for full relation_map data | tvdb4 bc the above will not be able to know the AniDBid Log.Info( "Source: {}, AniDBid: {}, Full AniDBids list: {}, Active AniDBids list: {}" .format(source, AniDBid, full_array, active_array)) for anidbid in sorted(AniDB_array, key=common.natural_sort_key): Log.Info('[+] {:>5}: {}'.format(anidbid, AniDB_array[anidbid])) ### Build list_abs_eps for tvdb 3/4/5 ### list_abs_eps, list_sp_eps = {}, [] if source in ('tvdb3', 'tvdb4'): for s in media.seasons: for e in media.seasons[s].episodes: if s == '0': list_sp_eps.append(e) else: list_abs_eps[e] = s Log.Info('Present abs eps: {}'.format(list_abs_eps)) ### Load anidb xmls in tvdb numbering format if needed ### for AniDBid in sorted(active_array, key=common.natural_sort_key): is_primary_entry = AniDBid == original or len(active_array) == 1 Log.Info(("--- %s ---" % AniDBid).ljust(157, '-')) Log.Info('AniDBid: {}, IsPrimary: {}, url: {}'.format( AniDBid, is_primary_entry, ANIDB_HTTP_API_URL + AniDBid)) Log.Info(("--- %s.series ---" % AniDBid).ljust(157, '-')) xml, cache = None, CACHE_1DAY * 6 xml_cache = common.LoadFileCache(filename=AniDBid + ".xml", relativeDirectory=os.path.join( "AniDB", "xml"))[0] if isinstance(xml_cache, str): xml_cache = '' if xml_cache: # Pull the enddate and adjust max cache age based on series enddate in relation to now ed = GetXml( xml_cache, 'enddate') or datetime.datetime.now().strftime("%Y-%m-%d") enddate = datetime.datetime.strptime( "{}-12-31".format(ed) if len(ed) == 4 else "{}-{}".format( ed, ([30, 31] if int(ed[-2:]) <= 7 else [31, 30] )[int(ed[-2:]) % 2] if ed[-2:] != '02' else 28) if len(ed) == 7 else ed, '%Y-%m-%d') days_old = (datetime.datetime.now() - enddate).days if days_old > 1825: cache = CACHE_1DAY * 365 # enddate > 5 years ago => 1 year cache elif days_old > 30: cache = ( days_old * CACHE_1DAY * 365 ) / 1825 # enddate > 30 days ago => (days_old/5yrs ended = x/1yrs cache) if AniDBBan: xml = xml_cache # Ban has been hit in this process' life span (which is transient) else: xml = common.LoadFile(filename=AniDBid + ".xml", relativeDirectory=os.path.join( "AniDB", "xml"), url=ANIDB_HTTP_API_URL + AniDBid, cache=cache, sleep=6, throttle=['AniDB', CACHE_1HOUR, 100]) if isinstance(xml, str) and 'banned' in xml: global AniDBBan AniDBBan = True # Set ban hit on process level if AniDBBan: SaveDict(True, AniDB_dict, 'Banned') # Set ban hit on series level if not xml or isinstance(xml, str): title, original_title, language_rank = GetAniDBTitle( AniDBTitlesDB.xpath( '/animetitles/anime[@aid="{}"]/title'.format(AniDBid))) if is_primary_entry: Log.Info("[ ] title: {}".format( SaveDict(title, AniDB_dict, 'title'))) Log.Info("[ ] original_title: {}".format( SaveDict(original_title, AniDB_dict, 'original_title'))) Log.Info("[ ] language_rank: {}".format( SaveDict(language_rank, AniDB_dict, 'language_rank'))) elif xml: title, original_title, language_rank = GetAniDBTitle( xml.xpath('/anime/titles/title')) if is_primary_entry: ### for each main anime AniDBid ### Log.Info("[ ] title: {}".format( SaveDict(title, AniDB_dict, 'title'))) Log.Info("[ ] original_title: {}".format( SaveDict(original_title, AniDB_dict, 'original_title'))) Log.Info("[ ] language_rank: {}".format( SaveDict(language_rank, AniDB_dict, 'language_rank'))) if SaveDict(GetXml(xml, 'startdate'), AniDB_dict, 'originally_available_at'): Log.Info("[ ] originally_available_at: '{}'".format( AniDB_dict['originally_available_at'])) if SaveDict(summary_sanitizer(GetXml( xml, 'description')), AniDB_dict, 'summary' ) and not movie and not anidb_numbering and Dict( mappingList, 'defaulttvdbseason').isdigit() and mappingList[ 'defaulttvdbseason'] in media.seasons: SaveDict(AniDB_dict['summary'], AniDB_dict, 'seasons', mappingList['defaulttvdbseason'], 'summary') Log.Info("[ ] rating: '{}'".format( SaveDict(GetXml(xml, 'ratings/permanent'), AniDB_dict, 'rating'))) ### Posters if GetXml(xml, 'picture'): AniDB_dict['posters'] = { ANIDB_PIC_BASE_URL + GetXml(xml, 'picture'): (os.path.join('AniDB', 'poster', GetXml(xml, 'picture')), common.poster_rank('AniDB', 'posters'), None) } # ANIDB_PIC_THUMB_URL.format(name=GetXml(xml, 'picture').split('.')[0]) ### genre ### RESTRICTED_GENRE = { "18 restricted": 'X', "pornography": 'X', "tv censoring": 'TV-MA', "borderline p**n": 'TV-MA' } for tag in xml.xpath('tags/tag'): if GetXml(tag, 'name') and tag.get( 'weight', '').isdigit() and int( tag.get('weight', '') or '200') >= int( Prefs['MinimumWeight'] or '200'): SaveDict([string.capwords(GetXml(tag, 'name'), '-')], AniDB_dict, 'genres') if GetXml(tag, 'name').lower() in RESTRICTED_GENRE: AniDB_dict['content_rating'] = RESTRICTED_GENRE[ GetXml(tag, 'name').lower()] if Dict(AniDB_dict, 'genres'): AniDB_dict['genres'].sort() SaveDict( "Continuing" if GetXml(xml, 'Anime/enddate') == "1970-01-01" else "Ended", AniDB_dict, 'status') Log.Info("[ ] genres ({}/{} above {} weight): {}".format( len(Dict(AniDB_dict, 'genres')), len(xml.xpath('tags/tag')), int(Prefs['MinimumWeight'] or 200), Dict(AniDB_dict, 'genres'))) for element in AniDBMovieSets.xpath( "/anime-set-list/set/anime"): if element.get('anidbid') == AniDBid or element.get( 'anidbid') in full_array: node = element.getparent() title, main, language_rank = GetAniDBTitle( node.xpath('titles')[0]) if title not in Dict(AniDB_dict, 'collections', default=[]): Log.Info( "[ ] title: {}, main: {}, language_rank: {}". format(title, main, language_rank)) SaveDict([title], AniDB_dict, 'collections') Log.Info( "[ ] collection: AniDBid '%s' is part of movie collection: '%s', related_anime_list: %s" % (AniDBid, title, str(full_array))) if not Dict(AniDB_dict, 'collections'): Log.Info( "[ ] collection: AniDBid '%s' is not part of any collection, related_anime_list: %s" % (AniDBid, str(full_array))) #roles ### NEW, NOT IN Plex FrameWork Documentation 2.1.1 ### Log.Info(("--- %s.actors ---" % AniDBid).ljust(157, '-')) for role in xml.xpath( 'characters/character[(@type="secondary cast in") or (@type="main character in")]' ): try: if GetXml(role, 'seiyuu') and GetXml(role, 'name'): role_dict = { 'role': role.find('name').text, 'name': role.find('seiyuu').text, 'photo': ANIDB_PIC_BASE_URL + role.find('seiyuu').get('picture') } SaveDict([role_dict], AniDB_dict, 'roles') Log.Info( '[ ] role: {:<20}, name: {:<20}, photo: {}'. format(role_dict['role'], role_dict['name'], role_dict['photo'])) except Exception as e: Log.Info("Seyiuu error: {}".format(e)) ### Creators ### creator_tags = { "Animation Work": "studio", "Work": "studio", "Direction": "directors", "Series Composition": "producers", "Original Work": "writers", "Script": "writers", "Screenplay": "writers" } studios = {} creators = {} for creator in xml.xpath('creators/name'): for tag in creator_tags: if tag != creator.get('type'): continue if creator_tags[tag] == "studio": studios[tag] = creator.text else: SaveDict([creator.text], creators, creator_tags[tag]) if is_primary_entry: Log.Info("[ ] studio: {}".format( SaveDict( Dict(studios, "Animation Work", default=Dict(studios, "Work")), AniDB_dict, 'studio'))) Log.Info("[ ] movie: {}".format( SaveDict(GetXml(xml, 'type') == 'Movie', AniDB_dict, 'movie'))) ### Movie ### if movie: Log.Info("[ ] year: '{}'".format( SaveDict( GetXml(xml, 'startdate')[0:4], AniDB_dict, 'year'))) if is_primary_entry: for creator in creators: Log.Info("[ ] {}: {}".format( creator, SaveDict(creators[creator], AniDB_dict, creator))) Log.Info(("--- %s.summary info ---" % AniDBid).ljust(157, '-')) ### Series ### else: ### Translate into season/episode mapping numEpisodes, totalDuration, mapped_eps, ending_table, op_nb = 0, 0, [], {}, 0 specials = { 'S': [0, 'Special'], 'C': [100, 'Opening/Ending'], 'T': [200, 'Trailer'], 'P': [300, 'Parody'], 'O': [400, 'Other'] } movie_ep_groups = {} ending_offset = 99 missing = {'0': [], '1': []} ### Episodes (and specials) not always in right order ### Log.Info(("--- %s.episodes ---" % AniDBid).ljust(157, '-')) Log.Info("[ ] ep creators (creators tag): " + str(creators)) for ep_obj in sorted( xml.xpath('episodes/episode'), key=lambda x: [ int(x.xpath('epno')[0].get('type')), int( x.xpath('epno')[0].text if x.xpath('epno')[0].text.isdigit() else x. xpath('epno')[0].text[1:]) ]): ### Title, Season, Episode number, Specials title, main, language_rank = GetAniDBTitle( ep_obj.xpath('title'), [ language.strip() for language in Prefs['EpisodeLanguagePriority'].split(',') ]) if not anidb_numbering and title == 'Complete Movie': title = "" # For mapping use meanningful titles epNum = ep_obj.xpath('epno')[0] epNumType = epNum.get('type') season = "1" if epNumType == "1" else "0" if epNumType == "3" and ep_obj.xpath( 'title')[0].text.startswith('Ending') and int( epNum.text[1:]) - 1 < ending_offset: ending_offset = int(epNum.text[1:]) - 1 if epNumType == "3" and int( epNum.text[1:]) > ending_offset: episode = str( int(epNum.text[1:]) + 150 - ending_offset) #shifted to 150 for 1st ending. elif epNumType == "1": episode = epNum.text else: episode = str(specials[epNum.text[0]][0] + int(epNum.text[1:])) numbering = "s{}e{:>3}".format(season, episode) #If tvdb numbering used, save anidb episode meta using tvdb numbering if source.startswith("tvdb") or source.startswith( "anidb") and not movie and max( map(int, media.seasons.keys())) > 1: season, episode = AnimeLists.tvdb_ep( mappingList, season, episode, AniDBid) # Get episode number to absolute number if source in ('tvdb3', 'tvdb4') and season not in ['-1', '0']: if source == 'tvdb4' or season == '1': ms, usl = ( season, True) if source == 'tvdb3' else ( Dict(mappingList, 'absolute_map', 'max_season'), Dict(mappingList, 'absolute_map', 'unknown_series_length')) if ms and usl: season = Dict(mappingList, 'absolute_map', episode, default=(ms if usl else str(int(ms) + 1), None))[0] else: try: episode = list( Dict(mappingList, 'absolute_map', default={}).keys())[list( Dict(mappingList, 'absolute_map', default={}).values() ).index((season, episode))] except: pass if not(season =='0' and episode in list_sp_eps) and \ not(source in ('tvdb3', 'tvdb4') and episode in list_abs_eps) and \ not(season in media.seasons and episode in media.seasons[season].episodes): Log.Info( '[ ] {} => s{:>1}e{:>3} epNumType: {}'.format( numbering, season, episode, epNumType)) continue ### Series poster as season poster if GetXml(xml, 'picture') and not Dict( AniDB_dict, 'seasons', season, 'posters', ANIDB_PIC_BASE_URL + GetXml(xml, 'picture')): SaveDict( (os.path.join('AniDB', 'poster', GetXml(xml, 'picture')), common.poster_rank('AniDB', 'posters'), None), AniDB_dict, 'seasons', season, 'posters', ANIDB_PIC_BASE_URL + GetXml(xml, 'picture')) ### In AniDB numbering, Movie episode group, create key and create key in dict with empty list if doesn't exist ### else: #if source.startswith("anidb") and not movie and max(map(int, media.seasons.keys()))<=1: ### Movie episode group, create key and create key in dict with empty list if doesn't exist ### key = '' if epNumType == '1' and GetXml( xml, '/anime/episodecount') == '1' and GetXml( xml, '/anime/type') in ('Movie', 'OVA'): key = '1' if title in ( 'Complete Movie', 'OVA' ) else title[-1] if title.startswith( 'Part ') and title[-1].isdigit() else '' #'-1' if key: SaveDict([], movie_ep_groups, key) #Episode missing from disk if not season in media.seasons or not episode in media.seasons[ season].episodes: Log.Info( '[ ] {} => s{:>1}e{:>3} air_date: {}'.format( numbering, season, episode, GetXml(ep_obj, 'airdate'))) current_air_date = GetXml(ep_obj, 'airdate').replace( '-', '') current_air_date = int( current_air_date ) if current_air_date.isdigit() and int( current_air_date) > 10000000 else 99999999 if int(time.strftime( "%Y%m%d")) > current_air_date + 1: if epNumType == '1' and key: SaveDict([numbering], movie_ep_groups, key) elif epNumType in ['1', '2']: SaveDict([episode], missing, season) continue ### Episodes SaveDict(language_rank, AniDB_dict, 'seasons', season, 'episodes', episode, 'language_rank') SaveDict(title, AniDB_dict, 'seasons', season, 'episodes', episode, 'title') Log.Info( '[X] {} => s{:>1}e{:>3} air_date: {} language_rank: {}, title: "{}"' .format(numbering, season, episode, GetXml(ep_obj, 'airdate'), language_rank, title)) if GetXml(ep_obj, 'length').isdigit(): SaveDict( int(GetXml(ep_obj, 'length')) * 1000 * 60, AniDB_dict, 'seasons', season, 'episodes', episode, 'duration' ) # AniDB stores it in minutes, Plex save duration in millisecs if season == "1": numEpisodes, totalDuration = numEpisodes + 1, totalDuration + int( GetXml(ep_obj, 'length')) SaveDict(GetXml(ep_obj, 'rating'), AniDB_dict, 'seasons', season, 'episodes', episode, 'rating') SaveDict(GetXml(ep_obj, 'airdate'), AniDB_dict, 'seasons', season, 'episodes', episode, 'originally_available_at') ep_summary = SaveDict( summary_sanitizer(GetXml(ep_obj, 'summary')), AniDB_dict, 'seasons', season, 'episodes', episode, 'summary') Log.Info(' - [ ] summary: {}'.format( (ep_summary[:200] ).replace("\n", "\\n").replace("\r", "\\r") + '..' if len(ep_summary) > 200 else ep_summary)) for creator in creators: SaveDict(",".join(creators[creator]), AniDB_dict, 'seasons', season, 'episodes', episode, creator) ### End of for ep_obj... Log.Info(("--- %s.summary info ---" % AniDBid).ljust(157, '-')) if SaveDict((int(totalDuration) / int(numEpisodes)) * 60 * 1000 if int(numEpisodes) else 0, AniDB_dict, 'duration'): Log.Info( "Duration: {}, numEpisodes: {}, average duration: {}". format(str(totalDuration), str(numEpisodes), AniDB_dict['duration'])) ### AniDB numbering Missing Episodes ### if source.startswith("anidb") and not movie and max( map(int, media.seasons.keys())) <= 1: if movie_ep_groups: Log.Info( "Movie/OVA Ep Groups: %s" % movie_ep_groups ) #movie_ep_groups: {'1': ['s1e1'], '3': ['s1e4', 's1e5', 's1e6'], '2': ['s1e3'], '-1': []} SaveDict([ value for key in movie_ep_groups for value in movie_ep_groups[key] if 0 < len(movie_ep_groups[key]) < int(key) ], missing, '1') for season in sorted(missing): missing_eps = sorted(missing[season], key=common.natural_sort_key) Log.Info('Season: {} Episodes: {} not on disk'.format( season, missing_eps)) if missing_eps: error_log[ 'Missing Specials' if season == '0' else 'Missing Episodes'].append( "AniDBid: %s | Title: '%s' | Missing Episodes: %s" % (common.WEB_LINK % (common.ANIDB_SERIE_URL + AniDBid, AniDBid), AniDB_dict['title'], str(missing_eps))) ### End of if not movie ### # Generate relations_map for anidb3/4(tvdb1/6) modes for relatedAnime in xml.xpath('/anime/relatedanime/anime'): if relatedAnime.get('id') not in Dict(mappingList, 'relations_map', AniDBid, relatedAnime.get('type'), default=[]): SaveDict([relatedAnime.get('id')], mappingList, 'relations_map', AniDBid, relatedAnime.get('type')) # External IDs ANNid = GetXml( xml, "/anime/resources/resource[@type='1']/externalentity/identifier" ) MALid = GetXml( xml, "/anime/resources/resource[@type='2']/externalentity/identifier" ) #ANFOid = GetXml(xml, "/anime/resources/resource[@type='3']/externalentity/identifier"), GetXml(xml, "/anime/resources/resource[@type='3']/externalentity/identifier") # Logs if not Dict(AniDB_dict, 'summary'): error_log['AniDB summaries missing'].append( "AniDBid: %s" % (common.WEB_LINK % (common.ANIDB_SERIE_URL + AniDBid, AniDBid) + " | Title: '%s'" % Dict(AniDB_dict, 'title'))) if not Dict(AniDB_dict, 'posters'): error_log['AniDB posters missing'].append( "AniDBid: %s" % (common.WEB_LINK % (common.ANIDB_SERIE_URL + AniDBid, AniDBid) + " | Title: '%s'" % Dict(AniDB_dict, 'title'))) #if not Dict(AniDB_dict, 'studio' ): error_log['anime-list studio logos'].append("AniDBid: %s | Title: '%s' | AniDB has studio '%s' and anime-list has '%s' | " % (common.WEB_LINK % (ANIDB_SERIE_URL % AniDBid, AniDBid), title, metadata.studio, mapping_studio) + common.WEB_LINK % (ANIDB_TVDB_MAPPING_FEEDBACK % ("aid:" + metadata.id + " " + title, String.StripTags( XML.StringFromElement(xml, encoding='utf8'))), "Submit bug report (need GIT account)")) #if metadata.studio and 'studio' in AniDB_dict and AniDB_dict ['studio'] and AniDB_dict ['studio'] != metadata.studio: error_log['anime-list studio logos'].append("AniDBid: %s | Title: '%s' | AniDB has studio '%s' and anime-list has '%s' | " % (common.WEB_LINK % (ANIDB_SERIE_URL % AniDBid, AniDBid), title, metadata.studio, mapping_studio) + common.WEB_LINK % (ANIDB_TVDB_MAPPING_FEEDBACK % ("aid:" + metadata.id + " " + title, String.StripTags( XML.StringFromElement(xml, encoding='utf8'))), "Submit bug report (need GIT account)")) #if metadata.studio == "" and 'studio' in AniDB_dict and AniDB_dict ['studio'] == "": error_log['anime-list studio logos'].append("AniDBid: %s | Title: '%s' | AniDB and anime-list are both missing the studio" % (common.WEB_LINK % (ANIDB_SERIE_URL % AniDBid, AniDBid), title) ) Log.Info("ANNid: '%s', MALid: '%s', xml loaded: '%s'" % (ANNid, MALid, str(xml is not None))) Log.Info("--- return ---".ljust(157, '-')) Log.Info("relations_map: {}".format( DictString(Dict(mappingList, 'relations_map', default={}), 1))) Log.Info("AniDB_dict: {}".format(DictString(AniDB_dict, 4))) return AniDB_dict, ANNid, MALid
def GetMetadata(movie, MALid): Log.Info("=== MyAnimeList.GetMetadata() ===".ljust(157, '=')) MyAnimeList_dict = {} Log.Info("MALid: '%s'" % MALid) if not MALid or not MALid.isdigit(): return MyAnimeList_dict Log.Info("--- series ---".ljust(157, '-')) xml = common.LoadFile(filename=MALid + ".xml", relativeDirectory=os.path.join('MyAnimeList', 'xml'), url=MAL_HTTP_API_URL + MALid) if isinstance(xml, str): Log.Error('Invalid str returned: "{}"'.format(xml)) elif xml: Log.Info("[ ] title: {}".format( SaveDict(GetXml(xml, 'title'), MyAnimeList_dict, 'title'))) Log.Info("[ ] summary: {}".format( SaveDict(GetXml(xml, 'synopsis'), MyAnimeList_dict, 'summary'))) Log.Info("[ ] score: {}".format( SaveDict(GetXml(xml, 'rating'), MyAnimeList_dict, 'score'))) #Log.Info("[ ] rating: {}" .format(SaveDict( GetXml(xml, 'content_rating').split(" ")[0], MyAnimeList_dict, 'rating' ))) Log.Info("[ ] originally_available_at: {}".format( SaveDict(GetXml(xml, 'firstAired'), MyAnimeList_dict, 'originally_available_at'))) #for item in xml.xpath('//anime/genres/genre' or []): SaveDict([item.text], MyAnimeList_dict, 'genres') if GetXml(xml, '//anime/genres/genre'): Log.Info("[ ] genres: {}".format( SaveDict( sorted([ item.text for item in xml.xpath('//anime/genres/genre') ]), MyAnimeList_dict, 'genres'))) if GetXml(xml, 'status') == 'Currently Airing': Log.Info("[ ] status: {}".format( SaveDict("Continuing", MyAnimeList_dict, 'status'))) if GetXml(xml, 'status') == 'Finished Airing': Log.Info("[ ] status: {}".format( SaveDict("Ended", MyAnimeList_dict, 'status'))) Log.Info("--- episodes ---".ljust(157, '-')) for item in xml.xpath('//anime/episodes/episode') or []: ep_number, ep_title, ep_air = GetXml( item, 'episodeNumber'), GetXml(xml, 'engTitle'), GetXml(xml, 'aired') Log.Info('[ ] s1e{:>3} air_date: {}, title: "{}"'.format( ep_number, ep_title, ep_air)) SaveDict(ep_title, MyAnimeList_dict, 'seasons', "1", 'episodes', ep_number, 'title') SaveDict(ep_air, MyAnimeList_dict, 'seasons', "1", 'episodes', ep_number, 'originally_available_at') Log.Info("--- images ---".ljust(157, '-')) for item in xml.xpath('//anime/covers/cover'): Log.Info("[ ] poster: {}".format( SaveDict(("MyAnimeList/" + "/".join(item.text.split('/')[3:]), common.poster_rank('MyAnimeList', 'posters'), None) if item.text.startswith(MAL_PREFIX) else "", MyAnimeList_dict, 'posters', item.text))) for item in xml.xpath('//anime/backgrounds/background'): Log.Info("[ ] art: {}".format( SaveDict(("MyAnimeList/" + "/".join(item.text.split('/')[3:]), common.poster_rank('MyAnimeList', 'art'), None) if item.text.startswith(MAL_PREFIX) else "", MyAnimeList_dict, 'art', item.text))) for item in xml.xpath('//anime/banners/banner'): Log.Info("[ ] banner: {}".format( SaveDict(("MyAnimeList/" + "/".join(item.text.split('/')[3:]), common.poster_rank('MyAnimeList', 'banners'), None) if item.text.startswith(MAL_PREFIX) else "", MyAnimeList_dict, 'banners', item.text))) Log.Info("--- return ---".ljust(157, '-')) Log.Info("MyAnimeList_dict: {}".format(DictString(MyAnimeList_dict, 4))) return MyAnimeList_dict
def GetMetadata(media, movie, TVDBid, TMDbid, IMDbid): Log.Info("=== TheMovieDb.GetMetadata() ===".ljust(157, '=')) TheMovieDb_dict = {} TSDbid = "" Log.Info("TVDBid: '{}', TMDbid: '{}', IMDbid: '{}'".format( TVDBid, TMDbid, IMDbid)) if TMDbid: url, filename = TMDB_MOVIE_SEARCH_BY_TMDBID.format( id=TMDbid), "TMDB-" + TMDbid + ".json" elif IMDbid: url, filename = TMDB_MOVIE_SEARCH_BY_TMDBID.format( id=IMDbid), "IMDb-" + IMDbid + ".json" elif TVDBid.isdigit(): url, filename = TMDB_SERIE_SEARCH_BY_TVDBID.format( id=TVDBid), "TVDB-" + TVDBid + ".json" else: return TheMovieDb_dict, TSDbid, TMDbid, IMDbid mode = "movie" if movie else "tv" Log.Info(("--- %s ---" % mode).ljust(157, '-')) json = common.LoadFile(filename=filename, relativeDirectory=os.path.join( 'TheMovieDb', 'json'), url=url) config_dict = common.LoadFile(filename="TMDB_CONFIG_URL.json", relativeDirectory="TheMovieDb", url=TMDB_CONFIG_URL, cache=CACHE_1MONTH) image_base_url = Dict(config_dict, 'images', 'secure_base_url') if not json: Log.Info("TMDB - url: failed to get json" + TMDB_MOVIE_SEARCH_BY_TMDBID.format(id=TMDbid)) else: if Dict(json, 'tv_results'): json, mode = json['tv_results'][0], "tv" elif Dict(json, 'movie_results'): json, mode = json['movie_results'][0], "movie" Log.Info("[ ] title: {}".format( SaveDict( Dict(json, 'title') or Dict(json, 'name'), TheMovieDb_dict, 'title'))) Log.Info( "[ ] rating: {}".format( SaveDict(Dict(json, 'vote_average'), TheMovieDb_dict, 'rating')) ) #if 'vote_count' in json and json['vote_count'] > 3: SaveDict( Dict(json, 'vote_average'), TheMovieDb_dict, 'rating') Log.Info("[ ] tagline: {}".format( SaveDict(Dict(json, 'tagline'), TheMovieDb_dict, 'tagline'))) Log.Info("[ ] summary: {}".format( SaveDict(Dict(json, 'overview'), TheMovieDb_dict, 'summary'))) Log.Info("[ ] duration: {}".format( SaveDict(Dict(json, 'runtime'), TheMovieDb_dict, 'duration'))) Log.Info("[ ] countries: {}".format( SaveDict(Dict(json, 'origin_country'), TheMovieDb_dict, 'countries'))) Log.Info("[ ] originally_available_at: {}".format( SaveDict( Dict(json, 'first_air_date') or Dict(json, 'release_date'), TheMovieDb_dict, 'originally_available_at'))) if Dict(json, 'belongs_to_collection', 'name'): Log.Info("[ ] collections: {}".format( SaveDict([Dict(json, 'belongs_to_collection', 'name')], TheMovieDb_dict, 'collections'))) if Dict(json, 'genres'): Log.Info("[ ] genres: {}".format( SaveDict( sorted([ Dict(genre, 'name') for genre in Dict(json, 'genres', default=[]) ]), TheMovieDb_dict, 'genres'))) if Dict(json, 'poster_path'): Log.Info("[ ] poster: {}".format(image_base_url + 'original' + json['poster_path'])) SaveDict((os.path.join('TheMovieDb', 'poster', json['poster_path'].lstrip('/')), common.poster_rank('TheMovieDb', 'posters'), None), TheMovieDb_dict, 'posters', image_base_url + 'original' + json['poster_path']) if Dict(json, 'backdrop_path'): Log.Info("[ ] art: {}".format(image_base_url + 'original' + json['backdrop_path'])) SaveDict((os.path.join('TheMovieDb', 'artwork', json['backdrop_path'].lstrip('/')), common.poster_rank('TheMovieDb', 'art'), image_base_url + 'w300' + json['backdrop_path']), TheMovieDb_dict, 'art', image_base_url + 'original' + json['backdrop_path']) try: Log.Info("[ ] duration: {}".format( SaveDict( int(Dict(json, 'duration')) * 60 * 1000, TheMovieDb_dict, 'duration'))) except: pass if mode == 'tv': TSDbid = str(Dict(json, 'id')) elif not TMDbid: TMDbid = str(Dict(json, 'id')) if not IMDbid: IMDbid = Dict(json, 'imdb_id') for studio in Dict(json, 'production_companies', default=[]): if studio['id'] <= json['production_companies'][0]['id']: Log.Info("[ ] studio: {}".format( SaveDict(studio['name'].strip(), TheMovieDb_dict, 'studio'))) ### More pictures ### Log.Info("--- pictures.more ---".ljust(157, '-')) Log.Info("TMDbid: '{}', TSDbid: '{}', IMDbid: '{}'".format( TMDbid, TSDbid, IMDbid)) for id in IMDbid.split(',') if ',' in IMDbid else []: json = common.LoadFile(filename="TMDB-" + (IMDbid or TMDbid) + ".json", relativeDirectory="TMDB", url=TMDB_MOVIE_IMAGES_URL.format(id=id, mode=mode)) for index, poster in enumerate(Dict(json, 'posters', default=[])): if Dict(poster, 'file_path'): Log.Info("[ ] poster: {}".format(image_base_url + 'original' + poster['file_path'])) SaveDict((os.path.join('TheMovieDb', 'poster', "%s-%s.jpg" % (TMDbid, index)), common.poster_rank('TheMovieDb', 'posters'), None), TheMovieDb_dict, 'posters', image_base_url + 'original' + poster['file_path']) for index, backdrop in enumerate(Dict(json, 'backdrops', default=[])): if Dict(backdrop, 'file_path'): Log.Info("[ ] artwork: {}".format(image_base_url + 'original' + backdrop['file_path'])) SaveDict((os.path.join( 'TheMovieDb', 'artwork', "%s-%s-art.jpg" % (TMDbid, index)), common.poster_rank('TheMovieDb', 'art'), image_base_url + 'w300' + backdrop['file_path']), TheMovieDb_dict, 'art', image_base_url + 'original' + backdrop['file_path']) Log.Info("--- return ---".ljust(157, '-')) Log.Info("TheMovieDb_dict: {}".format(DictString(TheMovieDb_dict, 4))) return TheMovieDb_dict, TSDbid, TMDbid, IMDbid
def GetMetadata(movie=False, TVDBid="", tmdbid="", imdbid="", season=0): #Fetch from fanart.tv - Posters Seasons Fanarts Banner Log.Info("=== FanartTv.GetMetadata() ===".ljust(157, '=')) FanartTV_dict = {} Log.Info("movie:'{movie}', TVDBid: '{TVDBid}', tmdbid: '{tmdbid}', imdbid:'{imdbid}', season: '{season}'".format(movie=movie, TVDBid=TVDBid, tmdbid=tmdbid, imdbid=imdbid, season=season)) if "," in imdbid: (GetMetadata(movie, "", "", imdbid_unique, season) for imdbid_unique in (tmdbid or imdbid).split(",")); return #recusive call for each imdbid to reduce complexity if "," in tmdbid: (GetMetadata(movie, "", tmdbid_unique, "", season) for tmdbid_unique in tmdbid.split(",")); return #recusive call for each tmdbid to reduce complexity if not movie and TVDBid.isdigit(): id, relativeDirectory, url = TVDBid, os.path.join("FanartTV", "tv", TVDBid), FTV_API_TV_URL.format(id=TVDBid) elif movie and (imdbid or tmdbid): id, relativeDirectory, url = imdbid or tmdbid, os.path.join("FanartTV", "movie", imdbid or tmdbid), FTV_API_MOVIES_URL.format(id=imdbid or tmdbid) else: return FanartTV_dict if TVDBid or tmdbid or imdbid: Log.Info(("--- %s.images ---" % id).ljust(157, '-')) json = common.LoadFile(filename=id+".json", relativeDirectory=relativeDirectory, url=url) #Movies if json and (imdbid or tmdbid): for item in Dict(json, 'movieposter' , default=[]): Log.Info("[ ] poster: {}".format(Dict(item, 'url'))); SaveDict((os.path.join(relativeDirectory, id, "movieposter", "{filename}.jpg".format(filename=Dict(item, 'id'))), common.poster_rank('FanartTV', 'posters'), None), FanartTV_dict, 'posters', Dict(item, 'url')) for item in Dict(json, 'moviebackground', default=[]): Log.Info("[ ] art: {}" .format(Dict(item, 'url'))); SaveDict((os.path.join(relativeDirectory, id, "moviebackground", "{filename}.jpg".format(filename=Dict(item, 'id'))), common.poster_rank('FanartTV', 'art' ), None), FanartTV_dict, 'art', Dict(item, 'url')) #Series if json and TVDBid.isdigit(): for item in Dict(json, 'tvposter' , default=[]): Log.Info("[ ] poster: {}" .format(Dict(item, 'url'))); SaveDict((os.path.join(relativeDirectory, id, "tvposter", "{filename}.jpg".format(filename=Dict(item, 'id'))), common.poster_rank('FanartTV', 'posters'), None), FanartTV_dict, 'posters', Dict(item, 'url')) for item in Dict(json, 'showbackground' , default=[]): Log.Info("[ ] art: {}" .format(Dict(item, 'url'))); SaveDict((os.path.join(relativeDirectory, id, "showbackground", "{filename}.jpg".format(filename=Dict(item, 'id'))), common.poster_rank('FanartTV', 'art' ), None), FanartTV_dict, 'art', Dict(item, 'url')) for item in Dict(json, 'tvbanner' , default=[]): Log.Info("[ ] banner: {}" .format(Dict(item, 'url'))); SaveDict((os.path.join(relativeDirectory, id, "tvbanner", "{filename}.jpg".format(filename=Dict(item, 'id'))), common.poster_rank('FanartTV', 'banners'), None), FanartTV_dict, 'banners', Dict(item, 'url')) for item in Dict(json, 'seasonposter' , default=[]): Log.Info("[ ] season poster: {}".format(Dict(item, 'url'))); SaveDict((os.path.join(relativeDirectory, id, "seasonposter", "{filename}.jpg".format(filename=Dict(item, 'id'))), common.poster_rank('FanartTV', 'posters'), None), FanartTV_dict, 'seasons', Dict(item, 'season'), 'posters', Dict(item, 'url')) Log.Info("--- return ---".ljust(157, '-')) Log.Info("FanartTV_dict: {}".format(DictString(FanartTV_dict, 4))) return FanartTV_dict
def GetMetadata(media, movie, source, TVDBid, mappingList, num=0): """ [tvdb4.mapping.xml] Attempt to get the ASS's episode mapping data [tvdb4.posters.xml] Attempt to get the ASS's image data """ Log.Info('=== tvdb4.GetMetadata() ==='.ljust(157, '=')) TVDB4_dict, TVDB4_mapping, TVDB4_xml = {}, None, None if movie or not source == "tvdb4": Log.Info("not tvdb4 mode") return TVDB4_dict Log.Info("tvdb4 mode") def find_tvdb4_file(file_to_find): try: folder = common.GetMediaDir(media, movie) while folder and folder[-1] not in ["/", "\\"]: filename = os.path.join(folder, file_to_find) if os.path.exists(filename): file = Core.storage.load(os.path.realpath(filename)) try: return XML.ElementFromString(file) except: return file folder = os.path.dirname(folder) else: Log.Info("No '{}' file detected locally".format(file_to_find)) except Exception as e: Log.Error( "Issues in finding setup info as directories have most likely changed post scan into Plex, Exception: '%s'" % e) return "" Log.Info("--- tvdb4.mapping.xml ---".ljust(157, '-')) TVDB4_mapping = find_tvdb4_file("tvdb4.mapping") if TVDB4_mapping: Log.Debug("'tvdb4.mapping' file detected locally") else: TVDB4_mapping = TVDB4_mapping or common.LoadFile( filename=os.path.basename(TVDB4_MAPPING_URL), url=TVDB4_MAPPING_URL) entry = "" if isinstance(TVDB4_mapping, str): entry = TVDB4_mapping else: entry = common.GetXml(TVDB4_mapping, "/tvdb4entries/anime[@tvdbid='%s']" % TVDBid) if not entry: Log.Error("TVDBid '%s' is not found in mapping file" % TVDBid) if entry: for line in filter(None, entry.strip().splitlines()): season = line.strip().split("|") for absolute_episode in range(int(season[1]), int(season[2]) + 1): SaveDict((str(int(season[0])), str(absolute_episode)), mappingList, 'absolute_map', str(absolute_episode)) SaveDict(True if "(unknown length)" in season[3] else False, mappingList, 'absolute_map', 'unknown_series_length') SaveDict(str(int(season[0])), mappingList, 'absolute_map', 'max_season') Log.Info( "[ ] season: {}, starting episode: {}, ending episode: {}, label: {}" .format(season[0], season[1], season[2], season[3])) Log.Info("--- tvdb4.posters.xml ---".ljust(157, '-')) TVDB4_xml = find_tvdb4_file(os.path.basename(TVDB4_POSTERS_URL)) if TVDB4_xml: Log.Debug("'tvdb4.posters.xml' file detected locally") else: TVDB4_xml = TVDB4_xml or common.LoadFile( filename=os.path.basename(TVDB4_POSTERS_URL), url=TVDB4_POSTERS_URL) if TVDB4_xml: season_posters = {} entry = common.GetXml(TVDB4_xml, "/tvdb4entries/posters[@tvdbid='%s']" % TVDBid) if not entry: Log.Error("TVDBid '%s' is not found in posters file" % TVDBid) for line in filter(None, entry.strip().splitlines()): season, url = line.strip().split("|", 1) season = season.lstrip("0") if season.lstrip("0") else "0" SaveDict( Dict(season_posters, season, default=0) + 1, season_posters, season) rank = common.poster_rank('tvdb4', 'posters', rank_adjustment=season_posters[season] - 1) SaveDict(("TheTVDB/seasons/%s-%s-%s" % (TVDBid, season, os.path.basename(url)), rank, None), TVDB4_dict, 'seasons', season, 'posters', url) Log.Info("[ ] season: {:>2}, rank: {:>3}, filename: {}".format( season, rank, url)) Log.Info("--- return ---".ljust(157, '-')) Log.Info("absolute_map: {}".format( DictString(Dict(mappingList, 'absolute_map', default={}), 0))) Log.Info("TVDB4_dict: {}".format(DictString(TVDB4_dict, 4))) return TVDB4_dict