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')
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')
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))
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))