def get_extracted_zip(self): if not self.download_url or not self.extract_to: return with busy_dialog(): response = self.open_url(self.download_url) if not response: xbmcgui.Dialog().ok(ADDON.getAddonInfo('name'), ADDON.getLocalizedString(32058)) return if not os.path.exists(self.extract_to): os.makedirs(self.extract_to) if xbmcgui.Dialog().yesno(ADDON.getAddonInfo('name'), self.msg_cleardir): with busy_dialog(): self.clear_dir(self.extract_to) with busy_dialog(): num_files = 0 with zipfile.ZipFile(BytesIO(response.content)) as downloaded_zip: for item in [ x for x in downloaded_zip.namelist() if x.endswith('.json') ]: filename = os.path.basename(item) if not filename: continue _file = downloaded_zip.open(item) with open(os.path.join(self.extract_to, filename), 'w') as target: target.write(_file.read()) num_files += 1 try: _tempzip = os.path.join(self.extract_to, 'temp.zip') os.remove(_tempzip) except Exception as e: kodi_log(u'Could not delete package {0}: {1}'.format( _tempzip, str(e))) if num_files: xbmcgui.Dialog().ok( ADDON.getAddonInfo('name'), u'{0}\n\n{1} {2}.'.format(ADDON.getLocalizedString(32059), num_files, ADDON.getLocalizedString(32060)))
def get_userlist(user_slug=None, list_slug=None, confirm=True, busy_spinner=True): with busy_dialog(is_enabled=busy_spinner): request = TraktAPI().get_response_json('users', user_slug, 'lists', list_slug, 'items') if not request: return if confirm: d_head = ADDON.getLocalizedString(32125) i_check_limits = check_overlimit(request) if i_check_limits: # List over limit so inform user that it is too large to add d_body = [ ADDON.getLocalizedString(32168).format(list_slug, user_slug), ADDON.getLocalizedString(32170).format(i_check_limits.get('show'), i_check_limits.get('movie')), '', ADDON.getLocalizedString(32164).format(LIBRARY_ADD_LIMIT_TVSHOWS, LIBRARY_ADD_LIMIT_MOVIES)] xbmcgui.Dialog().ok(d_head, '\n'.join(d_body)) return elif isinstance(confirm, bool) or len(request) > confirm: # List is within limits so ask for confirmation before adding it d_body = [ ADDON.getLocalizedString(32168).format(list_slug, user_slug), ADDON.getLocalizedString(32171).format(len(request)) if len(request) > 20 else '', '', ADDON.getLocalizedString(32126)] if not xbmcgui.Dialog().yesno(d_head, '\n'.join(d_body)): return return request
def _sync_userlist(self): with busy_dialog(): list_sync = self.trakt_api.get_list_of_lists('users/me/lists') or [] list_sync.append({'label': ADDON.getLocalizedString(32299)}) x = xbmcgui.Dialog().contextmenu([i.get('label') for i in list_sync]) if x == -1: return if list_sync[x].get('label') == ADDON.getLocalizedString(32299): return # TODO: CREATE NEW LIST list_slug = list_sync[x].get('params', {}).get('list_slug') if not list_slug: return with busy_dialog(): return self.trakt_api.add_list_item( list_slug, self.trakt_type, self.unique_id, self.id_type, season=self.season, episode=self.episode)
def build_menu(self, items): with busy_dialog(): self.menu = [ j for j in (i['class'](self, **i.get('kwargs', {}))._getself() for i in items) if j ] return self.menu
def log_request(**kwargs): with busy_dialog(): kwargs['response'] = None if not kwargs.get('url'): kwargs['url'] = xbmcgui.Dialog().input('URL') if not kwargs['url']: return if kwargs.get('log_request').lower() == 'trakt': kwargs['response'] = TraktAPI().get_response_json(kwargs['url']) else: kwargs['response'] = TMDb().get_response_json(kwargs['url']) if not kwargs['response']: xbmcgui.Dialog().ok(kwargs['log_request'].capitalize(), u'{}\nNo Response!'.format(kwargs['url'])) return filename = validify_filename(u'{}_{}.json'.format( kwargs['log_request'], kwargs['url'])) dumps_to_file(kwargs, 'log_request', filename) xbmcgui.Dialog().ok( kwargs['log_request'].capitalize(), u'[B]{}[/B]\n\n{}\n{}\n{}'.format( kwargs['url'], xbmc.translatePath('special://profile/addon_data/'), 'plugin.video.themoviedb.helper/log_request', filename)) xbmcgui.Dialog().textviewer(filename, dumps(kwargs['response'], indent=2))
def resolve_to_dummy(handle=None, stop_after=1, delay_wait=0): """ Kodi does 5x retries to resolve url if isPlayable property is set - strm files force this property. However, external plugins might not resolve directly to URL and instead might require PlayMedia. Also, if external plugin endpoint is a folder we need to do ActivateWindow/Container.Update instead. Passing False to setResolvedUrl doesn't work correctly and the retry is triggered anyway. In these instances we use a hack to avoid the retry by first resolving to a dummy file instead. """ # If we don't have a handle there's nothing to resolve if handle is None: return # Set our dummy resolved url path = u'{}/resources/dummy.mp4'.format(ADDONPATH) kodi_log(['lib.player.players - attempt to resolve dummy file\n', path], 1) xbmcplugin.setResolvedUrl(handle, True, ListItem(path=path).get_listitem()) # Wait till our file plays and then stop after setting duration if wait_for_player(to_start='dummy.mp4', stop_after=stop_after) <= 0: kodi_log(['lib.player.players - resolving dummy file timeout\n', path], 1) return -1 # Wait for our file to stop before continuing if wait_for_player() <= 0: kodi_log(['lib.player.players - stopping dummy file timeout\n', path], 1) return -1 # Added delay with busy_dialog(False if delay_wait < 1 else True): xbmc.Monitor().waitForAbort(delay_wait) # Success kodi_log(['lib.player.players -- successfully resolved dummy file\n', path], 1)
def _view_comments(self): trakt_type = 'show' if self.trakt_type in ['season', 'episode'] else self.trakt_type with busy_dialog(): slug = self.trakt_api.get_id(self.unique_id, self.id_type, trakt_type, 'slug') comments = self.trakt_api.get_response_json('{}s'.format(trakt_type), slug, 'comments', limit=50) or [] itemlist = [i.get('comment', '').replace('\n', ' ') for i in comments] return self._choose_comment(itemlist, comments)
def __init__(self, tmdb_type, tmdb_id=None, season=None, episode=None, ignore_default=False, **kwargs): with busy_dialog(): self.players = get_players_from_file() self.details = get_item_details(tmdb_type, tmdb_id, season, episode) self.item = get_detailed_item( tmdb_type, tmdb_id, season, episode, details=self.details) or {} self.playerstring = get_playerstring(tmdb_type, tmdb_id, season, episode, details=self.details) self.dialog_players = self._get_players_for_dialog(tmdb_type) self.default_player = ADDON.getSettingString( 'default_player_movies' ) if tmdb_type == 'movie' else ADDON.getSettingString( 'default_player_episodes') self.ignore_default = ignore_default
def sync(self): with busy_dialog(): choices = self._build_choices() x = xbmcgui.Dialog().contextmenu([i.get('name') for i in choices]) if x == -1: return name = choices[x].get('name') item_sync = self._sync_item(**choices[x]) if item_sync == -1: return if item_sync and item_sync.status_code in [200, 201, 204]: xbmcgui.Dialog().ok( ADDON.getLocalizedString(32295), ADDON.getLocalizedString(32297).format(name, self.trakt_type, self.id_type.upper(), self.unique_id)) xbmc.executebuiltin('Container.Refresh') xbmc.executebuiltin( 'UpdateLibrary(video,/fake/path/to/force/refresh/on/home)') return xbmcgui.Dialog().ok( ADDON.getLocalizedString(32295), ADDON.getLocalizedString(32296).format(name, self.trakt_type, self.id_type.upper(), self.unique_id))
def run_plugin(**kwargs): with busy_dialog(): kodi_log([ 'lib.script.router - attempting to play\n', kwargs.get('run_plugin') ], 1) xbmc.executebuiltin( try_encode(u'RunPlugin({})'.format(kwargs.get('run_plugin'))))
def play_media(**kwargs): with busy_dialog(): kodi_log([ 'lib.script.router - attempting to play\n', kwargs.get('play_media') ], 1) xbmc.executebuiltin( try_encode(u'PlayMedia({})'.format(kwargs.get('play_media'))))
def sync(self): """ Called after user selects choice """ with busy_dialog(): self._sync = self._trakt.sync_item( '{}/remove'.format(self.method) if self.remove else self.method, self._item.trakt_type, self._item.unique_id, self._item.id_type, self._item.season, self._item.episode) return self._sync
def _sync_item(self, method, **kwargs): if method == 'userlist': return self._sync_userlist(**kwargs) if method == 'comments': return self._view_comments() with busy_dialog(): return self.trakt_api.sync_item(method, self.trakt_type, self.unique_id, self.id_type, self.season, self.episode)
def add_query(self, query, tmdb_type): with busy_dialog(): query = try_decode(query) tmdb_id = TMDb().get_tmdb_id_from_query(tmdb_type, query, header=query, use_details=True, auto_single=True) if not tmdb_id: xbmcgui.Dialog().notification('TMDbHelper', ADDON.getLocalizedString(32310).format(query)) return url = 'plugin://plugin.video.themoviedb.helper/?info=details&tmdb_type={}&tmdb_id={}' url = url.format(tmdb_type, tmdb_id) return self.add_path(url)
def _sync_userlist_getlist(self): with busy_dialog(): list_sync = self.trakt_api.get_list_of_lists( 'users/me/lists') or [] list_sync.append({'label': ADDON.getLocalizedString(32299)}) x = xbmcgui.Dialog().contextmenu([i.get('label') for i in list_sync]) if x == -1: return if list_sync[x].get('label') == ADDON.getLocalizedString(32299): return self._sync_userlist_addlist() return list_sync[x].get('params', {}).get('list_slug')
def delete_player(self, filename): if not xbmcgui.Dialog().yesno( ADDON.getLocalizedString(32334), ADDON.getLocalizedString(32335).format(filename), yeslabel=xbmc.getLocalizedString(13007), nolabel=xbmc.getLocalizedString(222)): return with busy_dialog(): delete_file(PLAYERS_BASEDIR_SAVE, filename, join_addon_data=False) self.players = get_players_from_file() self.dialog_players = _get_dialog_players(self.players)
def refresh_all_artwork(self, ftv_id, ftv_type, ok_dialog=True, container_refresh=True): self.cache_refresh = True with busy_dialog(): artwork = self.get_all_artwork(ftv_id, ftv_type) if ok_dialog and not artwork: xbmcgui.Dialog().ok('FanartTV', ADDON.getLocalizedString(32217).format(ftv_type, ftv_id)) if ok_dialog and artwork: xbmcgui.Dialog().ok('FanartTV', ADDON.getLocalizedString(32218).format( ftv_type, ftv_id, ', '.join([k.capitalize() for k, v in artwork.items() if v]))) if artwork and container_refresh: xbmc.executebuiltin('Container.Refresh') xbmc.executebuiltin('UpdateLibrary(video,/fake/path/to/force/refresh/on/home)') return artwork
def get_gzip_text(self): if not self.download_url: return with busy_dialog(): response = self.open_url(self.download_url) if not response: xbmcgui.Dialog().ok(ADDON.getAddonInfo('name'), ADDON.getLocalizedString(32058)) return with gzip.GzipFile(fileobj=BytesIO(response.content)) as downloaded_gzip: content = downloaded_gzip.read() return content
def _sync_userlist(self, remove=False, **kwargs): list_slug = xbmc.getInfoLabel( "ListItem.Property(param.list_slug)" ) if remove else self._sync_userlist_getlist() if not list_slug: return with busy_dialog(): return self.trakt_api.add_list_item(list_slug, self.trakt_type, self.unique_id, self.id_type, season=self.season, episode=self.episode, remove=remove)
def sync(self): trakt_type = 'show' if self._item.trakt_type in [ 'season', 'episode' ] else self._item.trakt_type with busy_dialog(): slug = self._trakt.get_id(self._item.unique_id, self._item.id_type, trakt_type, 'slug') comments = self._trakt.get_response_json( u'{}s'.format(trakt_type), slug, 'comments', limit=50) or [] itemlist = [ i.get('comment', '').replace('\n', ' ') for i in comments ] self._sync = self._getcomment(itemlist, comments) return self._sync
def _getlist(self, get_currentlist=False): """ Get an existing Trakt list and returns tuple of list and user slug """ if get_currentlist: return (xbmc.getInfoLabel("ListItem.Property(param.list_slug)"), xbmc.getInfoLabel("ListItem.Property(param.user_slug)")) with busy_dialog(): list_sync = self._trakt.get_list_of_lists('users/me/lists') or [] list_sync.append({'label': ADDON.getLocalizedString(32299)}) x = xbmcgui.Dialog().contextmenu([i.get('label') for i in list_sync]) if x == -1: return if list_sync[x].get('label') == ADDON.getLocalizedString(32299): return self._addlist() return (list_sync[x].get('params', {}).get('list_slug'), list_sync[x].get('params', {}).get('user_slug'))
def save_player(self, player, filename, confirm=True): if confirm and not xbmcgui.Dialog().yesno( ADDON.getLocalizedString(32336), ADDON.getLocalizedString(32337).format(filename), yeslabel=xbmc.getLocalizedString(190), nolabel=ADDON.getLocalizedString(32338)): return with busy_dialog(): self.players[filename] = player # Update our players dictionary self.dialog_players = _get_dialog_players( self.players) # Update our dialog list dumps_to_file(player, PLAYERS_BASEDIR_SAVE, filename, indent=4, join_addon_data=False) # Write out file
def monitor_userlist(): # Build list choices with busy_dialog(): user_lists = [] user_lists += TraktAPI().get_list_of_lists( 'users/me/lists', authorize=True, next_page=False) or [] user_lists += TraktAPI().get_list_of_lists( 'users/likes/lists', authorize=True, next_page=False) or [] saved_lists = _get_monitor_userlists() dialog_list = [i['label'] for i in user_lists] preselected = [ x for x, i in enumerate(user_lists) if (i.get('params', {}).get('list_slug'), i.get('params', {}).get('user_slug')) in saved_lists ] # Ask user to choose lists indices = xbmcgui.Dialog().multiselect(ADDON.getLocalizedString(32312), dialog_list, preselect=preselected) if indices is None: return # Build the new settings and check that lists aren't over limit added_lists, added_users = [], [] for x in indices: list_slug = user_lists[x].get('params', {}).get('list_slug') user_slug = user_lists[x].get('params', {}).get('user_slug') if get_userlist(user_slug, list_slug, confirm=50): added_lists.append(list_slug) added_users.append(user_slug) # Set the added lists to our settings if not added_lists or not added_users: return added_lists = ' | '.join(added_lists) added_users = ' | '.join(added_users) ADDON.setSettingString('monitor_userlist', added_lists) ADDON.setSettingString('monitor_userslug', added_users) # Update library? if xbmcgui.Dialog().yesno(xbmc.getLocalizedString(653), ADDON.getLocalizedString(32132)): library_autoupdate(list_slugs=added_lists, user_slugs=added_users, busy_spinner=True)
def select_artwork(self, ftv_id, ftv_type, container_refresh=True, blacklist=[]): if ftv_type not in ['movies', 'tv']: return with busy_dialog(): artwork = self.get_artwork_request(ftv_id, ftv_type) if not artwork: return xbmcgui.Dialog().notification('FanartTV', ADDON.getLocalizedString(32217).format(ftv_type, ftv_id)) # Choose Type _artwork_types = ['poster', 'fanart', 'clearart', 'clearlogo', 'landscape', 'banner'] _artwork_types.append('discart' if ftv_type == 'movies' else 'characterart') artwork_types = [i for i in _artwork_types if i not in blacklist] # Remove types that we previously looked for choice = xbmcgui.Dialog().select(xbmc.getLocalizedString(13511), artwork_types) if choice == -1: return # Get artwork of user's choosing artwork_type = artwork_types[choice] artwork_items = self.get_artwork(ftv_id, ftv_type, artwork_type, get_list=True) # If there was not artwork of that type found then blacklist it before re-prompting if not artwork_items: xbmcgui.Dialog().notification('FanartTV', ADDON.getLocalizedString(32217).format(ftv_type, ftv_id)) blacklist.append(artwork_types[choice]) return self.select_artwork(ftv_id, ftv_type, container_refresh, blacklist) # Choose artwork from options items = [ ListItem( label=i.get('url'), label2=ADDON.getLocalizedString(32219).format(i.get('lang', ''), i.get('likes', 0), i.get('id', '')), art={'thumb': i.get('url')}).get_listitem() for i in artwork_items if i.get('url')] choice = xbmcgui.Dialog().select(xbmc.getLocalizedString(13511), items, useDetails=True) if choice == -1: # If user hits back go back to main menu rather than exit completely return self.select_artwork(ftv_id, ftv_type, container_refresh, blacklist) # Cache our choice as the best artwork forever since it was selected manually # Some types have have HD and SD variants so set cache for both for i in ARTWORK_TYPES.get(ftv_type, {}).get(artwork_type, []): success = self._cache.set_cache( artwork_items[choice].get('url'), cache_name=u'FanartTV.best.{}.{}.{}.{}'.format(self.language, ftv_id, ftv_type, i), cache_days=10000) if success and container_refresh: xbmc.executebuiltin('Container.Refresh') xbmc.executebuiltin('UpdateLibrary(video,/fake/path/to/force/refresh/on/home)')
def _get_path_from_actions(self, actions, is_folder=True): """ Returns tuple of (path, is_folder) """ keyboard_input = None path = (actions[0], is_folder) for action in actions[1:]: # Check if we've got a playable item already if not is_folder: return path # 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 busy_dialog(): folder = get_directory(string_format_map(path[0], 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 if action.get('dialog'): auto = True if action.get('dialog', '').lower() == 'auto' else False return self._player_dialog_select(folder, auto=auto) # Apply the rules for the current action and grab the path path = self._get_path_from_rules(folder, action) if not path: return return path
def refresh_details(tmdb_id=None, tmdb_type=None, season=None, episode=None, confirm=True, **kwargs): if not tmdb_id or not tmdb_type: return with busy_dialog(): details = TMDb().get_details(tmdb_type, tmdb_id, season, episode, cache_refresh=True) if details and confirm: xbmcgui.Dialog().ok( 'TMDbHelper', ADDON.getLocalizedString(32234).format(tmdb_type, tmdb_id)) container_refresh() return details
def sync(self): """ Entry point """ slug = self._getlist(get_currentlist=self.remove) if not slug: return with busy_dialog(): self._sync = self._trakt.add_list_item(slug[0], self._item.trakt_type, self._item.unique_id, self._item.id_type, season=self._item.season, episode=self._item.episode, remove=self.remove) if self._sync and self._sync.status_code in [ 200, 201, 204 ] and self._item.id_type == 'tmdb': self._addlibrary(convert_trakt_type(self._item.trakt_type), self._item.unique_id, slug=slug) return self._sync
def refresh_details(tmdb_id=None, tmdb_type=None, season=None, episode=None, **kwargs): if not tmdb_id or not tmdb_type: return with busy_dialog(): details = TMDb().get_details(tmdb_type, tmdb_id, season, episode, cache_refresh=True) if details: xbmcgui.Dialog().ok( 'TMDbHelper', ADDON.getLocalizedString(32234).format(tmdb_type, tmdb_id)) xbmc.executebuiltin('Container.Refresh') xbmc.executebuiltin( 'UpdateLibrary(video,/fake/path/to/force/refresh/on/home)')
def _update_listing_hack(self, folder_path=None, reset_focus=None): """ Some plugins use container.update after search results to rewrite path history This is a quick hack to rewrite the path back to our original path before updating """ if not folder_path: return xbmc.Monitor().waitForAbort(2) container_folderpath = xbmc.getInfoLabel("Container.FolderPath") if container_folderpath == folder_path: return xbmc.executebuiltin( try_encode(u'Container.Update({},replace)'.format(folder_path))) if not reset_focus: return with busy_dialog(): timeout = 20 while not xbmc.Monitor().abortRequested() and xbmc.getInfoLabel( "Container.FolderPath") != folder_path and timeout > 0: xbmc.Monitor().waitForAbort(0.25) timeout -= 1 xbmc.executebuiltin(reset_focus) xbmc.Monitor().waitForAbort(0.5)
def __init__(self): with busy_dialog(): self.players = get_players_from_file() self.dialog_players = _get_dialog_players(self.players)