# -*- coding: utf-8 -*-
### HTTP Anidb Metadata Agent (HAMA) By ZeroQI (Forked from Atomicstrawberry's v0.4 - AniDB, TVDB, AniDB mod agent for XBMC XML's, and Plex URL and path variable definition ###
ANIDB_TITLES = 'http://anidb.net/api/anime-titles.xml.gz' # AniDB title database file contain all ids, all languages #http://bakabt.info/anidb/animetitles.xml
ANIDB_TVDB_MAPPING = 'http://rawgit.com/ScudLee/anime-lists/master/anime-list-master.xml' # ScudLee mapping file url
ANIDB_TVDB_MAPPING_CUSTOM = 'anime-list-custom.xml' # custom local correction for ScudLee mapping file url
ANIDB_COLLECTION = 'http://rawgit.com/ScudLee/anime-lists/master/anime-movieset-list.xml' # ScudLee collection mapping file
ANIDB_HTTP_API_URL = 'http://api.anidb.net:9001/httpapi?request=anime&client=hama&clientver=1&protover=1&aid=' #
ANIDB_PIC_BASE_URL = 'http://img7.anidb.net/pics/anime/' # AniDB picture directory
ANIDB_SERIE_URL = 'http://anidb.net/perl-bin/animedb.pl?show=anime&aid=%s' # AniDB link to the anime
ANIDB_TVDB_MAPPING_FEEDBACK = 'http://github.com/ScudLee/anime-lists/issues/new?title=%s&body=%s' # ScudLee mapping file git feedback url
TVDB_HTTP_API_URL = 'http://thetvdb.com/api/A27AD9BE0DA63333/series/%s/all/%s.xml' # TVDB Serie XML for episodes sumaries for now
TVDB_BANNERS_URL = 'http://thetvdb.com/api/A27AD9BE0DA63333/series/%s/banners.xml' # TVDB Serie pictures xml: fanarts, posters, banners
TVDB_SERIE_SEARCH = 'http://thetvdb.com/api/GetSeries.php?seriesname=' #
TVDB_IMAGES_URL = 'http://thetvdb.com/banners/' # TVDB picture directory
TVDB_SERIE_URL = 'http://thetvdb.com/?tab=series&id=%s' #
TMDB_CONFIG_URL = 'http://api.tmdb.org/3/configuration?api_key=7f4a0bd0bd3315bb832e17feda70b5cd' #
TMDB_MOVIE_SEARCH = 'http://api.tmdb.org/3/search/movie?api_key=7f4a0bd0bd3315bb832e17feda70b5cd&query=%s&year=&language=en&include_adult=true'
TMDB_MOVIE_SEARCH_BY_TMDBID = 'http://api.tmdb.org/3/movie/%s?api_key=7f4a0bd0bd3315bb832e17feda70b5cd&append_to_response=releases,credits&language=en'
TMDB_SEARCH_URL_BY_IMDBID = 'http://api.tmdb.org/3/find/%s?api_key=7f4a0bd0bd3315bb832e17feda70b5cd&external_source=imdb_id' #
TMDB_IMAGES_URL = 'http://api.tmdb.org/3/movie/%s/images?api_key=7f4a0bd0bd3315bb832e17feda70b5cd' #
TMDB_SERIE_SEARCH_BY_TMDBID = 'http://api.tmdb.org/3/tv/%s?api_key=7f4a0bd0bd3315bb832e17feda70b5cd&append_to_response=releases,credits&language=en' #
TMDB_SERIE_IMAGES_URL = 'https://api.tmdb.org/3/tv/%s/images?api_key=7f4a0bd0bd3315bb832e17feda70b5cd' #
OMDB_HTTP_API_URL = "http://www.omdbapi.com/?i=" #
THEME_URL = 'http://tvthemes.plexapp.com/%s.mp3' # Plex TV Theme url
ASS_MAPPING_URL = 'http://rawgit.com/ZeroQI/Absolute-Series-Scanner/master/tvdb4.mapping.xml' #
ASS_POSTERS_URL = 'http://rawgit.com/ZeroQI/Absolute-Series-Scanner/master/tvdb4.posters.xml' #
FANART_TV_TV_URL = 'http://webservice.fanart.tv/v3/tv/{tvdbid}?api_key={api_key}' # Fanart TV URL for TV
FANART_TV_MOVIES_URL = 'http://webservice.fanart.tv/v3/movies/{tmdbid}?api_key={api_key}' # Fanart TV URL for Movies
FANART_TV_API_KEY = 'cfa9dc054d221b8d107f8411cd20b13f' # API key for Hama Dev
RESTRICTED_GENRE = {'X': ["18 restricted", "pornography"], 'TV-MA': ["tv censoring", "borderline porn"]}
MOVIE_RATING_MAP = {'TV-Y': 'G', 'TV-Y7': 'G', 'TV-G': 'G', 'TV-PG': 'PG', 'TV-14': 'PG-13', 'TV-MA': 'NC-17', 'X': 'X'}
FILTER_CHARS = "\\/:*?<>|~-; "
SPLIT_CHARS = [';', ':', '*', '?', ',', '.', '~', '-', '\\', '/' ] #Space is implied, characters forbidden by os filename limitations
WEB_LINK = "%s"
FILTER_SEARCH_WORDS = [ ### These are words which cause extra noise due to being uninteresting for doing searches on, Lowercase only #############################################################
'to', 'wa', 'ga', 'no', 'age', 'da', 'chou', 'super', 'yo', 'de', 'chan', 'hime', 'ni', 'sekai', # Jp
'a', 'of', 'an', 'the', 'motion', 'picture', 'special', 'oav', 'ova', 'tv', 'special', 'eternal', 'final', 'last', 'one', 'movie', 'me', 'princess', 'theater', # En Continued
'le', 'la', 'un', 'les', 'nos', 'vos', 'des', 'ses', # Fr
'i', 'ii', 'iii', 'iv', 'v', 'vi', 'vii', 'viii', 'ix', 'x', 'xi', 'xii', 'xiii', 'xiv', 'xv', 'xvi'] # Roman digits
import os, re, time, datetime, string, thread, threading, urllib, copy # Functions used per module: os (read), re (sub, match), time (sleep), datetim (datetime).
AniDB_title_tree, AniDB_collection_tree, AniDB_TVDB_mapping_tree = None, None, None #ValueError if in Start()
SERIE_LANGUAGE_PRIORITY = [ Prefs['SerieLanguage1' ].encode('utf-8'), Prefs['SerieLanguage2' ].encode('utf-8'), Prefs['SerieLanguage3'].encode('utf-8') ] #override default language
EPISODE_LANGUAGE_PRIORITY = [ Prefs['EpisodeLanguage1'].encode('utf-8'), Prefs['EpisodeLanguage2'].encode('utf-8') ] #override default language
error_log_locked, error_log_lock_sleep = {}, 10
import logging
hama_logger, formatter = logging.getLogger('com.plexapp.agents.hama'), logging.Formatter('%(asctime)-15s - %(name)s (%(thread)x) : %(levelname)s (%(module)s/%(funcName)s:%(lineno)d) - %(message)s')
#Log("Loggers: %s" % logging.Logger.manager.loggerDict) #Log("Logger->Handlers: 'com.plexapp.agents.hama': %s" % hama_logger.handlers)
for handler in hama_logger.handlers: handler.setFormatter(formatter)
### Pre-Defined ValidatePrefs function Values in "DefaultPrefs.json", accessible in Settings>Tab:Plex Media Server>Sidebar:Agents>Tab:Movies/TV Shows>Tab:HamaTV #######
def ValidatePrefs(): # a = sum(getattr(t, name, 0) for name in "xyz")
DefaultPrefs = ("GetTvdbFanart", "GetTvdbPosters", "GetTvdbBanners", "GetAnidbPoster", "GetTmdbFanart", "GetTmdbPoster", "GetOmdbPoster", "GetFanartTVBackground", "GetFanartTVPoster", "GetFanartTVBanner", "GetASSPosters", "localart", "adult",
"GetPlexThemes", "MinimumWeight", "SerieLanguage1", "SerieLanguage2", "SerieLanguage3", "EpisodeLanguage1", "EpisodeLanguage2")
try:
for key in DefaultPrefs: Log.Info("Prefs[%s] = %s" % (key, Prefs[key]))
if [Prefs[key] == None for key in DefaultPrefs].count(True) > 0: Log.Error("Some Pref values do not exist. Edit and save your preferences.")
except: err_str = "Value '%s' missing from 'DefaultPrefs.json', update it." % key; Log.Error("DefaultPrefs.json invalid. " + err_str); return MessageContainer ('Error', err_str)
else: ok_str = 'HAMA - Provided preference values are ok'; Log.Info( "DefaultPrefs.json is valid." + ok_str ); return MessageContainer ('Success', ok_str )
### Pre-Defined Start function #########################################################################################################################################
def Start():
msgContainer = ValidatePrefs();
if msgContainer.header == 'Error': return
Log.Info('### HTTP Anidb Metadata Agent (HAMA) Started ##############################################################################################################')
global AniDB_title_tree, AniDB_TVDB_mapping_tree, AniDB_collection_tree # only this one to make search after start faster
AniDB_title_tree = HamaCommonAgent().xmlElementFromFile(ANIDB_TITLES, os.path.splitext(os.path.basename(ANIDB_TITLES))[0] , True, CACHE_1HOUR * 24 * 2)
if not AniDB_title_tree: Log.Critical("Failed to load core file '%s'" % os.path.splitext(os.path.basename(ANIDB_TITLES))[0]); raise Exception("HAMA Fatal Error Hit") #; AniDB_title_tree = XML.ElementFromString("")
AniDB_TVDB_mapping_tree = HamaCommonAgent().xmlElementFromFile(ANIDB_TVDB_MAPPING, os.path.basename(ANIDB_TVDB_MAPPING), False, CACHE_1HOUR * 24 * 2)
if not AniDB_TVDB_mapping_tree: Log.Critical("Failed to load core file '%s'" % os.path.basename(ANIDB_TVDB_MAPPING)); raise Exception("HAMA Fatal Error Hit") #; AniDB_TVDB_mapping_tree = XML.ElementFromString("")
AniDB_collection_tree = HamaCommonAgent().xmlElementFromFile(ANIDB_COLLECTION, os.path.basename(ANIDB_COLLECTION ), False, CACHE_1HOUR * 24 * 2)
if not AniDB_collection_tree: AniDB_collection_tree = XML.ElementFromString(""); Log.Error("Failed to load core file '%s'" % os.path.basename(ANIDB_COLLECTION ))
HTTP.CacheTime = CACHE_1HOUR * 24
class HamaCommonAgent:
### Serie search ######################################################################################################################################################
def Search(self, results, media, lang, manual, movie):
Log.Info("=== Search - Begin - ================================================================================================")
orig_title = ( media.title if movie else media.show )
try: orig_title = orig_title.encode('utf-8') # NEEDS UTF-8
except Exception as e: Log.Error("UTF-8 encode issue, Exception: '%s'" % e)
if not orig_title: return
if orig_title.startswith("clear-cache"): HTTP.ClearCache() ### Clear Plex http cache manually by searching a serie named "clear-cache" ###
Log.Info("Title: '%s', name: '%s', filename: '%s', manual:'%s'" % (orig_title, media.name, media.filename, str(manual))) #if media.filename is not None: filename = String.Unquote(media.filename) #auto match only
### Check if a guid is specified "Show name [anidb-id]" ###
global SERIE_LANGUAGE_PRIORITY
match = re.search("(?P.*?) ?\[(?P