def action(action, tmdb_id=None, tmdb_type=None, season=None, episode=None, label=None, cache_refresh=False):
    _traktapi = TraktAPI()

    if action == 'history':
        func = _traktapi.sync_history
    elif action == 'collection':
        func = _traktapi.sync_collection
    elif action == 'watchlist':
        func = _traktapi.sync_watchlist
    elif action == 'add_to_userlist':
        return sync_userlist()
    elif action == 'remove_from_userlist':
        return sync_userlist(remove_item=True)
    elif action == 'library_userlist':
        return library_userlist()
    elif action == 'library':
        return library()
    elif action == 'play':
        return play()
    elif action == 'open':
        return browse()
    else:
        return

    with utils.busy_dialog():
        if tmdb_type == 'episode' and (not season or not episode):
            return
        elif tmdb_id and tmdb_type:
            dbtype = utils.type_convert(tmdb_type, 'dbtype')
            label = label or 'this {}'.format(utils.type_convert(tmdb_type, 'trakt'))
        else:
            label = sys.listitem.getLabel()
            dbtype = sys.listitem.getVideoInfoTag().getMediaType()
            tmdb_id = sys.listitem.getProperty('tmdb_id') if not dbtype == 'episode' else sys.listitem.getProperty('tvshow.tmdb_id')
            season = sys.listitem.getVideoInfoTag().getSeason() if dbtype == 'episode' else None
            episode = sys.listitem.getVideoInfoTag().getEpisode() if dbtype == 'episode' else None
        tmdb_type = 'movie' if dbtype == 'movie' else 'tv'
        trakt_ids = func(utils.type_convert(tmdb_type, 'trakt'), 'tmdb', cache_refresh=cache_refresh)
        boolean = 'remove' if int(tmdb_id) in trakt_ids else 'add'

    dialog_header = 'Trakt {0}'.format(action.capitalize())
    dialog_text = xbmcaddon.Addon().getLocalizedString(32065) if boolean == 'add' else xbmcaddon.Addon().getLocalizedString(32064)
    dialog_text = dialog_text.format(utils.try_decode_string(label), action.capitalize(), tmdb_type, tmdb_id)
    dialog_text = dialog_text + ' Season: {}  Episode: {}'.format(season, episode) if dbtype == 'episode' else dialog_text
    if not xbmcgui.Dialog().yesno(dialog_header, dialog_text):
        return

    with utils.busy_dialog():
        trakt_type = 'episode' if dbtype == 'episode' else utils.type_convert(tmdb_type, 'trakt')
        slug_type = 'show' if dbtype == 'episode' else trakt_type
        slug = _traktapi.get_traktslug(slug_type, 'tmdb', tmdb_id)
        item = _traktapi.get_details(slug_type, slug, season=season, episode=episode)
        items = {trakt_type + 's': [item]}
        func(slug_type, mode=boolean, items=items)

    dialog_header = 'Trakt {0}'.format(action.capitalize())
    dialog_text = xbmcaddon.Addon().getLocalizedString(32062) if boolean == 'add' else xbmcaddon.Addon().getLocalizedString(32063)
    dialog_text = dialog_text.format(tmdb_id, action.capitalize())
    xbmcgui.Dialog().ok(dialog_header, dialog_text)
    xbmc.executebuiltin('Container.Refresh')
Exemplo n.º 2
0
def action(action):
    _traktapi = TraktAPI()

    if action == 'history':
        func = _traktapi.sync_history
    elif action == 'collection':
        func = _traktapi.sync_collection
    elif action == 'watchlist':
        func = _traktapi.sync_watchlist
    elif action == 'library':
        return library()
    else:
        return

    with utils.busy_dialog():
        label = sys.listitem.getLabel()
        dbtype = sys.listitem.getVideoInfoTag().getMediaType()
        tmdb_id = sys.listitem.getProperty('tmdb_id')
        tmdb_type = 'movie' if dbtype == 'movie' else 'tv'
        trakt_ids = func(utils.type_convert(tmdb_type, 'trakt'), 'tmdb')
        boolean = 'remove' if int(tmdb_id) in trakt_ids else 'add'

    dialog_header = 'Trakt {0}'.format(action.capitalize())
    dialog_text = xbmcaddon.Addon().getLocalizedString(32065) if boolean == 'add' else xbmcaddon.Addon().getLocalizedString(32064)
    dialog_text = dialog_text.format(label, action.capitalize(), dbtype.capitalize(), tmdb_id)
    if not xbmcgui.Dialog().yesno(dialog_header, dialog_text):
        return

    with utils.busy_dialog():
        trakt_type = utils.type_convert(tmdb_type, 'trakt')
        slug_type = 'show' if dbtype == 'episode' else trakt_type
        slug = _traktapi.get_traktslug(slug_type, 'tmdb', tmdb_id)
        season = sys.listitem.getVideoInfoTag().getSeason() if dbtype == 'episode' else None
        episode = sys.listitem.getVideoInfoTag().getEpisode() if dbtype == 'episode' else None
        item = _traktapi.get_details(slug_type, slug, season=season, episode=episode)
        items = {trakt_type + 's': [item]}
        func(slug_type, mode=boolean, items=items)

    dialog_header = 'Trakt {0}'.format(action.capitalize())
    dialog_text = xbmcaddon.Addon().getLocalizedString(32062) if boolean == 'add' else xbmcaddon.Addon().getLocalizedString(32063)
    dialog_text = dialog_text.format(tmdb_id, action.capitalize())
    xbmcgui.Dialog().ok(dialog_header, dialog_text)
    xbmc.executebuiltin('Container.Refresh')
