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)
Esempio n. 2
0
 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()
Esempio n. 3
0
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)
Esempio n. 4
0
 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'
Esempio n. 5
0
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')
            }
        }
Esempio n. 6
0
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
Esempio n. 7
0
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))
Esempio n. 8
0
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()
Esempio n. 9
0
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)
Esempio n. 10
0
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()
Esempio n. 11
0
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)')
Esempio n. 13
0
 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()
Esempio n. 15
0
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()