def __init__(self): self.handle = int(sys.argv[1]) self.paramstring = try_decode(sys.argv[2][1:]) self.params = parse_paramstring(self.paramstring) self.parent_params = self.params # self.container_path = u'{}?{}'.format(sys.argv[0], self.paramstring) self.update_listing = False self.plugin_category = '' self.container_content = '' self.container_update = None self.container_refresh = False self.item_type = None self.kodi_db = None self.kodi_db_tv = {} self.library = None self.tmdb_cache_only = True self.tmdb_api = TMDb() self.trakt_watchedindicators = ADDON.getSettingBool('trakt_watchedindicators') self.trakt_api = TraktAPI() self.is_widget = True if self.params.pop('widget', '').lower() == 'true' else False self.hide_watched = ADDON.getSettingBool('widgets_hidewatched') if self.is_widget else False self.flatten_seasons = ADDON.getSettingBool('flatten_seasons') self.ftv_forced_lookup = self.params.pop('fanarttv', '').lower() self.ftv_api = FanartTV(cache_only=self.ftv_is_cache_only()) self.filter_key = self.params.pop('filter_key', None) self.filter_value = split_items(self.params.pop('filter_value', None))[0] self.exclude_key = self.params.pop('exclude_key', None) self.exclude_value = split_items(self.params.pop('exclude_value', None))[0] self.pagination = self.pagination_is_allowed() self.params = reconfigure_legacy_params(**self.params)
def __init__(self, trakt_type, unique_id, season=None, episode=None, id_type=None): self.trakt_type = trakt_type self.unique_id = unique_id self.season = try_int(season) if season is not None else None self.episode = try_int(episode) if episode is not None else None self.id_type = id_type self.trakt_api = TraktAPI()
class Script(object): def __init__(self): self.params = {} for arg in [try_decode(arg) for arg in sys.argv[1:]]: if '=' in arg: key, value = arg.split('=', 1) self.params[key] = value.strip('\'').strip( '"') if value else True else: self.params[arg] = True self.params = reconfigure_legacy_params(**self.params) routing_table = { 'authenticate_trakt': lambda **kwargs: TraktAPI(force=True), 'revoke_trakt': lambda **kwargs: TraktAPI().logout(), 'split_value': lambda **kwargs: split_value(**kwargs), 'kodi_setting': lambda **kwargs: kodi_setting(**kwargs), 'sync_trakt': lambda **kwargs: sync_trakt(**kwargs), 'manage_artwork': lambda **kwargs: manage_artwork(**kwargs), 'refresh_details': lambda **kwargs: refresh_details(**kwargs), 'related_lists': lambda **kwargs: related_lists(**kwargs), 'user_list': lambda **kwargs: user_list(**kwargs), 'like_list': lambda **kwargs: like_list(**kwargs), 'delete_list': lambda **kwargs: delete_list(**kwargs), 'rename_list': lambda **kwargs: rename_list(**kwargs), 'blur_image': lambda **kwargs: blur_image(**kwargs), 'image_colors': lambda **kwargs: image_colors(**kwargs), 'monitor_userlist': lambda **kwargs: monitor_userlist(), 'update_players': lambda **kwargs: update_players(), 'set_defaultplayer': lambda **kwargs: set_defaultplayer(**kwargs), 'configure_players': lambda **kwargs: configure_players(**kwargs), 'library_autoupdate': lambda **kwargs: library_update(**kwargs), # 'play_season': lambda **kwargs: play_season(**kwargs), 'play_media': lambda **kwargs: play_media(**kwargs), 'run_plugin': lambda **kwargs: run_plugin(**kwargs), 'log_request': lambda **kwargs: log_request(**kwargs), 'play': lambda **kwargs: play_external(**kwargs), 'add_path': lambda **kwargs: WindowManager(**kwargs).router(), 'add_query': lambda **kwargs: WindowManager(**kwargs).router(), 'close_dialog': lambda **kwargs: WindowManager(**kwargs).router(), 'reset_path': lambda **kwargs: WindowManager(**kwargs).router(), 'call_id': lambda **kwargs: WindowManager(**kwargs).router(), 'call_path': lambda **kwargs: WindowManager(**kwargs).router(), 'call_update': lambda **kwargs: WindowManager(**kwargs).router() } def router(self): if not self.params: return if self.params.get('restart_service'): # Only do the import here because this function only for debugging purposes from resources.lib.monitor.service import restart_service_monitor return restart_service_monitor() routes_available = set(self.routing_table.keys()) params_given = set(self.params.keys()) route_taken = set.intersection(routes_available, params_given).pop() kodi_log(['lib.script.router.Script - route_taken\t', route_taken], 0) return self.routing_table[route_taken](**self.params)
def __init__(self): self.properties = set() self.index_properties = set() self.trakt_api = TraktAPI() self.tmdb_api = TMDb() self.fanarttv = FanartTV() self.omdb_api = OMDb() if ADDON.getSettingString('omdb_apikey') else None self.imdb_top250 = {} self.property_prefix = 'ListItem'
def get_external_ids(li, season=None, episode=None): trakt_api = TraktAPI() unique_id, trakt_type = None, None if li.infolabels.get('mediatype') == 'movie': unique_id = li.unique_ids.get('tmdb') trakt_type = 'movie' elif li.infolabels.get('mediatype') == 'tvshow': unique_id = li.unique_ids.get('tmdb') trakt_type = 'show' elif li.infolabels.get('mediatype') in ['season', 'episode']: unique_id = li.unique_ids.get('tvshow.tmdb') trakt_type = 'show' if not unique_id or not trakt_type: return trakt_slug = trakt_api.get_id(id_type='tmdb', unique_id=unique_id, trakt_type=trakt_type, output_type='slug') if not trakt_slug: return details = trakt_api.get_details(trakt_type, trakt_slug, extended=None) if not details: return if li.infolabels.get('mediatype') in ['movie', 'tvshow', 'season']: return { 'unique_ids': { 'tmdb': unique_id, 'tvdb': details.get('ids', {}).get('tvdb'), 'imdb': details.get('ids', {}).get('imdb'), 'slug': details.get('ids', {}).get('slug'), 'trakt': details.get('ids', {}).get('trakt') } } episode_details = trakt_api.get_details(trakt_type, trakt_slug, season=season or li.infolabels.get('season'), episode=episode or li.infolabels.get('episode'), extended=None) if episode_details: return { 'unique_ids': { 'tvshow.tmdb': unique_id, 'tvshow.tvdb': details.get('ids', {}).get('tvdb'), 'tvshow.imdb': details.get('ids', {}).get('imdb'), 'tvshow.slug': details.get('ids', {}).get('slug'), 'tvshow.trakt': details.get('ids', {}).get('trakt'), 'tvdb': episode_details.get('ids', {}).get('tvdb'), 'tmdb': episode_details.get('ids', {}).get('tmdb'), 'imdb': episode_details.get('ids', {}).get('imdb'), 'slug': episode_details.get('ids', {}).get('slug'), 'trakt': episode_details.get('ids', {}).get('trakt') } }
def get_userlist(user_slug=None, list_slug=None, confirm=True, busy_spinner=True): with busy_dialog(is_enabled=busy_spinner): request = TraktAPI().get_response_json('users', user_slug, 'lists', list_slug, 'items') if not request: return if confirm: d_head = ADDON.getLocalizedString(32125) i_check_limits = check_overlimit(request) if i_check_limits: # List over limit so inform user that it is too large to add d_body = [ ADDON.getLocalizedString(32168).format(list_slug, user_slug), ADDON.getLocalizedString(32170).format(i_check_limits.get('show'), i_check_limits.get('movie')), '', ADDON.getLocalizedString(32164).format(LIBRARY_ADD_LIMIT_TVSHOWS, LIBRARY_ADD_LIMIT_MOVIES)] xbmcgui.Dialog().ok(d_head, '\n'.join(d_body)) return elif isinstance(confirm, bool) or len(request) > confirm: # List is within limits so ask for confirmation before adding it d_body = [ ADDON.getLocalizedString(32168).format(list_slug, user_slug), ADDON.getLocalizedString(32171).format(len(request)) if len(request) > 20 else '', '', ADDON.getLocalizedString(32126)] if not xbmcgui.Dialog().yesno(d_head, '\n'.join(d_body)): return return request
def log_request(**kwargs): with busy_dialog(): kwargs['response'] = None if not kwargs.get('url'): kwargs['url'] = xbmcgui.Dialog().input('URL') if not kwargs['url']: return if kwargs.get('log_request').lower() == 'trakt': kwargs['response'] = TraktAPI().get_response_json(kwargs['url']) else: kwargs['response'] = TMDb().get_response_json(kwargs['url']) if not kwargs['response']: xbmcgui.Dialog().ok(kwargs['log_request'].capitalize(), u'{}\nNo Response!'.format(kwargs['url'])) return filename = validify_filename(u'{}_{}.json'.format( kwargs['log_request'], kwargs['url'])) dumps_to_file(kwargs, 'log_request', filename) xbmcgui.Dialog().ok( kwargs['log_request'].capitalize(), u'[B]{}[/B]\n\n{}\n{}\n{}'.format( kwargs['url'], xbmc.translatePath('special://profile/addon_data/'), 'plugin.video.themoviedb.helper/log_request', filename)) xbmcgui.Dialog().textviewer(filename, dumps(kwargs['response'], indent=2))
def delete_list(delete_list, **kwargs): if not xbmcgui.Dialog().yesno( ADDON.getLocalizedString(32358), ADDON.getLocalizedString(32357).format(delete_list)): return TraktAPI().delete_response('users/me/lists', delete_list) container_refresh()
def monitor_userlist(): # Build list choices with busy_dialog(): user_lists = [] user_lists += TraktAPI().get_list_of_lists( 'users/me/lists', authorize=True, next_page=False) or [] user_lists += TraktAPI().get_list_of_lists( 'users/likes/lists', authorize=True, next_page=False) or [] saved_lists = _get_monitor_userlists() dialog_list = [i['label'] for i in user_lists] preselected = [ x for x, i in enumerate(user_lists) if (i.get('params', {}).get('list_slug'), i.get('params', {}).get('user_slug')) in saved_lists ] # Ask user to choose lists indices = xbmcgui.Dialog().multiselect(ADDON.getLocalizedString(32312), dialog_list, preselect=preselected) if indices is None: return # Build the new settings and check that lists aren't over limit added_lists, added_users = [], [] for x in indices: list_slug = user_lists[x].get('params', {}).get('list_slug') user_slug = user_lists[x].get('params', {}).get('user_slug') if get_userlist(user_slug, list_slug, confirm=50): added_lists.append(list_slug) added_users.append(user_slug) # Set the added lists to our settings if not added_lists or not added_users: return added_lists = ' | '.join(added_lists) added_users = ' | '.join(added_users) ADDON.setSettingString('monitor_userlist', added_lists) ADDON.setSettingString('monitor_userslug', added_users) # Update library? if xbmcgui.Dialog().yesno(xbmc.getLocalizedString(653), ADDON.getLocalizedString(32132)): library_autoupdate(list_slugs=added_lists, user_slugs=added_users, busy_spinner=True)
def rename_list(rename_list, **kwargs): name = xbmcgui.Dialog().input(ADDON.getLocalizedString(32359)) if not name: return TraktAPI().post_response('users/me/lists', rename_list, postdata={'name': name}, response_method='put') container_refresh()
def like_list(like_list, user_slug=None, delete=False, **kwargs): user_slug = user_slug or 'me' if not user_slug or not like_list: return TraktAPI().like_userlist(user_slug=user_slug, list_slug=like_list, confirmation=True, delete=delete) if not delete: return container_refresh()
def like_list(like_list, user_slug=None, delete=False, **kwargs): user_slug = user_slug or 'me' if not user_slug or not like_list: return TraktAPI().like_userlist(user_slug=user_slug, list_slug=like_list, confirmation=True, delete=delete) if not delete: return xbmc.executebuiltin('Container.Refresh') xbmc.executebuiltin( 'UpdateLibrary(video,/fake/path/to/force/refresh/on/home)')
def __init__(self, items, **kwargs): set_kwargattr(self, kwargs) self._trakt = TraktAPI() self.build_menu(items)
class Container(TMDbLists, BaseDirLists, SearchLists, UserDiscoverLists, TraktLists): def __init__(self): self.handle = int(sys.argv[1]) self.paramstring = try_decode(sys.argv[2][1:]) self.params = parse_paramstring(self.paramstring) self.parent_params = self.params # self.container_path = u'{}?{}'.format(sys.argv[0], self.paramstring) self.update_listing = False self.plugin_category = '' self.container_content = '' self.container_update = None self.container_refresh = False self.item_type = None self.kodi_db = None self.kodi_db_tv = {} self.library = None self.tmdb_cache_only = True self.tmdb_api = TMDb() self.trakt_watchedindicators = ADDON.getSettingBool('trakt_watchedindicators') self.trakt_api = TraktAPI() self.is_widget = True if self.params.pop('widget', '').lower() == 'true' else False self.hide_watched = ADDON.getSettingBool('widgets_hidewatched') if self.is_widget else False self.flatten_seasons = ADDON.getSettingBool('flatten_seasons') self.ftv_forced_lookup = self.params.pop('fanarttv', '').lower() self.ftv_api = FanartTV(cache_only=self.ftv_is_cache_only()) self.filter_key = self.params.pop('filter_key', None) self.filter_value = split_items(self.params.pop('filter_value', None))[0] self.exclude_key = self.params.pop('exclude_key', None) self.exclude_value = split_items(self.params.pop('exclude_value', None))[0] self.pagination = self.pagination_is_allowed() self.params = reconfigure_legacy_params(**self.params) def pagination_is_allowed(self): if self.params.pop('nextpage', '').lower() == 'false': return False if self.is_widget and not ADDON.getSettingBool('widgets_nextpage'): return False return True def ftv_is_cache_only(self): if self.ftv_forced_lookup == 'true': return False if self.ftv_forced_lookup == 'false': return True if self.is_widget and ADDON.getSettingBool('widget_fanarttv_lookup'): return False if not self.is_widget and ADDON.getSettingBool('fanarttv_lookup'): return False return True def _add_item(self, x, li, tmdb_cache_only): cache_only = True if tmdb_cache_only and not self.ftv_api else False li.set_details(details=self.get_tmdb_details(li, cache_only=cache_only)) # Quick because only get cached li.set_details(details=self.get_ftv_artwork(li), reverse=True) # Slow when not cache only self.items_queue[x] = li def add_items(self, items=None, pagination=True, parent_params=None, property_params=None, kodi_db=None, tmdb_cache_only=True): if not items: return self.check_is_aired = parent_params.get('info') not in NO_LABEL_FORMATTING # Build empty queue and thread pool self.items_queue, pool = [None] * len(items), [None] * len(items) # Start item build threads for x, i in enumerate(items): if not pagination and 'next_page' in i: continue if self.item_is_excluded(i): continue li = ListItem(parent_params=parent_params, **i) pool[x] = Thread(target=self._add_item, args=[x, li, tmdb_cache_only]) pool[x].start() # Wait to join threads in pool first before adding item to directory for x, i in enumerate(pool): if not i: continue i.join() li = self.items_queue[x] if not li: continue li.set_episode_label() if self.check_is_aired and li.is_unaired(): return li.set_details(details=self.get_kodi_details(li), reverse=True) # Quick because local db li.set_playcount(playcount=self.get_playcount_from_trakt(li)) # Quick because of agressive caching of Trakt object and pre-emptive dict comprehension if self.hide_watched and try_int(li.infolabels.get('playcount')) != 0: continue li.set_context_menu() # Set the context menu items li.set_uids_to_info() # Add unique ids to properties so accessible in skins li.set_params_reroute(self.ftv_forced_lookup, self.flatten_seasons) # Reroute details to proper end point li.set_params_to_info(self.plugin_category) # Set path params to properties for use in skins li.infoproperties.update(property_params or {}) xbmcplugin.addDirectoryItem( handle=self.handle, url=li.get_url(), listitem=li.get_listitem(), isFolder=li.is_folder) def set_params_to_container(self, **kwargs): params = {} for k, v in viewitems(kwargs): if not k or not v: continue try: k = u'Param.{}'.format(k) v = u'{}'.format(v) params[k] = v xbmcplugin.setProperty(self.handle, k, v) # Set params to container properties except Exception as exc: kodi_log(u'Error: {}\nUnable to set param {} to {}'.format(exc, k, v), 1) return params def finish_container(self, update_listing=False, plugin_category='', container_content=''): xbmcplugin.setPluginCategory(self.handle, plugin_category) # Container.PluginCategory xbmcplugin.setContent(self.handle, container_content) # Container.Content xbmcplugin.endOfDirectory(self.handle, updateListing=update_listing) def item_is_excluded(self, item): if self.filter_key and self.filter_value: if self.filter_key in item.get('infolabels', {}): if filtered_item(item['infolabels'], self.filter_key, self.filter_value): return True elif self.filter_key in item.get('infoproperties', {}): if filtered_item(item['infoproperties'], self.filter_key, self.filter_value): return True if self.exclude_key and self.exclude_value: if self.exclude_key in item.get('infolabels', {}): if filtered_item(item['infolabels'], self.exclude_key, self.exclude_value, True): return True elif self.exclude_key in item.get('infoproperties', {}): if filtered_item(item['infoproperties'], self.exclude_key, self.exclude_value, True): return True def get_tmdb_details(self, li, cache_only=True): if not self.tmdb_api: return return self.tmdb_api.get_details( li.get_tmdb_type(), li.unique_ids.get('tvshow.tmdb') if li.infolabels.get('mediatype') in ['season', 'episode'] else li.unique_ids.get('tmdb'), li.infolabels.get('season') if li.infolabels.get('mediatype') in ['season', 'episode'] else None, li.infolabels.get('episode') if li.infolabels.get('mediatype') == 'episode' else None, cache_only=cache_only) def get_ftv_artwork(self, li): if not self.ftv_api: return artwork = self.ftv_api.get_all_artwork(li.get_ftv_id(), li.get_ftv_type()) if not artwork: return if li.infolabels.get('mediatype') in ['season', 'episode']: artwork = {u'tvshow.{}'.format(k): v for k, v in viewitems(artwork) if v} return {'art': artwork} def get_playcount_from_trakt(self, li): if not self.trakt_watchedindicators: return if li.infolabels.get('mediatype') == 'movie': return self.trakt_api.get_movie_playcount( id_type='tmdb', unique_id=try_int(li.unique_ids.get('tmdb'))) if li.infolabels.get('mediatype') == 'episode': return self.trakt_api.get_episode_playcount( id_type='tmdb', unique_id=try_int(li.unique_ids.get('tvshow.tmdb')), season=li.infolabels.get('season'), episode=li.infolabels.get('episode')) if li.infolabels.get('mediatype') == 'tvshow': li.infolabels['episode'] = self.trakt_api.get_episodes_airedcount( id_type='tmdb', unique_id=try_int(li.unique_ids.get('tmdb'))) return self.trakt_api.get_episodes_watchcount( id_type='tmdb', unique_id=try_int(li.unique_ids.get('tmdb'))) if li.infolabels.get('mediatype') == 'season': li.infolabels['episode'] = self.trakt_api.get_episodes_airedcount( id_type='tmdb', unique_id=try_int(li.unique_ids.get('tmdb')), season=li.infolabels.get('season')) return self.trakt_api.get_episodes_watchcount( id_type='tmdb', unique_id=try_int(li.unique_ids.get('tmdb')), season=li.infolabels.get('season')) def get_kodi_database(self, tmdb_type): if ADDON.getSettingBool('local_db'): return get_kodi_library(tmdb_type) def get_kodi_parent_dbid(self, li): if not self.kodi_db: return if li.infolabels.get('mediatype') in ['movie', 'tvshow']: return self.kodi_db.get_info( info='dbid', imdb_id=li.unique_ids.get('imdb'), tmdb_id=li.unique_ids.get('tmdb'), tvdb_id=li.unique_ids.get('tvdb'), originaltitle=li.infolabels.get('originaltitle'), title=li.infolabels.get('title'), year=li.infolabels.get('year')) if li.infolabels.get('mediatype') in ['season', 'episode']: return self.kodi_db.get_info( info='dbid', imdb_id=li.unique_ids.get('tvshow.imdb'), tmdb_id=li.unique_ids.get('tvshow.tmdb'), tvdb_id=li.unique_ids.get('tvshow.tvdb'), title=li.infolabels.get('tvshowtitle')) def get_kodi_details(self, li): if not self.kodi_db: return dbid = self.get_kodi_parent_dbid(li) if not dbid: return if li.infolabels.get('mediatype') == 'movie': return get_movie_details(dbid) if li.infolabels.get('mediatype') == 'tvshow': return get_tvshow_details(dbid) if li.infolabels.get('mediatype') == 'season': return set_show(self.get_kodi_tvchild_details( tvshowid=dbid, season=li.infolabels.get('season'), is_season=True) or get_empty_item(), get_tvshow_details(dbid)) if li.infolabels.get('mediatype') == 'episode': return set_show(self.get_kodi_tvchild_details( tvshowid=dbid, season=li.infolabels.get('season'), episode=li.infolabels.get('episode')) or get_empty_item(), get_tvshow_details(dbid)) def get_kodi_tvchild_details(self, tvshowid, season=None, episode=None, is_season=False): if not tvshowid or not season or (not episode and not is_season): return library = 'season' if is_season else 'episode' self.kodi_db_tv[tvshowid] = self.kodi_db_tv.get(tvshowid) or get_kodi_library(library, tvshowid) if not self.kodi_db_tv[tvshowid].database: return dbid = self.kodi_db_tv[tvshowid].get_info('dbid', season=season, episode=episode) if not dbid: return details = get_season_details(dbid) if is_season else get_episode_details(dbid) details['infoproperties']['tvshow.dbid'] = tvshowid return details def get_container_content(self, tmdb_type, season=None, episode=None): if tmdb_type == 'tv' and season and episode: return convert_type('episode', 'container') elif tmdb_type == 'tv' and season: return convert_type('season', 'container') return convert_type(tmdb_type, 'container') def list_randomised_trakt(self, **kwargs): kwargs['info'] = RANDOMISED_TRAKT.get(kwargs.get('info'), {}).get('info') kwargs['randomise'] = True self.parent_params = kwargs return self.get_items(**kwargs) def list_randomised(self, **kwargs): params = merge_two_dicts(kwargs, RANDOMISED_LISTS.get(kwargs.get('info'), {}).get('params')) item = random_from_list(self.get_items(**params)) if not item: return self.plugin_category = item.get('label') self.parent_params = item.get('params', {}) return self.get_items(**item.get('params', {})) def get_tmdb_id(self, info, **kwargs): if info == 'collection': kwargs['tmdb_type'] = 'collection' return self.tmdb_api.get_tmdb_id(**kwargs) def _noop(self): return None def _get_items(self, func, **kwargs): return func['lambda'](getattr(self, func['getattr']), **kwargs) def get_items(self, **kwargs): info = kwargs.get('info') # Check routes that don't require ID lookups first route = ROUTE_NO_ID route.update(TRAKT_LIST_OF_LISTS) route.update(RANDOMISED_LISTS) route.update(RANDOMISED_TRAKT) # Early exit if we have a route if info in route: return self._get_items(route[info]['route'], **kwargs) # Check routes that require ID lookups second route = ROUTE_TMDB_ID route.update(TMDB_BASIC_LISTS) route.update(TRAKT_BASIC_LISTS) route.update(TRAKT_SYNC_LISTS) # Early exit to basedir if no route found if info not in route: return self.list_basedir(info) # Lookup up our TMDb ID if not kwargs.get('tmdb_id'): kwargs['tmdb_id'] = self.get_tmdb_id(**kwargs) return self._get_items(route[info]['route'], **kwargs) def get_directory(self): items = self.get_items(**self.params) if not items: return self.add_items( items, pagination=self.pagination, parent_params=self.parent_params, property_params=self.set_params_to_container(**self.params), kodi_db=self.kodi_db, tmdb_cache_only=self.tmdb_cache_only) self.finish_container( update_listing=self.update_listing, plugin_category=self.plugin_category, container_content=self.container_content) if self.container_update: xbmc.executebuiltin(try_encode(u'Container.Update({})'.format(self.container_update))) if self.container_refresh: xbmc.executebuiltin('Container.Refresh') def play_external(self, **kwargs): kodi_log(['lib.container.router - Attempting to play item\n', kwargs], 1) if not kwargs.get('tmdb_id'): kwargs['tmdb_id'] = self.tmdb_api.get_tmdb_id(**kwargs) Players(**kwargs).play(handle=self.handle if self.handle != -1 else None) def context_related(self, **kwargs): if not kwargs.get('tmdb_id'): kwargs['tmdb_id'] = self.tmdb_api.get_tmdb_id(**kwargs) kwargs['container_update'] = True related_lists(include_play=True, **kwargs) def router(self): if self.params.get('info') == 'play': return self.play_external(**self.params) if self.params.get('info') == 'related': return self.context_related(**self.params) return self.get_directory()
class CommonMonitorFunctions(object): def __init__(self): self.properties = set() self.index_properties = set() self.trakt_api = TraktAPI() self.tmdb_api = TMDb() self.fanarttv = FanartTV() self.omdb_api = OMDb() if ADDON.getSettingString( 'omdb_apikey') else None self.imdb_top250 = {} self.property_prefix = 'ListItem' @try_except_log('lib.monitor.common clear_property') def clear_property(self, key): key = u'{}.{}'.format(self.property_prefix, key) get_property(key, clear_property=True) @try_except_log('lib.monitor.common set_property') def set_property(self, key, value): key = u'{}.{}'.format(self.property_prefix, key) if value is None: get_property(key, clear_property=True) else: get_property(key, set_property=u'{0}'.format(try_decode(value))) def set_iter_properties(self, dictionary, keys): if not isinstance(dictionary, dict): dictionary = {} for k in keys: try: v = dictionary.get(k, None) if isinstance(v, list): try: v = ' / '.join(v) except Exception as exc: kodi_traceback( exc, u'\nlib.monitor.common set_iter_properties\nk: {} v: {}' .format(k, v)) self.properties.add(k) self.set_property(k, v) except Exception as exc: kodi_traceback( exc, u'\nlib.monitor.common set_iter_properties\nk: {}'.format( k)) def set_indexed_properties(self, dictionary): if not isinstance(dictionary, dict): return index_properties = set() for k, v in viewitems(dictionary): if k in self.properties or k in SETPROP_RATINGS or k in SETMAIN_ARTWORK: continue try: v = v or '' self.set_property(k, v) index_properties.add(k) except Exception as exc: kodi_traceback( exc, u'\nlib.monitor.common set_indexed_properties\nk: {} v: {}' .format(k, v)) for k in (self.index_properties - index_properties): self.clear_property(k) self.index_properties = index_properties.copy() @try_except_log('lib.monitor.common set_list_properties') def set_list_properties(self, items, key, prop): if not isinstance(items, list): return joinlist = [i[key] for i in items[:10] if i.get(key)] joinlist = ' / '.join(joinlist) self.properties.add(prop) self.set_property(prop, joinlist) @try_except_log('lib.monitor.common set_time_properties') def set_time_properties(self, duration): minutes = duration // 60 % 60 hours = duration // 60 // 60 totalmin = duration // 60 self.set_property('Duration', totalmin) self.set_property('Duration_H', hours) self.set_property('Duration_M', minutes) self.set_property('Duration_HHMM', u'{0:02d}:{1:02d}'.format(hours, minutes)) self.properties.update( ['Duration', 'Duration_H', 'Duration_M', 'Duration_HHMM']) def set_properties(self, item): self.set_iter_properties(item, SETMAIN) self.set_iter_properties(item.get('infolabels', {}), SETINFO) self.set_iter_properties(item.get('infoproperties', {}), SETPROP) self.set_time_properties(item.get('infolabels', {}).get('duration', 0)) self.set_list_properties(item.get('cast', []), 'name', 'cast') if xbmc.getCondVisibility( "!Skin.HasSetting(TMDbHelper.DisableExtendedProperties)"): self.set_indexed_properties(item.get('infoproperties', {})) @try_except_log('lib.monitor.common get_tmdb_id') def get_tmdb_id(self, tmdb_type, imdb_id=None, query=None, year=None, episode_year=None): if imdb_id and imdb_id.startswith('tt'): return self.tmdb_api.get_tmdb_id(tmdb_type=tmdb_type, imdb_id=imdb_id) return self.tmdb_api.get_tmdb_id(tmdb_type=tmdb_type, query=query, year=year, episode_year=episode_year) def get_fanarttv_artwork(self, item, tmdb_type=None, tmdb_id=None, tvdb_id=None): if not self.fanarttv or tmdb_type not in ['movie', 'tv']: return item lookup_id = None if tmdb_type == 'tv': lookup_id = tvdb_id or item.get('unique_ids', {}).get('tvshow.tvdb') or item.get( 'unique_ids', {}).get('tvdb') func = self.fanarttv.get_tv_all_artwork elif tmdb_type == 'movie': lookup_id = tmdb_id or item.get('unique_ids', {}).get('tmdb') func = self.fanarttv.get_movies_all_artwork if lookup_id: item['art'] = merge_two_dicts(item.get('art', {}), func(lookup_id)) return item def get_trakt_ratings(self, item, trakt_type, season=None, episode=None): ratings = self.trakt_api.get_ratings( trakt_type=trakt_type, imdb_id=item.get('unique_ids', {}).get('tvshow.imdb') or item.get('unique_ids', {}).get('imdb'), trakt_id=item.get('unique_ids', {}).get('tvshow.trakt') or item.get('unique_ids', {}).get('trakt'), slug_id=item.get('unique_ids', {}).get('tvshow.slug') or item.get('unique_ids', {}).get('slug'), season=season, episode=episode) if not ratings: return item item['infoproperties'] = merge_two_dicts( item.get('infoproperties', {}), ratings) return item def get_imdb_top250_rank(self, item): if not self.imdb_top250: self.imdb_top250 = self.trakt_api.get_imdb_top250(id_type='tmdb') try: item['infolabels']['top250'] = self.imdb_top250.index( try_int(item.get('unique_ids', {}).get('tmdb'))) + 1 except Exception: pass return item def get_omdb_ratings(self, item, cache_only=False): if not self.omdb_api: return item imdb_id = item.get('infolabels', {}).get('imdbnumber') if not imdb_id or not imdb_id.startswith('tt'): imdb_id = item.get('unique_ids', {}).get('imdb') if not imdb_id or not imdb_id.startswith('tt'): imdb_id = item.get('unique_ids', {}).get('tvshow.imdb') if not imdb_id: return item ratings = self.omdb_api.get_ratings_awards(imdb_id=imdb_id, cache_only=cache_only) item['infoproperties'] = merge_two_dicts( item.get('infoproperties', {}), ratings.get('infoproperties', {})) return item def clear_properties(self, ignore_keys=None): ignore_keys = ignore_keys or set() for k in self.properties - ignore_keys: self.clear_property(k) self.properties = set() for k in self.index_properties: self.clear_property(k) self.index_properties = set() def clear_property_list(self, properties): for k in properties: self.clear_property(k)
class SyncItem(): def __init__(self, trakt_type, unique_id, season=None, episode=None, id_type=None): self.trakt_type = trakt_type self.unique_id = unique_id self.season = try_int(season) if season is not None else None self.episode = try_int(episode) if episode is not None else None self.id_type = id_type self.trakt_api = TraktAPI() def _build_choices(self): choices = self._user_list_check() choices += [ j for j in (self._sync_item_check(**i) for i in _sync_item_methods()) if j ] choices += [{ 'name': ADDON.getLocalizedString(32304), 'method': 'comments' }] return choices def _user_list_check(self): if xbmc.getInfoLabel("ListItem.Property(param.owner)") == 'true': return [{ 'name': ADDON.getLocalizedString(32355), 'method': 'userlist', 'remove': True }] return [{ 'name': ADDON.getLocalizedString(32298), 'method': 'userlist' }] def _sync_item_check(self, sync_type=None, method=None, name_add=None, name_remove=None, allow_episodes=False): if self.season is not None and (not allow_episodes or not self.episode): return if self.trakt_api.is_sync(self.trakt_type, self.unique_id, self.season, self.episode, self.id_type, sync_type): return {'name': name_remove, 'method': '{}/remove'.format(method)} return {'name': name_add, 'method': method} def _sync_userlist_addlist(self): name = xbmcgui.Dialog().input(ADDON.getLocalizedString(32356)) if not name: return response = self.trakt_api.post_response('users/me/lists', postdata={'name': name}) if not response or not response.json(): return return response.json().get('ids', {}).get('slug') def _sync_userlist_getlist(self): with busy_dialog(): list_sync = self.trakt_api.get_list_of_lists( 'users/me/lists') or [] list_sync.append({'label': ADDON.getLocalizedString(32299)}) x = xbmcgui.Dialog().contextmenu([i.get('label') for i in list_sync]) if x == -1: return if list_sync[x].get('label') == ADDON.getLocalizedString(32299): return self._sync_userlist_addlist() return list_sync[x].get('params', {}).get('list_slug') def _sync_userlist(self, remove=False, **kwargs): list_slug = xbmc.getInfoLabel( "ListItem.Property(param.list_slug)" ) if remove else self._sync_userlist_getlist() if not list_slug: return with busy_dialog(): return self.trakt_api.add_list_item(list_slug, self.trakt_type, self.unique_id, self.id_type, season=self.season, episode=self.episode, remove=remove) def _view_comments(self): trakt_type = 'show' if self.trakt_type in ['season', 'episode' ] else self.trakt_type with busy_dialog(): slug = self.trakt_api.get_id(self.unique_id, self.id_type, trakt_type, 'slug') comments = self.trakt_api.get_response_json( '{}s'.format(trakt_type), slug, 'comments', limit=50) or [] itemlist = [ i.get('comment', '').replace('\n', ' ') for i in comments ] return self._choose_comment(itemlist, comments) def _choose_comment(self, itemlist, comments): if not itemlist: xbmcgui.Dialog().ok(ADDON.getLocalizedString(32305), ADDON.getLocalizedString(32306)) return -1 x = xbmcgui.Dialog().select(ADDON.getLocalizedString(32305), itemlist) if x == -1: return -1 info = comments[x].get('comment') name = comments[x].get('user', {}).get('name') rate = comments[x].get('user_stats', {}).get('rating') info = u'{}\n\n{} {}/10'.format(info, xbmc.getLocalizedString(563), rate) if rate else u'{}'.format(info) xbmcgui.Dialog().textviewer(name, info) return self._choose_comment(itemlist, comments) def _sync_item(self, method, **kwargs): if method == 'userlist': return self._sync_userlist(**kwargs) if method == 'comments': return self._view_comments() with busy_dialog(): return self.trakt_api.sync_item(method, self.trakt_type, self.unique_id, self.id_type, self.season, self.episode) def sync(self): with busy_dialog(): choices = self._build_choices() x = xbmcgui.Dialog().contextmenu([i.get('name') for i in choices]) if x == -1: return name = choices[x].get('name') item_sync = self._sync_item(**choices[x]) if item_sync == -1: return if item_sync and item_sync.status_code in [200, 201, 204]: xbmcgui.Dialog().ok( ADDON.getLocalizedString(32295), ADDON.getLocalizedString(32297).format(name, self.trakt_type, self.id_type.upper(), self.unique_id)) xbmc.executebuiltin('Container.Refresh') xbmc.executebuiltin( 'UpdateLibrary(video,/fake/path/to/force/refresh/on/home)') return xbmcgui.Dialog().ok( ADDON.getLocalizedString(32295), ADDON.getLocalizedString(32296).format(name, self.trakt_type, self.id_type.upper(), self.unique_id))
class Container(TMDbLists, BaseDirLists, SearchLists, UserDiscoverLists, TraktLists): def __init__(self): self.handle = int(sys.argv[1]) self.paramstring = try_decode(sys.argv[2][1:]) self.params = parse_paramstring(self.paramstring) self.parent_params = self.params self.container_path = '{}?{}'.format(sys.argv[0], self.paramstring) self.update_listing = False self.plugin_category = '' self.container_content = '' self.container_update = None self.container_refresh = False self.item_type = None self.kodi_db = None self.kodi_db_tv = {} self.library = None self.tmdb_cache_only = True self.tmdb_api = TMDb() self.trakt_watchedindicators = ADDON.getSettingBool( 'trakt_watchedindicators') self.trakt_api = TraktAPI() self.is_widget = True if self.params.pop( 'widget', '').lower() == 'true' else False self.hide_watched = ADDON.getSettingBool( 'widgets_hidewatched') if self.is_widget else False self.flatten_seasons = ADDON.getSettingBool('flatten_seasons') self.ftv_forced_lookup = self.params.pop('fanarttv', '').lower() self.ftv_api = FanartTV(cache_only=self.ftv_is_cache_only()) self.filter_key = self.params.pop('filter_key', None) self.filter_value = split_items(self.params.pop('filter_value', None))[0] self.exclude_key = self.params.pop('exclude_key', None) self.exclude_value = split_items(self.params.pop( 'exclude_value', None))[0] self.pagination = self.pagination_is_allowed() self.params = reconfigure_legacy_params(**self.params) def pagination_is_allowed(self): if self.params.pop('nextpage', '').lower() == 'false': return False if self.is_widget and not ADDON.getSettingBool('widgets_nextpage'): return False return True def ftv_is_cache_only(self): if self.ftv_forced_lookup == 'true': return False if self.ftv_forced_lookup == 'false': return True if self.is_widget and ADDON.getSettingBool('widget_fanarttv_lookup'): return False if not self.is_widget and ADDON.getSettingBool('fanarttv_lookup'): return False return True def add_items(self, items=None, pagination=True, parent_params=None, kodi_db=None, tmdb_cache_only=True): if not items: return check_is_aired = parent_params.get('info') not in NO_LABEL_FORMATTING for i in items: if not pagination and 'next_page' in i: continue if self.item_is_excluded(i): continue li = ListItem(parent_params=parent_params, **i) li.set_details(details=self.get_tmdb_details( li, cache_only=tmdb_cache_only)) # Quick because only get cached li.set_episode_label() if check_is_aired and li.is_unaired(): continue li.set_details(details=self.get_ftv_artwork(li), reverse=True) # Slow when not cache only li.set_details(details=self.get_kodi_details(li), reverse=True) # Quick because local db li.set_playcount( playcount=self.get_playcount_from_trakt(li) ) # Quick because of agressive caching of Trakt object and pre-emptive dict comprehension if self.hide_watched and try_int( li.infolabels.get('playcount')) != 0: continue li.set_context_menu() # Set the context menu items li.set_uids_to_info( ) # Add unique ids to properties so accessible in skins li.set_params_reroute( self.ftv_forced_lookup, self.flatten_seasons) # Reroute details to proper end point li.set_params_to_info( self.plugin_category ) # Set path params to properties for use in skins xbmcplugin.addDirectoryItem(handle=self.handle, url=li.get_url(), listitem=li.get_listitem(), isFolder=li.is_folder) def set_params_to_container(self, **kwargs): for k, v in viewitems(kwargs): if not k or not v: continue try: xbmcplugin.setProperty( self.handle, u'Param.{}'.format(k), u'{}'.format(v)) # Set params to container properties except Exception as exc: kodi_log( u'Error: {}\nUnable to set Param.{} to {}'.format( exc, k, v), 1) def finish_container(self, update_listing=False, plugin_category='', container_content=''): xbmcplugin.setPluginCategory( self.handle, plugin_category) # Container.PluginCategory xbmcplugin.setContent(self.handle, container_content) # Container.Content xbmcplugin.endOfDirectory(self.handle, updateListing=update_listing) def item_is_excluded(self, item): if self.filter_key and self.filter_value: if self.filter_key in item.get('infolabels', {}): if filtered_item(item['infolabels'], self.filter_key, self.filter_value): return True elif self.filter_key in item.get('infoproperties', {}): if filtered_item(item['infoproperties'], self.filter_key, self.filter_value): return True if self.exclude_key and self.exclude_value: if self.exclude_key in item.get('infolabels', {}): if filtered_item(item['infolabels'], self.exclude_key, self.exclude_value, True): return True elif self.exclude_key in item.get('infoproperties', {}): if filtered_item(item['infoproperties'], self.exclude_key, self.exclude_value, True): return True def get_tmdb_details(self, li, cache_only=True): if not self.tmdb_api: return return self.tmdb_api.get_details( li.get_tmdb_type(), li.unique_ids.get('tvshow.tmdb') if li.infolabels.get('mediatype') in ['season', 'episode'] else li.unique_ids.get('tmdb'), li.infolabels.get('season') if li.infolabels.get('mediatype') in ['season', 'episode'] else None, li.infolabels.get('episode') if li.infolabels.get('mediatype') == 'episode' else None, cache_only=cache_only) def get_ftv_artwork(self, li): if not self.ftv_api: return artwork = self.ftv_api.get_all_artwork(li.get_ftv_id(), li.get_ftv_type()) if not artwork: return if li.infolabels.get('mediatype') in ['season', 'episode']: artwork = { u'tvshow.{}'.format(k): v for k, v in viewitems(artwork) if v } return {'art': artwork} def get_playcount_from_trakt(self, li): if not self.trakt_watchedindicators: return if li.infolabels.get('mediatype') == 'movie': return self.trakt_api.get_movie_playcount( id_type='tmdb', unique_id=try_int(li.unique_ids.get('tmdb'))) if li.infolabels.get('mediatype') == 'episode': return self.trakt_api.get_episode_playcount( id_type='tmdb', unique_id=try_int(li.unique_ids.get('tvshow.tmdb')), season=li.infolabels.get('season'), episode=li.infolabels.get('episode')) if li.infolabels.get('mediatype') == 'tvshow': li.infolabels['episode'] = self.trakt_api.get_episodes_airedcount( id_type='tmdb', unique_id=try_int(li.unique_ids.get('tmdb'))) return self.trakt_api.get_episodes_watchcount( id_type='tmdb', unique_id=try_int(li.unique_ids.get('tmdb'))) if li.infolabels.get('mediatype') == 'season': li.infolabels['episode'] = self.trakt_api.get_episodes_airedcount( id_type='tmdb', unique_id=try_int(li.unique_ids.get('tmdb')), season=li.infolabels.get('season')) return self.trakt_api.get_episodes_watchcount( id_type='tmdb', unique_id=try_int(li.unique_ids.get('tmdb')), season=li.infolabels.get('season')) def get_kodi_database(self, tmdb_type): if ADDON.getSettingBool('local_db'): return get_kodi_library(tmdb_type) def get_kodi_parent_dbid(self, li): if not self.kodi_db: return if li.infolabels.get('mediatype') in ['movie', 'tvshow']: return self.kodi_db.get_info( info='dbid', imdb_id=li.unique_ids.get('imdb'), tmdb_id=li.unique_ids.get('tmdb'), tvdb_id=li.unique_ids.get('tvdb'), originaltitle=li.infolabels.get('originaltitle'), title=li.infolabels.get('title'), year=li.infolabels.get('year')) if li.infolabels.get('mediatype') in ['season', 'episode']: return self.kodi_db.get_info( info='dbid', imdb_id=li.unique_ids.get('tvshow.imdb'), tmdb_id=li.unique_ids.get('tvshow.tmdb'), tvdb_id=li.unique_ids.get('tvshow.tvdb'), title=li.infolabels.get('tvshowtitle')) def get_kodi_details(self, li): if not self.kodi_db: return dbid = self.get_kodi_parent_dbid(li) if not dbid: return if li.infolabels.get('mediatype') == 'movie': return get_movie_details(dbid) if li.infolabels.get('mediatype') == 'tvshow': return get_tvshow_details(dbid) if li.infolabels.get('mediatype') == 'season': return set_show( self.get_kodi_tvchild_details( tvshowid=dbid, season=li.infolabels.get('season'), is_season=True) or get_empty_item(), get_tvshow_details(dbid)) if li.infolabels.get('mediatype') == 'episode': return set_show( self.get_kodi_tvchild_details( tvshowid=dbid, season=li.infolabels.get('season'), episode=li.infolabels.get('episode')) or get_empty_item(), get_tvshow_details(dbid)) def get_kodi_tvchild_details(self, tvshowid, season=None, episode=None, is_season=False): if not tvshowid or not season or (not episode and not is_season): return library = 'season' if is_season else 'episode' self.kodi_db_tv[tvshowid] = self.kodi_db_tv.get( tvshowid) or get_kodi_library(library, tvshowid) if not self.kodi_db_tv[tvshowid].database: return dbid = self.kodi_db_tv[tvshowid].get_info('dbid', season=season, episode=episode) if not dbid: return details = get_season_details( dbid) if is_season else get_episode_details(dbid) details['infoproperties']['tvshow.dbid'] = tvshowid return details def get_container_content(self, tmdb_type, season=None, episode=None): if tmdb_type == 'tv' and season and episode: return convert_type('episode', 'container') elif tmdb_type == 'tv' and season: return convert_type('season', 'container') return convert_type(tmdb_type, 'container') def list_randomised_trakt(self, **kwargs): kwargs['info'] = RANDOMISED_TRAKT.get(kwargs.get('info')) kwargs['randomise'] = True self.parent_params = kwargs return self.get_items(**kwargs) def list_randomised(self, **kwargs): params = merge_two_dicts(kwargs, RANDOMISED_LISTS.get(kwargs.get('info'))) item = random_from_list(self.get_items(**params)) if not item: return self.plugin_category = item.get('label') return self.get_items(**item.get('params', {})) def get_tmdb_id(self, info, **kwargs): if info == 'collection': kwargs['tmdb_type'] = 'collection' return self.tmdb_api.get_tmdb_id(**kwargs) def get_items(self, **kwargs): info = kwargs.get('info') if info == 'pass': return if info == 'dir_search': return self.list_searchdir_router(**kwargs) if info == 'search': return self.list_search(**kwargs) if info == 'user_discover': return self.list_userdiscover(**kwargs) if info == 'dir_discover': return self.list_discoverdir_router(**kwargs) if info == 'discover': return self.list_discover(**kwargs) if info == 'all_items': return self.list_all_items(**kwargs) if info == 'trakt_userlist': return self.list_userlist(**kwargs) if info in ['trakt_becauseyouwatched', 'trakt_becausemostwatched']: return self.list_becauseyouwatched(**kwargs) if info == 'trakt_inprogress': return self.list_inprogress(**kwargs) if info == 'trakt_nextepisodes': return self.list_nextepisodes(**kwargs) if info == 'trakt_calendar': return self.list_trakt_calendar(**kwargs) if info == 'library_nextaired': return self.list_trakt_calendar(library=True, **kwargs) if info in TRAKT_LIST_OF_LISTS: return self.list_lists(**kwargs) if info in RANDOMISED_LISTS: return self.list_randomised(**kwargs) if info in RANDOMISED_TRAKT: return self.list_randomised_trakt(**kwargs) if info == 'trakt_sortby': return self.list_trakt_sortby(**kwargs) if info and not kwargs.get('tmdb_id'): kwargs['tmdb_id'] = self.get_tmdb_id(**kwargs) if info == 'details': return self.list_details(**kwargs) if info == 'seasons': return self.list_seasons(**kwargs) if info == 'flatseasons': return self.list_flatseasons(**kwargs) if info == 'episodes': return self.list_episodes(**kwargs) if info == 'episode_groups': return self.list_episode_groups(**kwargs) if info == 'episode_group_seasons': return self.list_episode_group_seasons(**kwargs) if info == 'episode_group_episodes': return self.list_episode_group_episodes(**kwargs) if info == 'cast': return self.list_cast(**kwargs) if info == 'crew': return self.list_crew(**kwargs) if info == 'trakt_upnext': return self.list_upnext(**kwargs) if info in TMDB_BASIC_LISTS: return self.list_tmdb(**kwargs) if info in TRAKT_BASIC_LISTS: return self.list_trakt(**kwargs) if info in TRAKT_SYNC_LISTS: return self.list_sync(**kwargs) return self.list_basedir(info) def get_directory(self): items = self.get_items(**self.params) if not items: return self.add_items(items, pagination=self.pagination, parent_params=self.parent_params, kodi_db=self.kodi_db, tmdb_cache_only=self.tmdb_cache_only) self.finish_container(update_listing=self.update_listing, plugin_category=self.plugin_category, container_content=self.container_content) self.set_params_to_container(**self.params) if self.container_update: xbmc.executebuiltin( try_encode(u'Container.Update({})'.format( self.container_update))) if self.container_refresh: xbmc.executebuiltin('Container.Refresh') def play_external(self, **kwargs): kodi_log(['lib.container.router - Attempting to play item\n', kwargs], 1) if not kwargs.get('tmdb_id'): kwargs['tmdb_id'] = self.tmdb_api.get_tmdb_id(**kwargs) Players(**kwargs).play( handle=self.handle if self.handle != -1 else None) def context_related(self, **kwargs): if not kwargs.get('tmdb_id'): kwargs['tmdb_id'] = self.tmdb_api.get_tmdb_id(**kwargs) kwargs['container_update'] = True related_lists(include_play=True, **kwargs) def router(self): if self.params.get('info') == 'play': return self.play_external(**self.params) if self.params.get('info') == 'related': return self.context_related(**self.params) return self.get_directory()