class Player(Plugin):
    def __init__(self):
        super(Player, self).__init__()
        self.traktapi = TraktAPI()
        self.search_movie, self.search_episode, self.play_movie, self.play_episode = [], [], [], []
        self.item = defaultdict(lambda: '+')
        self.itemlist, self.actions, self.players, self.identifierlist = [], [], {}, []
        self.is_local = None
        self.dp_movies = self.addon.getSettingString('default_player_movies')
        self.dp_episodes = self.addon.getSettingString(
            'default_player_episodes')
        self.dp_movies_id = None
        self.dp_episodes_id = None
        self.fallbacks = {}

    def setup_players(self,
                      tmdbtype=None,
                      details=False,
                      clearsetting=False,
                      assertplayers=True):
        self.build_players(tmdbtype)
        if details:
            self.build_details()
        self.build_selectbox(clearsetting, assertplayers)

    def get_fallback(self, dp_file, dp_action):
        fallback = self.players.get(dp_file, {}).get('fallback',
                                                     {}).get(dp_action)
        if not fallback:  # No fallback so prompt dialog
            return xbmcgui.Dialog().select(
                self.addon.getLocalizedString(32042), self.itemlist)
        if fallback in self.identifierlist:  # Found a fallback in list so play that
            return self.identifierlist.index(fallback)
        fb_file, fb_action = fallback.split()
        return self.get_fallback(
            fb_file, fb_action
        )  # Fallback not in list so let's check fallback's fallback

    def get_playerindex(self, force_dialog=False):
        if force_dialog or (self.itemtype == 'movie' and
                            not self.dp_movies) or (self.itemtype == 'episode'
                                                    and not self.dp_episodes):
            return xbmcgui.Dialog().select(
                self.addon.getLocalizedString(32042), self.itemlist)
        for i in range(0, len(self.itemlist)):
            label = self.itemlist[i].getLabel()
            if ((label == self.dp_movies and self.itemtype == 'movie') or
                (label == self.dp_episodes and self.itemtype == 'episode')
                    or (label == u'{0} {1}'.format(
                        self.addon.getLocalizedString(32061), 'Kodi'))):
                return i  # Play local or with default player if found

        # Check for fallbacks
        if self.itemtype == 'movie' and self.dp_movies_id:
            dp_file, dp_action = self.dp_movies_id.split()
            return self.get_fallback(dp_file, dp_action)
        if self.itemtype == 'episode' and self.dp_episodes_id:
            dp_file, dp_action = self.dp_episodes_id.split()
            return self.get_fallback(dp_file, dp_action)

        return -1

    def play_external(self, force_dialog=False, playerindex=-1):
        if playerindex > -1:  # Previous iteration didn't find an item to play so remove it and retry
            xbmcgui.Dialog().notification(
                self.itemlist[playerindex].getLabel(),
                self.addon.getLocalizedString(32040))
            del self.actions[
                playerindex]  # Item not found so remove the player's action list
            del self.itemlist[
                playerindex]  # Item not found so remove the player's select dialog entry
            del self.identifierlist[
                playerindex]  # Item not found so remove the player's index

        playerindex = self.get_playerindex(force_dialog=force_dialog)

        # User cancelled dialog
        if not playerindex > -1:
            return False

        player = self.actions[playerindex]
        if not player or not player[1]:
            return False

        # External player has list of actions so let's iterate through them to find our item
        resolve_url = False
        if isinstance(player[1], list):
            actionlist = player[1]
            player = (False, actionlist[0])
            for d in actionlist[1:]:
                if player[0]:
                    break  # Playable item was found in last action so let's break and play it
                folder = KodiLibrary().get_directory(
                    string_format_map(
                        player[1],
                        self.item))  # Get the next folder from the plugin

                if d.get(
                        'dialog'
                ):  # Special option to show dialog of items to select from
                    d_items = []
                    for f in folder:  # Create our list of items
                        if not f.get('label') or f.get('label') == 'None':
                            continue
                        lb_list = []
                        label_a = f.get('label')
                        if f.get('year') and f.get('year') != 1601:
                            label_a = u'{} ({})'.format(label_a, f.get('year'))
                        if utils.try_parse_int(f.get(
                                'season', 0)) > 0 and utils.try_parse_int(
                                    f.get('episode', 0)) > 0:
                            label_a = u'{}x{}. {}'.format(
                                f.get('season'), f.get('episode'), label_a)
                        if f.get('streamdetails'):
                            sdv_list = f.get('streamdetails', {}).get(
                                'video', [{}]) or [{}]
                            sda_list = f.get('streamdetails', {}).get(
                                'audio', [{}]) or [{}]
                            sdv, sda = sdv_list[0], sda_list[0]
                            if sdv.get('width') or sdv.get('height'):
                                lb_list.append(u'{}x{}'.format(
                                    sdv.get('width'), sdv.get('height')))
                            if sdv.get('codec'):
                                lb_list.append(u'{}'.format(
                                    sdv.get('codec', '').upper()))
                            if sda.get('codec'):
                                lb_list.append(u'{}'.format(
                                    sda.get('codec', '').upper()))
                            if sda.get('channels'):
                                lb_list.append(u'{} CH'.format(
                                    sda.get('channels', '')))
                            for i in sda_list:
                                if i.get('language'):
                                    lb_list.append(u'{}'.format(
                                        i.get('language', '').upper()))
                            if sdv.get('duration'):
                                lb_list.append(u'{} mins'.format(
                                    utils.try_parse_int(sdv.get('duration', 0))
                                    // 60))
                        if f.get('size'):
                            lb_list.append(u'{}'.format(
                                utils.normalise_filesize(f.get('size', 0))))
                        label_b = ' | '.join(lb_list) if lb_list else ''
                        d_items.append(
                            ListItem(label=label_a,
                                     label2=label_b,
                                     icon=f.get('thumbnail')).set_listitem())
                    if d_items:
                        idx = 0
                        if d.get('dialog',
                                 '').lower() != 'auto' or len(d_items) != 1:
                            idx = xbmcgui.Dialog().select('Select Item',
                                                          d_items,
                                                          useDetails=True)
                        if idx == -1:  # User exited the dialog so return and do nothing
                            return
                        resolve_url = True if folder[idx].get(
                            'filetype'
                        ) == 'file' else False  # Set true for files so we can play
                        player = (resolve_url, folder[idx].get('file')
                                  )  # Set the folder path to open/play
                        break  # Move onto next action
                    else:  # Ask user to select a different player if no items in dialog
                        return self.play_external(force_dialog=force_dialog,
                                                  playerindex=playerindex)

                x = 0
                for f in folder:  # Iterate through plugin folder looking for a matching item
                    x += 1  # Keep an index for position matching
                    for k, v in d.items(
                    ):  # Iterate through our key (infolabel) / value (infolabel must match) pairs of our action
                        if k == 'position':  # We're looking for an item position not an infolabel
                            if utils.try_parse_int(
                                    string_format_map(v, self.item)
                            ) != x:  # Format our position value
                                break  # Not the item position we want so let's go to next item in folder
                        elif not f.get(k) or string_format_map(
                                v, self.item
                        ) not in u'{}'.format(
                                f.get(k, '')
                        ):  # Format our value and check if it matches the infolabel key
                            break  # Item's key value doesn't match value we are looking for so let's got to next item in folder
                    else:  # Item matched our criteria so let's open it up
                        resolve_url = True if f.get(
                            'filetype'
                        ) == 'file' else False  # Set true for files so we can play
                        player = (resolve_url, f.get('file')
                                  )  # Get ListItem.FolderPath for item
                        break  # Move onto next action (either open next folder or play file)
                else:
                    return self.play_external(
                        force_dialog=force_dialog, playerindex=playerindex
                    )  # Ask user to select a different player

        # Play/Search found item
        if player and player[1]:
            action = string_format_map(player[1], self.item)
            if player[0] and action.endswith(
                    '.strm'):  # Action is play and is a strm so PlayMedia
                xbmc.executebuiltin(
                    utils.try_decode_string(u'PlayMedia({0})'.format(action)))
            elif player[
                    0]:  # Action is play and not a strm so play with player
                xbmc.Player().play(
                    action,
                    ListItem(library='video', **self.details).set_listitem())
            else:
                action = u'Container.Update({0})'.format(
                    action) if xbmc.getCondVisibility(
                        "Window.IsMedia"
                    ) else u'ActivateWindow(videos,{0},return)'.format(action)
                xbmc.executebuiltin(utils.try_decode_string(action))
            return action

    def play(self,
             itemtype,
             tmdb_id,
             season=None,
             episode=None,
             force_dialog=False):
        """ Entry point for player method """
        if not tmdb_id or not itemtype:
            return

        # Get the details for the item
        self.itemtype, self.tmdb_id, self.season, self.episode = itemtype, tmdb_id, season, episode
        self.tmdbtype = 'tv' if self.itemtype in ['episode', 'tv'] else 'movie'
        self.details = self.tmdb.get_detailed_item(self.tmdbtype,
                                                   tmdb_id,
                                                   season=season,
                                                   episode=episode)
        self.item['imdb_id'] = self.details.get('infolabels',
                                                {}).get('imdbnumber')
        self.item['originaltitle'] = self.details.get('infolabels',
                                                      {}).get('originaltitle')
        self.item['title'] = self.details.get(
            'infolabels', {}).get('tvshowtitle') or self.details.get(
                'infolabels', {}).get('title')
        self.item['year'] = self.details.get('infolabels', {}).get('year')

        # Check if we have a local file
        # TODO: Add option to auto play local
        if self.details and self.itemtype == 'movie':
            self.is_local = self.localmovie()
        if self.details and self.itemtype == 'episode':
            self.is_local = self.localepisode()

        self.setup_players(details=True)

        if not self.itemlist:
            return False

        return self.play_external(force_dialog=force_dialog)

    def build_details(self):
        self.item['id'] = self.tmdb_id
        self.item['tmdb'] = self.tmdb_id
        self.item['imdb'] = self.details.get('infolabels',
                                             {}).get('imdbnumber')
        self.item['name'] = u'{0} ({1})'.format(self.item.get('title'),
                                                self.item.get('year'))
        self.item['firstaired'] = self.details.get('infolabels',
                                                   {}).get('premiered')
        self.item['premiered'] = self.details.get('infolabels',
                                                  {}).get('premiered')
        self.item['released'] = self.details.get('infolabels',
                                                 {}).get('premiered')
        self.item['showname'] = self.item.get('title')
        self.item['clearname'] = self.item.get('title')
        self.item['tvshowtitle'] = self.item.get('title')
        self.item['title'] = self.item.get('title')
        self.item['thumbnail'] = self.details.get('thumb')
        self.item['poster'] = self.details.get('poster')
        self.item['fanart'] = self.details.get('fanart')
        self.item['now'] = datetime.datetime.now().strftime('%Y%m%d%H%M%S%f')

        if self.traktapi:
            slug_type = utils.type_convert(self.tmdbtype, 'trakt')
            trakt_details = self.traktapi.get_details(
                slug_type,
                self.traktapi.get_traktslug(slug_type, 'tmdb', self.tmdb_id))
            self.item['trakt'] = trakt_details.get('ids', {}).get('trakt')
            self.item['imdb'] = trakt_details.get('ids', {}).get('imdb')
            self.item['tvdb'] = trakt_details.get('ids', {}).get('tvdb')
            self.item['slug'] = trakt_details.get('ids', {}).get('slug')

        if self.itemtype == 'episode':  # Do some special episode stuff
            self.item['id'] = self.item.get('tvdb')
            self.item['title'] = self.details.get('infolabels', {}).get(
                'title')  # Set Episode Title
            self.item['name'] = u'{0} S{1:02d}E{2:02d}'.format(
                self.item.get('showname'),
                int(utils.try_parse_int(self.season)),
                int(utils.try_parse_int(self.episode)))
            self.item['season'] = self.season
            self.item['episode'] = self.episode

        if self.traktapi and self.itemtype == 'episode':
            trakt_details = self.traktapi.get_details(slug_type,
                                                      self.item.get('slug'),
                                                      season=self.season,
                                                      episode=self.episode)
            self.item['epid'] = trakt_details.get('ids', {}).get('tvdb')
            self.item['epimdb'] = trakt_details.get('ids', {}).get('imdb')
            self.item['eptmdb'] = trakt_details.get('ids', {}).get('tmdb')
            self.item['eptrakt'] = trakt_details.get('ids', {}).get('trakt')

        for k, v in self.item.copy().items():
            v = u'{0}'.format(v)
            self.item[k] = v.replace(',', '')
            self.item[k + '_+'] = v.replace(' ', '+')
            self.item[k + '_-'] = v.replace(' ', '-')
            self.item[k + '_escaped'] = v.replace(' ', '%2520')
            self.item[k + '_escaped+'] = v.replace(' ', '%252B')
            self.item[k + '_url'] = quote_plus(utils.try_encode_string(v))

    def build_players(self, tmdbtype=None):
        basedirs = [
            'special://profile/addon_data/plugin.video.themoviedb.helper/players/'
        ]
        if self.addon.getSettingBool('bundled_players'):
            basedirs.append(
                'special://home/addons/plugin.video.themoviedb.helper/resources/players/'
            )
        for basedir in basedirs:
            files = [
                x for x in xbmcvfs.listdir(basedir)[1] if x.endswith('.json')
            ]
            for file in files:
                vfs_file = xbmcvfs.File(basedir + file)
                try:
                    content = vfs_file.read()
                    meta = loads(content) or {}
                finally:
                    vfs_file.close()

                self.players[file] = meta
                if not meta.get('plugin') or not xbmc.getCondVisibility(
                        u'System.HasAddon({0})'.format(meta.get('plugin'))):
                    continue  # Don't have plugin so skip

                tmdbtype = tmdbtype or self.tmdbtype
                priority = utils.try_parse_int(meta.get('priority')) or 1000
                if tmdbtype == 'movie' and meta.get('search_movie'):
                    self.search_movie.append((file, priority))
                if tmdbtype == 'movie' and meta.get('play_movie'):
                    self.play_movie.append((file, priority))
                if tmdbtype == 'tv' and meta.get('search_episode'):
                    self.search_episode.append((file, priority))
                if tmdbtype == 'tv' and meta.get('play_episode'):
                    self.play_episode.append((file, priority))

    def build_playeraction(self, playerfile, action, assertplayers=True):
        player = self.players.get(playerfile, {})
        isplay = True if action.startswith('play_') else False
        prefix = self.addon.getLocalizedString(32061) if action.startswith(
            'play_') else xbmc.getLocalizedString(137)
        label = u'{0} {1}'.format(prefix, player.get('name', ''))

        # Check if matches default player and set default player id
        if label == self.dp_movies:
            self.dp_movies_id = '{} {}'.format(playerfile, action)
        if label == self.dp_episodes:
            self.dp_episodes_id = '{} {}'.format(playerfile, action)

        # Check that asserted values exist
        if assertplayers:
            for i in player.get('assert', {}).get(action, []):
                if i.startswith('!'):
                    if self.item.get(i[1:]) and self.item.get(i[1:]) != 'None':
                        return  # inverted assert - has value but we don't want it so don't build that player
                else:
                    if not self.item.get(i) or self.item.get(i) == 'None':
                        return  # missing / empty asserted value so don't build that player

        # Add player action to list for dialog
        self.append_playeraction(label=label,
                                 action=player.get(action, ''),
                                 isplay=isplay,
                                 identifier='{} {}'.format(playerfile, action))

    def append_playeraction(self, label, action, isplay=True, identifier=''):
        self.itemlist.append(xbmcgui.ListItem(label))
        self.actions.append((isplay, action))
        self.identifierlist.append(identifier)

    def build_selectbox(self, clearsetting=False, assertplayers=True):
        self.itemlist, self.actions = [], []
        if clearsetting:
            self.itemlist.append(
                xbmcgui.ListItem(
                    xbmc.getLocalizedString(13403)))  # Clear Default
        if self.is_local:
            self.append_playeraction(u'{0} {1}'.format(
                self.addon.getLocalizedString(32061), 'Kodi'),
                                     self.is_local,
                                     identifier='play_kodi')
        for i in sorted(self.play_movie, key=lambda x: x[1]):
            self.build_playeraction(i[0],
                                    'play_movie',
                                    assertplayers=assertplayers)
        for i in sorted(self.search_movie, key=lambda x: x[1]):
            self.build_playeraction(i[0],
                                    'search_movie',
                                    assertplayers=assertplayers)
        for i in sorted(self.play_episode, key=lambda x: x[1]):
            self.build_playeraction(i[0],
                                    'play_episode',
                                    assertplayers=assertplayers)
        for i in sorted(self.search_episode, key=lambda x: x[1]):
            self.build_playeraction(i[0],
                                    'search_episode',
                                    assertplayers=assertplayers)

    def localfile(self, file):
        if not file:
            return
        if file.endswith('.strm'):
            f = xbmcvfs.File(file)
            contents = f.read()
            f.close()
            if contents.startswith('plugin://plugin.video.themoviedb.helper'):
                return
        return file

    def localmovie(self):
        fuzzy_match = self.addon.getSettingBool('fuzzymatch_movie')
        return self.localfile(
            KodiLibrary(dbtype='movie').get_info('file',
                                                 fuzzy_match=fuzzy_match,
                                                 **self.item))

    def localepisode(self):
        fuzzy_match = self.addon.getSettingBool('fuzzymatch_tv')
        fuzzy_match = True  # TODO: Get tvshow year to match against but for now force fuzzy match
        dbid = KodiLibrary(dbtype='tvshow').get_info('dbid',
                                                     fuzzy_match=fuzzy_match,
                                                     **self.item)
        return self.localfile(
            KodiLibrary(dbtype='episode',
                        tvshowid=dbid).get_info('file',
                                                season=self.season,
                                                episode=self.episode))
def action(action, tmdb_id=None, tmdb_type=None, season=None, episode=None, label=None):
    _traktapi = TraktAPI()

    if action == 'history':
        func = _traktapi.sync_history
    elif action == 'collection':
        func = _traktapi.sync_collection
    elif action == 'watchlist':
        func = _traktapi.sync_watchlist
    elif action == 'add_to_userlist':
        return sync_userlist()
    elif action == 'remove_from_userlist':
        return sync_userlist(remove_item=True)
    elif action == 'library_userlist':
        return library_userlist()
    elif action == 'library':
        return library()
    elif action == 'refresh_item':
        return refresh_item()
    elif action == 'play':
        return play()
    elif action == 'open':
        return browse()
    else:
        return

    with utils.busy_dialog():
        if tmdb_id and tmdb_type:  # Passed details via script
            dbtype = utils.type_convert(tmdb_type, 'dbtype')
            label = label or 'this {}'.format(utils.type_convert(tmdb_type, 'trakt'))
            parent_tmdb_id = tmdb_id
        else:  # Context menu so retrieve details from listitem
            label = sys.listitem.getLabel()
            dbtype = sys.listitem.getVideoInfoTag().getMediaType()
            tmdb_id = sys.listitem.getProperty('tmdb_id')
            parent_tmdb_id = sys.listitem.getProperty('tvshow.tmdb_id') if dbtype == 'episode' else tmdb_id
            season = sys.listitem.getVideoInfoTag().getSeason() if dbtype == 'episode' else None
            episode = sys.listitem.getVideoInfoTag().getEpisode() if dbtype == 'episode' else None

        if tmdb_type == 'episode':  # Passed episode details via script
            if not season or not episode:  # Need season and episode for episodes
                return  # Need season and episode if run from script so leave
            # Retrieve episode details so that we can get tmdb_id for episode
            episode_details = _plugin.tmdb.get_detailed_item(tmdb_type, parent_tmdb_id, season=season, episode=episode)
            tmdb_id = episode_details.get('infoproperties', {}).get('imdb_id')

        if dbtype == 'movie':
            tmdb_type = 'movie'
        elif dbtype == 'tvshow':
            tmdb_type = 'tv'
        elif dbtype == 'episode':
            tmdb_type = 'episode'
        else:
            return

        # Check if we're adding or removing the item and confirm with the user that they want to do that
        trakt_ids = func(utils.type_convert(tmdb_type, 'trakt'), 'tmdb', cache_refresh=True)
        boolean = 'remove' if int(tmdb_id) in trakt_ids else 'add'
        dialog_header = 'Trakt {0}'.format(action.capitalize())
        dialog_text = xbmcaddon.Addon().getLocalizedString(32065) if boolean == 'add' else xbmcaddon.Addon().getLocalizedString(32064)
        dialog_text = dialog_text.format(utils.try_decode_string(label), action.capitalize(), tmdb_type, tmdb_id)
        dialog_text = dialog_text + ' Season: {}  Episode: {}'.format(season, episode) if dbtype == 'episode' else dialog_text
        if not xbmcgui.Dialog().yesno(dialog_header, dialog_text):
            return

        with utils.busy_dialog():
            slug_type = 'show' if tmdb_type == 'episode' else utils.type_convert(tmdb_type, 'trakt')
            trakt_type = utils.type_convert(tmdb_type, 'trakt')
            slug = _traktapi.get_traktslug(slug_type, 'tmdb', parent_tmdb_id)
            item = _traktapi.get_details(slug_type, slug, season=season, episode=episode)
            items = {trakt_type + 's': [item]}
            func(slug_type, mode=boolean, items=items)

        dialog_header = 'Trakt {0}'.format(action.capitalize())
        dialog_text = xbmcaddon.Addon().getLocalizedString(32062) if boolean == 'add' else xbmcaddon.Addon().getLocalizedString(32063)
        dialog_text = dialog_text.format(tmdb_id, action.capitalize())
        xbmcgui.Dialog().ok(dialog_header, dialog_text)
        xbmc.executebuiltin('Container.Refresh')
Exemplo n.º 5
0
class Player(Plugin):
    def __init__(self):
        super(Player, self).__init__()
        self.traktapi = TraktAPI()
        self.search_movie, self.search_episode, self.play_movie, self.play_episode = [], [], [], []
        self.item = defaultdict(lambda: '+')
        self.itemlist = []
        self.actions = []
        self.players = {}

    def setup_players(self, tmdbtype=None, details=False, clearsetting=False):
        self.build_players(tmdbtype)
        if details:
            self.build_details()
        self.build_selectbox(clearsetting)

    def get_itemindex(self, force_dialog=False):
        default_player_movies = self.addon.getSettingString(
            'default_player_movies')
        default_player_episodes = self.addon.getSettingString(
            'default_player_episodes')
        if force_dialog or (self.itemtype == 'movie'
                            and not default_player_movies) or (
                                self.itemtype == 'episode'
                                and not default_player_episodes):
            return xbmcgui.Dialog().select(
                self.addon.getLocalizedString(32042), self.itemlist)
        itemindex = -1
        with utils.busy_dialog():
            for index in range(0, len(self.itemlist)):
                label = self.itemlist[index].getLabel()
                if (label == default_player_movies and self.itemtype
                        == 'movie') or (label == default_player_episodes
                                        and self.itemtype == 'episode'):
                    return index
        return itemindex

    def play_external(self, force_dialog=False):
        itemindex = self.get_itemindex(force_dialog=force_dialog)

        # User cancelled dialog
        if not itemindex > -1:
            return False

        player = self.actions[itemindex]
        if not player or not player[1]:
            return False

        # External player has list of actions so let's iterate through them to find our item
        resolve_url = False
        if isinstance(player[1], list):
            actionlist = player[1]
            player = (False, actionlist[0])
            with utils.busy_dialog():
                for d in actionlist[1:]:
                    if player[0]:
                        break  # Playable item was found in last action so let's break and play it
                    folder = KodiLibrary().get_directory(
                        string_format_map(
                            player[1],
                            self.item))  # Get the next folder from the plugin
                    x = 0
                    for f in folder:  # Iterate through plugin folder looking for a matching item
                        x += 1  # Keep an index for position matching
                        for k, v in d.items(
                        ):  # Iterate through our key (infolabel) / value (infolabel must match) pairs of our action
                            if k == 'position':  # We're looking for an item position not an infolabel
                                if utils.try_parse_int(
                                        string_format_map(v, self.item)
                                ) != x:  # Format our position value
                                    break  # Not the item position we want so let's go to next item in folder
                            elif not f.get(k) or string_format_map(
                                    v, self.item
                            ) not in u'{}'.format(
                                    f.get(k, '')
                            ):  # Format our value and check if it matches the infolabel key
                                break  # Item's key value doesn't match value we are looking for so let's got to next item in folder
                        else:  # Item matched our criteria so let's open it up
                            resolve_url = True if f.get(
                                'filetype'
                            ) == 'file' else False  # Set true for files so we can play
                            player = (resolve_url, f.get('file')
                                      )  # Get ListItem.FolderPath for item
                            break  # Move onto next action (either open next folder or play file)
                    else:
                        xbmcgui.Dialog().notification(
                            self.itemlist[itemindex].getLabel(),
                            self.addon.getLocalizedString(32040))
                        del self.actions[
                            itemindex]  # Item not found so remove the player's action list
                        del self.itemlist[
                            itemindex]  # Item not found so remove the player's select dialog entry
                        return self.play_external(
                            force_dialog=True
                        )  # Ask user to select a different player

        # Play/Search found item
        if player and player[1]:
            action = string_format_map(player[1], self.item)
            if player[0]:  # Action is play so let's play the item and return
                xbmc.Player().play(
                    action,
                    ListItem(library='video', **self.details).set_listitem())
                return action
            # Action is search so let's load the plugin path
            action = u'Container.Update({0})'.format(
                action) if xbmc.getCondVisibility(
                    "Window.IsMedia"
                ) else u'ActivateWindow(videos,{0},return)'.format(action)
            xbmc.executebuiltin(utils.try_decode_string(action))
            return action

    def play(self, itemtype, tmdb_id, season=None, episode=None):
        """ Entry point for player method """
        if not tmdb_id or not itemtype:
            return

        # Get the details for the item
        self.itemtype, self.tmdb_id, self.season, self.episode = itemtype, tmdb_id, season, episode
        self.tmdbtype = 'tv' if self.itemtype in ['episode', 'tv'] else 'movie'
        self.details = self.tmdb.get_detailed_item(self.tmdbtype,
                                                   tmdb_id,
                                                   season=season,
                                                   episode=episode)
        self.item['imdb_id'] = self.details.get('infolabels',
                                                {}).get('imdbnumber')
        self.item['originaltitle'] = self.details.get('infolabels',
                                                      {}).get('originaltitle')
        self.item['title'] = self.details.get(
            'infolabels', {}).get('tvshowtitle') or self.details.get(
                'infolabels', {}).get('title')
        self.item['year'] = self.details.get('infolabels', {}).get('year')

        # Attempt to play local file first
        is_local = False
        if self.details and self.itemtype == 'movie':
            is_local = self.playmovie()
        if self.details and self.itemtype == 'episode':
            is_local = self.playepisode()
        if is_local:
            return is_local

        with utils.busy_dialog():
            self.setup_players(details=True)

        if not self.itemlist:
            return False

        return self.play_external()

    def build_details(self):
        self.item['id'] = self.tmdb_id
        self.item['tmdb'] = self.tmdb_id
        self.item['imdb'] = self.details.get('infolabels',
                                             {}).get('imdbnumber')
        self.item['name'] = u'{0} ({1})'.format(self.item.get('title'),
                                                self.item.get('year'))
        self.item['firstaired'] = self.details.get('infolabels',
                                                   {}).get('premiered')
        self.item['premiered'] = self.details.get('infolabels',
                                                  {}).get('premiered')
        self.item['released'] = self.details.get('infolabels',
                                                 {}).get('premiered')
        self.item['showname'] = self.item.get('title')
        self.item['clearname'] = self.item.get('title')
        self.item['tvshowtitle'] = self.item.get('title')
        self.item['title'] = self.item.get('title')
        self.item['thumbnail'] = self.details.get('thumb')
        self.item['poster'] = self.details.get('poster')
        self.item['fanart'] = self.details.get('fanart')
        self.item['now'] = datetime.datetime.now().strftime('%Y%m%d%H%M%S%f')

        if self.traktapi:
            slug_type = utils.type_convert(self.tmdbtype, 'trakt')
            trakt_details = self.traktapi.get_details(
                slug_type,
                self.traktapi.get_traktslug(slug_type, 'tmdb', self.tmdb_id))
            self.item['trakt'] = trakt_details.get('ids', {}).get('trakt')
            self.item['imdb'] = trakt_details.get('ids', {}).get('imdb')
            self.item['tvdb'] = trakt_details.get('ids', {}).get('tvdb')
            self.item['slug'] = trakt_details.get('ids', {}).get('slug')

        if self.itemtype == 'episode':  # Do some special episode stuff
            self.item['id'] = self.item.get('tvdb')
            self.item['title'] = self.details.get('infolabels', {}).get(
                'title')  # Set Episode Title
            self.item['name'] = u'{0} S{1:02d}E{2:02d}'.format(
                self.item.get('showname'), int(self.season), int(self.episode))
            self.item['season'] = self.season
            self.item['episode'] = self.episode

        if self.traktapi and self.itemtype == 'episode':
            trakt_details = self.traktapi.get_details(slug_type,
                                                      self.item.get('slug'),
                                                      season=self.season,
                                                      episode=self.episode)
            self.item['epid'] = trakt_details.get('ids', {}).get('tvdb')
            self.item['epimdb'] = trakt_details.get('ids', {}).get('imdb')
            self.item['eptmdb'] = trakt_details.get('ids', {}).get('tmdb')
            self.item['eptrakt'] = trakt_details.get('ids', {}).get('trakt')

        for k, v in self.item.copy().items():
            v = u'{0}'.format(v)
            self.item[k] = v.replace(',', '')
            self.item[k + '_+'] = v.replace(' ', '+')
            self.item[k + '_-'] = v.replace(' ', '-')
            self.item[k + '_escaped'] = v.replace(' ', '%2520')
            self.item[k + '_escaped+'] = v.replace(' ', '%252B')

    def build_players(self, tmdbtype=None):
        basedirs = [
            'special://profile/addon_data/plugin.video.themoviedb.helper/players/'
        ]
        if self.addon.getSettingBool('bundled_players'):
            basedirs.append(
                'special://home/addons/plugin.video.themoviedb.helper/resources/players/'
            )
        for basedir in basedirs:
            files = [
                x for x in xbmcvfs.listdir(basedir)[1] if x.endswith('.json')
            ]
            for file in files:
                vfs_file = xbmcvfs.File(basedir + file)
                try:
                    content = vfs_file.read()
                    meta = loads(content) or {}
                finally:
                    vfs_file.close()
                if not meta.get('plugin') or not xbmc.getCondVisibility(
                        u'System.HasAddon({0})'.format(meta.get('plugin'))):
                    continue  # Don't have plugin so skip

                tmdbtype = tmdbtype or self.tmdbtype
                priority = utils.try_parse_int(meta.get('priority')) or 1000
                if tmdbtype == 'movie' and meta.get('search_movie'):
                    self.search_movie.append((vfs_file, priority))
                if tmdbtype == 'movie' and meta.get('play_movie'):
                    self.play_movie.append((vfs_file, priority))
                if tmdbtype == 'tv' and meta.get('search_episode'):
                    self.search_episode.append((vfs_file, priority))
                if tmdbtype == 'tv' and meta.get('play_episode'):
                    self.play_episode.append((vfs_file, priority))
                self.players[vfs_file] = meta

    def build_selectbox(self, clearsetting=False):
        self.itemlist, self.actions = [], []
        if clearsetting:
            self.itemlist.append(
                xbmcgui.ListItem(
                    xbmc.getLocalizedString(13403)))  # Clear Default
        for i in sorted(self.play_movie, key=lambda x: x[1]):
            self.itemlist.append(
                xbmcgui.ListItem(u'{0} {1}'.format(
                    self.addon.getLocalizedString(32061),
                    self.players.get(i[0], {}).get('name', ''))))
            self.actions.append(
                (True, self.players.get(i[0], {}).get('play_movie', '')))
        for i in sorted(self.search_movie, key=lambda x: x[1]):
            self.itemlist.append(
                xbmcgui.ListItem(u'{0} {1}'.format(
                    xbmc.getLocalizedString(137),
                    self.players.get(i[0], {}).get('name', ''))))
            self.actions.append(
                (False, self.players.get(i[0], {}).get('search_movie', '')))
        for i in sorted(self.play_episode, key=lambda x: x[1]):
            self.itemlist.append(
                xbmcgui.ListItem(u'{0} {1}'.format(
                    self.addon.getLocalizedString(32061),
                    self.players.get(i[0], {}).get('name', ''))))
            self.actions.append(
                (True, self.players.get(i[0], {}).get('play_episode', '')))
        for i in sorted(self.search_episode, key=lambda x: x[1]):
            self.itemlist.append(
                xbmcgui.ListItem(u'{0} {1}'.format(
                    xbmc.getLocalizedString(137),
                    self.players.get(i[0], {}).get('name', ''))))
            self.actions.append(
                (False, self.players.get(i[0], {}).get('search_episode', '')))

    def playfile(self, file):
        if not file:
            return
        if file.endswith('.strm'):
            f = xbmcvfs.File(file)
            contents = f.read()
            f.close()
            if contents.startswith('plugin://plugin.video.themoviedb.helper'):
                return
        xbmc.executebuiltin(u'PlayMedia({0})'.format(file))
        return file

    def playmovie(self):
        fuzzy_match = self.addon.getSettingBool('fuzzymatch_movie')
        return self.playfile(
            KodiLibrary(dbtype='movie').get_info('file',
                                                 fuzzy_match=fuzzy_match,
                                                 **self.item))

    def playepisode(self):
        fuzzy_match = self.addon.getSettingBool('fuzzymatch_tv')
        dbid = KodiLibrary(dbtype='tvshow').get_info('dbid',
                                                     fuzzy_match=fuzzy_match,
                                                     **self.item)
        return self.playfile(
            KodiLibrary(dbtype='episode',
                        tvshowid=dbid).get_info('file',
                                                season=self.season,
                                                episode=self.episode))
Exemplo n.º 6
0
class Player(Plugin):
    def __init__(self):
        super(Player, self).__init__()
        self.traktapi = TraktAPI()
        self.search_movie, self.search_episode, self.play_movie, self.play_episode = [], [], [], []
        self.item = defaultdict(lambda: '+')
        self.itemlist, self.actions, self.players, self.identifierlist = [], [], {}, []
        self.is_local = None
        self.autoplay_single = self.addon.getSettingBool('autoplay_single')
        self.dp_local = self.addon.getSettingBool('default_player_local')
        self.dp_movies = self.addon.getSettingString('default_player_movies')
        self.dp_episodes = self.addon.getSettingString(
            'default_player_episodes')
        self.dp_movies_id = None
        self.dp_episodes_id = None
        self.fallbacks = {}
        self.playerstring = None

    def setup_players(self,
                      tmdbtype=None,
                      details=False,
                      clearsetting=False,
                      assertplayers=True):
        self.build_players(tmdbtype)
        if details:
            self.build_details()
        self.build_selectbox(clearsetting, assertplayers)

    def get_fallback(self, dp_file, dp_action):
        fallback = self.players.get(dp_file, {}).get('fallback',
                                                     {}).get(dp_action)
        if not fallback:  # No fallback so prompt dialog
            utils.kodi_log(
                u'Player -- {} {}\nFallback not set!'.format(
                    dp_file, dp_action), 2)
            return xbmcgui.Dialog().select(
                self.addon.getLocalizedString(32042), self.itemlist)
        if fallback in self.identifierlist:  # Found a fallback in list so play that
            utils.kodi_log(
                u'Player -- {} {}\nFallback found: {}'.format(
                    dp_file, dp_action, fallback), 2)
            return self.identifierlist.index(fallback)
        fb_file, fb_action = fallback.split()
        utils.kodi_log(
            u'Player -- {} {}\nFallback NOT found!\n{}'.format(
                dp_file, dp_action, fallback), 2)
        return self.get_fallback(
            fb_file, fb_action
        )  # Fallback not in list so let's check fallback's fallback

    def get_playerindex(self, force_dialog=False):
        if not self.itemlist:
            return -1  # No players left so cancel

        if (force_dialog
                or (self.itemtype == 'movie' and not self.dp_movies and
                    (not self.is_local or not self.dp_local))
                or (self.itemtype == 'episode' and not self.dp_episodes and
                    (not self.is_local or not self.dp_local))):
            idx = xbmcgui.Dialog().select(
                self.addon.getLocalizedString(32042),
                self.itemlist)  # Ask user to select player
            if self.itemtype == 'movie':
                self.dp_movies = self.itemlist[idx].getLabel()
                self.dp_movies_id = self.identifierlist[idx]
                utils.kodi_log(
                    u'Player -- User selected {}\n{}'.format(
                        self.dp_movies, self.dp_movies_id), 2)
            elif self.itemtype == 'episode':
                self.dp_episodes = self.itemlist[idx].getLabel()
                self.dp_episodes_id = self.identifierlist[idx]
                utils.kodi_log(
                    u'Player -- User selected {}\n{}'.format(
                        self.dp_episodes, self.dp_episodes_id), 2)
            return idx

        for i in range(0, len(self.itemlist)):
            label = self.itemlist[i].getLabel()
            if ((label == self.dp_movies and self.itemtype == 'movie') or
                (label == self.dp_episodes and self.itemtype == 'episode')
                    or (label == u'{0} {1}'.format(
                        self.addon.getLocalizedString(32061), 'Kodi')
                        and self.dp_local)):
                utils.kodi_log(
                    u'Player -- Attempting to Play with Default Player:\n {}'.
                    format(label), 2)
                return i  # Play local or with default player if found

        # Check for fallbacks
        utils.kodi_log(u'Player -- Checking for Fallbacks', 2)
        if self.itemtype == 'movie' and self.dp_movies_id:
            dp_file, dp_action = self.dp_movies_id.split()
            return self.get_fallback(dp_file, dp_action)
        if self.itemtype == 'episode' and self.dp_episodes_id:
            dp_file, dp_action = self.dp_episodes_id.split()
            return self.get_fallback(dp_file, dp_action)

        return -1

    def player_getnewindex(self, playerindex=-1, force_dialog=False):
        if playerindex > -1:  # Previous iteration didn't find an item to play so remove it and retry
            xbmcgui.Dialog().notification(
                self.itemlist[playerindex].getLabel(),
                self.addon.getLocalizedString(32040))
            del self.actions[
                playerindex]  # Item not found so remove the player's action list
            del self.itemlist[
                playerindex]  # Item not found so remove the player's select dialog entry
            del self.identifierlist[
                playerindex]  # Item not found so remove the player's index
        playerindex = 0 if len(
            self.itemlist
        ) == 1 and self.autoplay_single else self.get_playerindex(
            force_dialog=force_dialog)
        return playerindex

    def player_dialogselect(self, folder, auto=False):
        d_items = []
        for f in folder:

            # Skip items without labels as probably not worth playing
            if not f.get('label') or f.get('label') == 'None':
                continue

            # Get the label of the item
            label_a = f.get('label')

            # Add year to our label if exists and not special value of 1601
            if f.get('year') and f.get('year') != 1601:
                label_a = u'{} ({})'.format(label_a, f.get('year'))

            # Add season and episode numbers to label
            if utils.try_parse_int(f.get('season',
                                         0)) > 0 and utils.try_parse_int(
                                             f.get('episode', 0)) > 0:
                label_a = u'{}x{}. {}'.format(f.get('season'),
                                              f.get('episode'), label_a)

            # Add various stream details to ListItem.Label2 (aka label_b)
            label_b_list = []
            if f.get('streamdetails'):
                sdv_list = f.get('streamdetails', {}).get('video',
                                                          [{}]) or [{}]
                sda_list = f.get('streamdetails', {}).get('audio',
                                                          [{}]) or [{}]
                sdv, sda = sdv_list[0], sda_list[0]
                if sdv.get('width') or sdv.get('height'):
                    label_b_list.append(u'{}x{}'.format(
                        sdv.get('width'), sdv.get('height')))
                if sdv.get('codec'):
                    label_b_list.append(u'{}'.format(
                        sdv.get('codec', '').upper()))
                if sda.get('codec'):
                    label_b_list.append(u'{}'.format(
                        sda.get('codec', '').upper()))
                if sda.get('channels'):
                    label_b_list.append(u'{} CH'.format(sda.get(
                        'channels', '')))
                for i in sda_list:
                    if i.get('language'):
                        label_b_list.append(u'{}'.format(
                            i.get('language', '').upper()))
                if sdv.get('duration'):
                    label_b_list.append(u'{} mins'.format(
                        utils.try_parse_int(sdv.get('duration', 0)) // 60))
            if f.get('size'):
                label_b_list.append(u'{}'.format(
                    utils.normalise_filesize(f.get('size', 0))))
            label_b = ' | '.join(label_b_list) if label_b_list else ''

            # Add item to select dialog list
            d_items.append(
                ListItem(label=label_a,
                         label2=label_b,
                         icon=f.get('thumbnail')).set_listitem())

        if not d_items:
            return -1  # No items so ask user to select new player

        # If autoselect enabled and only 1 item choose that otherwise ask user to choose
        idx = 0 if auto and len(d_items) == 1 else xbmcgui.Dialog().select(
            'Select Item', d_items, useDetails=True)

        if idx == -1:
            return  # User exited the dialog so return nothing

        resolve_url = True if folder[idx].get(
            'filetype'
        ) == 'file' else False  # Set true for files so we can play
        return (resolve_url, folder[idx].get('file'))  # Return the player

    def player_applyrules(self, folder, action):
        for x, f in enumerate(folder):
            for k, v in action.items(
            ):  # Iterate through our key (infolabel) / value (infolabel must match) pairs of our action
                if k == 'position':  # We're looking for an item position not an infolabel
                    if utils.try_parse_int(
                            string_format_map(v, self.item)
                    ) != x + 1:  # Format our position value and add one since people are dumb and don't know that arrays start at 0
                        break  # Not the item position we want so let's go to next item in folder
                elif not f.get(k) or not re.match(
                        string_format_map(v, self.item), u'{}'.format(
                            f.get(k, ''))
                ):  # Format our value and check if it regex matches the infolabel key
                    break  # Item's key value doesn't match value we are looking for so let's got to next item in folder
            else:  # Item matched our criteria so let's return it
                utils.kodi_log('Player -- Found Match!\n{}'.format(f), 2)
                resolve_url = True if f.get(
                    'filetype'
                ) == 'file' else False  # Set true for files so we can play
                return (
                    resolve_url, f.get('file')
                )  # Get ListItem.FolderPath for item and return as player
        utils.kodi_log('Player -- Failed to find match!\n{}'.format(action), 2)
        return -1  # Got through the entire folder without a match so ask user to select new player

    def player_resolveurl(self, player=None):
        if not player or not player[1] or not isinstance(player[1], list):
            return player  # No player configured or not a list of actions so return

        keyboard_input = None
        player_actions = player[1]
        player = (False, player_actions[0]
                  )  # player tuple is: isPlayable flag; path URI to call.

        for action in player_actions[1:]:

            # If playable item was found in last action then let's break and play it
            if player[0]:
                break

            # Start thread with keyboard inputter if needed
            if action.get('keyboard'):
                if action.get('keyboard') in [
                        'Up', 'Down', 'Left', 'Right', 'Select'
                ]:
                    keyboard_input = KeyboardInputter(
                        action="Input.{}".format(action.get('keyboard')))
                else:
                    keyboard_input = KeyboardInputter(text=string_format_map(
                        action.get('keyboard', ''), self.item))
                keyboard_input.setName('keyboard_input')
                keyboard_input.start()
                continue  # Go to next action

            # Get the next folder from the plugin
            with utils.busy_dialog():
                folder = KodiLibrary().get_directory(
                    string_format_map(player[1], self.item))

            # Kill our keyboard inputter thread
            if keyboard_input:
                keyboard_input.exit = True
                keyboard_input = None

            # Special option to show dialog of items to select from
            if action.get('dialog'):
                auto = True if action.get('dialog',
                                          '').lower() == 'auto' else False
                return self.player_dialogselect(folder, auto=auto)

            utils.kodi_log(
                'Player -- Retrieved Folder\n{}'.format(
                    string_format_map(player[1], self.item)), 2)

            # Iterate through plugin folder looking for item that matches rules
            player = self.player_applyrules(folder, action) or player

            if player == -1:
                break

        return player

    def play_external(self, playerindex=-1, force_dialog=False):
        playerindex = self.player_getnewindex(playerindex,
                                              force_dialog=force_dialog)

        # User cancelled dialog
        if not playerindex > -1:
            utils.kodi_log(u'Player -- User cancelled', 2)
            return False

        # Run through player actions
        player = self.player_resolveurl(self.actions[playerindex])

        # Previous player failed so ask user to select a new one
        if player == -1:
            return self.play_external(playerindex, force_dialog=force_dialog)

        # Play/Search found item
        if player and player[1]:
            action = string_format_map(player[1], self.item)
            if player[0] and (
                    action.endswith('.strm')
                    or self.identifierlist[playerindex] == 'play_kodi'
            ):  # Action is play and is a strm/local so PlayMedia
                utils.kodi_log(
                    u'Player -- Found strm or local.\nAttempting PLAYMEDIA({})'
                    .format(action), 1)
                xbmc.executebuiltin(
                    utils.try_decode_string(
                        u'PlayMedia(\"{0}\")'.format(action)))
            elif player[
                    0]:  # Action is play and not a strm so play with player
                utils.kodi_log(
                    u'Player -- Found file.\nAttempting to PLAY: {}'.format(
                        action), 2)
                xbmcgui.Window(10000).setProperty(
                    'TMDbHelper.PlayerInfoString',
                    self.playerstring) if self.playerstring else None
                xbmc.Player().play(
                    action,
                    ListItem(library='video', **self.details).set_listitem())
            else:
                action = u'Container.Update({0})'.format(
                    action) if xbmc.getCondVisibility(
                        "Window.IsMedia"
                    ) else u'ActivateWindow(videos,{0},return)'.format(action)
                utils.kodi_log(
                    u'Player -- Found folder.\nAttempting to OPEN: {}'.format(
                        action), 2)
                xbmc.executebuiltin(
                    utils.try_encode_string(utils.try_decode_string(action)))
            return action

    def play(self,
             itemtype,
             tmdb_id,
             season=None,
             episode=None,
             force_dialog=False,
             kodi_db=False):
        """ Entry point for player method """
        if not tmdb_id or not itemtype:
            return

        xbmcgui.Window(10000).clearProperty('TMDbHelper.PlayerInfoString')

        with utils.busy_dialog():
            # Get the details for the item
            self.itemtype, self.tmdb_id, self.season, self.episode = itemtype, tmdb_id, season, episode
            self.tmdbtype = 'tv' if self.itemtype in ['episode', 'tv'
                                                      ] else 'movie'
            self.details = self.tmdb.get_detailed_item(self.tmdbtype,
                                                       tmdb_id,
                                                       season=season,
                                                       episode=episode)
            self.item['tmdb_id'] = self.tmdb_id
            self.item['imdb_id'] = self.details.get(
                'infoproperties',
                {}).get('tvshow.imdb_id') or self.details.get(
                    'infoproperties', {}).get('imdb_id')
            self.item['tvdb_id'] = self.details.get(
                'infoproperties',
                {}).get('tvshow.tvdb_id') or self.details.get(
                    'infoproperties', {}).get('tvdb_id')
            self.item['originaltitle'] = self.details.get(
                'infolabels', {}).get('originaltitle')
            self.item['title'] = self.details.get(
                'infolabels', {}).get('tvshowtitle') or self.details.get(
                    'infolabels', {}).get('title')
            self.item['year'] = self.details.get('infolabels', {}).get('year')

            # Check if we have a local file
            # TODO: Add option to auto play local
            if self.details and self.itemtype == 'movie':
                self.is_local = self.localmovie()
            if self.details and self.itemtype == 'episode':
                self.is_local = self.localepisode()

            self.setup_players(details=True)

        if not self.itemlist:
            return False

        if kodi_db:
            self.playerstring = dumps({
                'tmdbtype':
                'episode' if itemtype in ['episode', 'tv'] else 'movie',
                'season':
                season,
                'episode':
                episode,
                'tmdb_id':
                self.tmdb_id,
                'tvdb_id':
                self.item.get('tvdb_id'),
                'imdb_id':
                self.item.get('imdb_id')
            })

        return self.play_external(force_dialog=force_dialog)

    def build_details(self):
        self.item['id'] = self.tmdb_id
        self.item['tmdb'] = self.tmdb_id
        self.item['imdb'] = self.details.get('infolabels',
                                             {}).get('imdbnumber')
        self.item['name'] = u'{0} ({1})'.format(self.item.get('title'),
                                                self.item.get('year'))
        self.item['premiered'] = self.item['firstaired'] = self.item[
            'released'] = self.details.get('infolabels', {}).get('premiered')
        self.item['plot'] = self.details.get('infolabels', {}).get('plot')
        self.item['cast'] = self.item['actors'] = " / ".join([
            i.get('name') for i in self.details.get('cast', [])
            if i.get('name')
        ])
        self.item['showname'] = self.item['clearname'] = self.item[
            'tvshowtitle'] = self.item['title'] = self.item.get('title')
        self.item['thumbnail'] = self.details.get('thumb')
        self.item['poster'] = self.details.get('poster')
        self.item['fanart'] = self.details.get('fanart')
        self.item['now'] = datetime.datetime.now().strftime('%Y%m%d%H%M%S%f')

        if self.traktapi:
            slug_type = utils.type_convert(self.tmdbtype, 'trakt')
            trakt_details = self.traktapi.get_details(
                slug_type,
                self.traktapi.get_traktslug(slug_type, 'tmdb', self.tmdb_id))
            self.item['trakt'] = trakt_details.get('ids', {}).get('trakt')
            self.item['imdb'] = self.details.get(
                'infoproperties',
                {}).get('tvshow.imdb_id') or trakt_details.get('ids',
                                                               {}).get('imdb')
            self.item['tvdb'] = self.details.get(
                'infoproperties',
                {}).get('tvshow.tvdb_id') or trakt_details.get('ids',
                                                               {}).get('tvdb')
            self.item['slug'] = trakt_details.get('ids', {}).get('slug')

        if self.itemtype == 'episode':  # Do some special episode stuff
            self.item['id'] = self.item.get('tvdb')
            self.item['title'] = self.details.get('infolabels', {}).get(
                'title')  # Set Episode Title
            self.item['name'] = u'{0} S{1:02d}E{2:02d}'.format(
                self.item.get('showname'),
                int(utils.try_parse_int(self.season)),
                int(utils.try_parse_int(self.episode)))
            self.item['season'] = self.season
            self.item['episode'] = self.episode
            self.item['showpremiered'] = self.details.get(
                'infoproperties', {}).get('tvshow.premiered')
            self.item['showyear'] = self.details.get('infoproperties',
                                                     {}).get('tvshow.year')

        if self.traktapi and self.itemtype == 'episode':
            trakt_details = self.traktapi.get_details(slug_type,
                                                      self.item.get('slug'),
                                                      season=self.season,
                                                      episode=self.episode)
            self.item['epid'] = self.details.get(
                'infoproperties', {}).get('tvdb_id') or trakt_details.get(
                    'ids', {}).get('tvdb')
            self.item['epimdb'] = trakt_details.get('ids', {}).get('imdb')
            self.item['eptmdb'] = self.details.get(
                'infoproperties', {}).get('tmdb_id') or trakt_details.get(
                    'ids', {}).get('tmdb')
            self.item['eptrakt'] = trakt_details.get('ids', {}).get('trakt')

        utils.kodi_log(u'Player Details - Item:\n{}'.format(self.item), 2)

        for k, v in self.item.copy().items():
            if k not in constants.PLAYER_URLENCODE:
                continue
            v = u'{0}'.format(v)
            for key, value in {k: v, '{}_meta'.format(k): dumps(v)}.items():
                self.item[key] = value.replace(',', '')
                self.item[key + '_+'] = value.replace(',',
                                                      '').replace(' ', '+')
                self.item[key + '_-'] = value.replace(',',
                                                      '').replace(' ', '-')
                self.item[key + '_escaped'] = quote(
                    quote(utils.try_encode_string(value)))
                self.item[key + '_escaped+'] = quote(
                    quote_plus(utils.try_encode_string(value)))
                self.item[key + '_url'] = quote(utils.try_encode_string(value))
                self.item[key + '_url+'] = quote_plus(
                    utils.try_encode_string(value))

    def build_players(self, tmdbtype=None):
        basedirs = [
            'special://profile/addon_data/plugin.video.themoviedb.helper/players/'
        ]
        if self.addon.getSettingBool('bundled_players'):
            basedirs.append(
                'special://home/addons/plugin.video.themoviedb.helper/resources/players/'
            )
        for basedir in basedirs:
            files = [
                x for x in xbmcvfs.listdir(basedir)[1] if x.endswith('.json')
            ]
            for file in files:
                meta = loads(utils.read_file(basedir + file)) or {}

                self.players[file] = meta

                plugins = meta.get(
                    'plugin'
                ) or 'plugin.undefined'  # Give dummy name to undefined plugins so that they fail the check
                plugins = plugins if isinstance(plugins, list) else [
                    plugins
                ]  # Listify for simplicity of code
                for plugin in plugins:
                    if not xbmc.getCondVisibility(
                            u'System.HasAddon({0})'.format(plugin)):
                        break  # System doesn't have a required plugin so skip this player
                else:  # If the system has all the listed addons then build the player
                    tmdbtype = tmdbtype or self.tmdbtype
                    priority = utils.try_parse_int(
                        meta.get('priority')) or 1000
                    if tmdbtype == 'movie' and meta.get('search_movie'):
                        self.search_movie.append((file, priority))
                    if tmdbtype == 'movie' and meta.get('play_movie'):
                        self.play_movie.append((file, priority))
                    if tmdbtype == 'tv' and meta.get('search_episode'):
                        self.search_episode.append((file, priority))
                    if tmdbtype == 'tv' and meta.get('play_episode'):
                        self.play_episode.append((file, priority))

    def build_playeraction(self, playerfile, action, assertplayers=True):
        player = self.players.get(playerfile, {})
        isplay = True if action.startswith('play_') else False
        prefix = self.addon.getLocalizedString(32061) if action.startswith(
            'play_') else xbmc.getLocalizedString(137)
        label = u'{0} {1}'.format(prefix, player.get('name', ''))

        # Check if matches default player and set default player id
        if label == self.dp_movies:
            self.dp_movies_id = '{} {}'.format(playerfile, action)
        if label == self.dp_episodes:
            self.dp_episodes_id = '{} {}'.format(playerfile, action)

        # Check that asserted values exist
        if assertplayers:
            for i in player.get('assert', {}).get(action, []):
                if i.startswith('!'):
                    if self.item.get(i[1:]) and self.item.get(i[1:]) != 'None':
                        return  # inverted assert - has value but we don't want it so don't build that player
                else:
                    if not self.item.get(i) or self.item.get(i) == 'None':
                        return  # missing / empty asserted value so don't build that player

        # Add player action to list for dialog
        self.append_playeraction(label=label,
                                 action=player.get(action, ''),
                                 isplay=isplay,
                                 identifier='{} {}'.format(playerfile, action))

    def append_playeraction(self, label, action, isplay=True, identifier=''):
        self.itemlist.append(xbmcgui.ListItem(label))
        self.actions.append((isplay, action))
        self.identifierlist.append(identifier)

    def build_selectbox(self, clearsetting=False, assertplayers=True):
        self.itemlist, self.actions = [], []
        if clearsetting:
            self.itemlist.append(
                xbmcgui.ListItem(
                    xbmc.getLocalizedString(13403)))  # Clear Default
        if self.is_local:
            self.append_playeraction(u'{0} {1}'.format(
                self.addon.getLocalizedString(32061), 'Kodi'),
                                     self.is_local,
                                     identifier='play_kodi')
        for i in sorted(self.play_movie, key=lambda x: x[1]):
            self.build_playeraction(i[0],
                                    'play_movie',
                                    assertplayers=assertplayers)
        for i in sorted(self.search_movie, key=lambda x: x[1]):
            self.build_playeraction(i[0],
                                    'search_movie',
                                    assertplayers=assertplayers)
        for i in sorted(self.play_episode, key=lambda x: x[1]):
            self.build_playeraction(i[0],
                                    'play_episode',
                                    assertplayers=assertplayers)
        for i in sorted(self.search_episode, key=lambda x: x[1]):
            self.build_playeraction(i[0],
                                    'search_episode',
                                    assertplayers=assertplayers)
        utils.kodi_log(
            u'Player -- Built {} Players!\n{}'.format(len(self.itemlist),
                                                      self.identifierlist), 2)

    def localfile(self, file):
        if not file:
            return
        if file.endswith('.strm'):
            f = xbmcvfs.File(file)
            contents = f.read()
            f.close()
            if contents.startswith('plugin://plugin.video.themoviedb.helper'):
                return
        return file

    def localmovie(self):
        # fuzzy_match = self.addon.getSettingBool('fuzzymatch_movie')
        return self.localfile(
            KodiLibrary(dbtype='movie').get_info(
                'file',
                fuzzy_match=False,
                tmdb_id=self.item.get('tmdb_id'),
                imdb_id=self.item.get('imdb_id')))

    def localepisode(self):
        # fuzzy_match = self.addon.getSettingBool('fuzzymatch_tv')
        dbid = KodiLibrary(dbtype='tvshow').get_info(
            'dbid',
            fuzzy_match=False,
            tmdb_id=self.item.get('tmdb_id'),
            tvdb_id=self.item.get('tvdb_id'),
            imdb_id=self.item.get('imdb_id'))
        return self.localfile(
            KodiLibrary(dbtype='episode',
                        tvshowid=dbid).get_info('file',
                                                season=self.season,
                                                episode=self.episode))