def get_item_details(tmdb_type, tmdb_id, season=None, episode=None, language=None): tmdb_api = TMDb(language=language) if language else TMDb() details = tmdb_api.get_details(tmdb_type, tmdb_id, season, episode) del tmdb_api if not details: return details = ListItem(**details) details.infolabels['mediatype'] == 'movie' if tmdb_type == 'movie' else 'episode' details.set_details(details=get_external_ids(details, season=season, episode=episode)) return details
def select_player(self, detailed=True, clear_player=False, header=ADDON.getLocalizedString(32042)): """ Returns user selected player via dialog - detailed bool switches dialog style """ dialog_players = [] if not clear_player else [{ 'name': ADDON.getLocalizedString(32311), 'plugin_name': 'plugin.video.themoviedb.helper', 'plugin_icon': u'{}/resources/icons/other/kodi.png'.format(ADDONPATH) }] dialog_players += self.dialog_players players = [ ListItem(label=i.get('name'), label2=u'{} v{}'.format( i.get('plugin_name'), xbmcaddon.Addon(i.get('plugin_name', '')).getAddonInfo('version')), art={ 'thumb': i.get('plugin_icon') }).get_listitem() for i in dialog_players ] x = xbmcgui.Dialog().select(header, players, useDetails=detailed) if x == -1: return {} player = dialog_players[x] player['idx'] = x return player
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 get_item_details(tmdb_type, tmdb_id, season=None, episode=None): details = TMDb().get_details(tmdb_type, tmdb_id, season, episode) if not details: return details = ListItem(**details) details.infolabels['mediatype'] == 'movie' if tmdb_type == 'movie' else 'episode' details.set_details(details=get_external_ids(details, season=season, episode=episode)) return details
def _player_dialog_select(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 try_int(f.get('season', 0)) > 0 and try_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(try_int(sdv.get('duration', 0)) // 60)) if f.get('size'): label_b_list.append(u'{}'.format(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, art={'thumb': f.get('thumbnail')}).get_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(ADDON.getLocalizedString(32236), d_items, useDetails=True) if idx == -1: return # User exited the dialog so return nothing is_folder = False if folder[idx].get('filetype') == 'file' else True return (folder[idx].get('file'), is_folder) # Return the player
def get_tmdb_id_from_query(self, tmdb_type, query, header=None, use_details=False, get_listitem=False, auto_single=False): if not query or not tmdb_type: return response = self.get_tmdb_id(tmdb_type, query=query, raw_data=True) items = [ListItem(**self.mapper.get_info(i, tmdb_type)).get_listitem() for i in response] if not items: return x = 0 if not auto_single or len(items) != 1: x = xbmcgui.Dialog().select(header, items, useDetails=use_details) if x != -1: return items[x] if get_listitem else items[x].getUniqueID('tmdb')
def _get_dialog_players(players): return [ ListItem( label=v.get('name'), label2=k, art={ 'thumb': v.get('icon', '').format(ADDONPATH) or xbmcaddon.Addon(v.get('plugin', '')).getAddonInfo('icon') }).get_listitem() for k, v in sorted(viewitems(players), key=lambda i: try_int(i[1].get('priority')) or PLAYERS_PRIORITY) ]
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 add_items(self, items=None, pagination=True, parent_params=None, property_params=None, kodi_db=None, tmdb_cache_only=True): if not items: return self.check_is_aired = parent_params.get('info') not in NO_LABEL_FORMATTING # Build empty queue and thread pool self.items_queue, pool = [None] * len(items), [None] * len(items) # Start item build threads for x, i in enumerate(items): if not pagination and 'next_page' in i: continue if self.item_is_excluded(i): continue li = ListItem(parent_params=parent_params, **i) pool[x] = Thread(target=self._add_item, args=[x, li, tmdb_cache_only]) pool[x].start() # Wait to join threads in pool first before adding item to directory for x, i in enumerate(pool): if not i: continue i.join() li = self.items_queue[x] if not li: continue li.set_episode_label() if self.check_is_aired and li.is_unaired(): return li.set_details(details=self.get_kodi_details(li), reverse=True) # Quick because local db li.set_playcount(playcount=self.get_playcount_from_trakt(li)) # Quick because of agressive caching of Trakt object and pre-emptive dict comprehension if self.hide_watched and try_int(li.infolabels.get('playcount')) != 0: continue li.set_context_menu() # Set the context menu items li.set_uids_to_info() # Add unique ids to properties so accessible in skins li.set_params_reroute(self.ftv_forced_lookup, self.flatten_seasons) # Reroute details to proper end point li.set_params_to_info(self.plugin_category) # Set path params to properties for use in skins li.infoproperties.update(property_params or {}) xbmcplugin.addDirectoryItem( handle=self.handle, url=li.get_url(), listitem=li.get_listitem(), isFolder=li.is_folder)
def add_items(self, items=None, pagination=True, parent_params=None, property_params=None, kodi_db=None, tmdb_cache_only=True): if not items: return check_is_aired = parent_params.get('info') not in NO_LABEL_FORMATTING for i in items: if not pagination and 'next_page' in i: continue if self.item_is_excluded(i): continue li = ListItem(parent_params=parent_params, **i) li.set_details(details=self.get_tmdb_details(li, cache_only=tmdb_cache_only)) # Quick because only get cached li.set_episode_label() if check_is_aired and li.is_unaired(): continue li.set_details(details=self.get_ftv_artwork(li), reverse=True) # Slow when not cache only li.set_details(details=self.get_kodi_details(li), reverse=True) # Quick because local db li.set_playcount(playcount=self.get_playcount_from_trakt(li)) # Quick because of agressive caching of Trakt object and pre-emptive dict comprehension if self.hide_watched and try_int(li.infolabels.get('playcount')) != 0: continue li.set_context_menu() # Set the context menu items li.set_uids_to_info() # Add unique ids to properties so accessible in skins li.set_params_reroute(self.ftv_forced_lookup, self.flatten_seasons) # Reroute details to proper end point li.set_params_to_info(self.plugin_category) # Set path params to properties for use in skins li.infoproperties.update(property_params or {}) xbmcplugin.addDirectoryItem( handle=self.handle, url=li.get_url(), listitem=li.get_listitem(), isFolder=li.is_folder)
def add_items(self, items=None, pagination=True, parent_params=None, property_params=None, kodi_db=None, cache_only=True): if not items: return check_is_aired = parent_params.get('info') not in NO_LABEL_FORMATTING hide_nodate = ADDON.getSettingBool('nodate_is_unaired') # Pre-game details and artwork cache for seasons/episodes before threading to avoid multiple API calls ftv_art = None if parent_params.get('info') in ['seasons', 'episodes', 'episode_groups', 'trakt_upnext']: details = self.tmdb_api.get_details('tv', parent_params.get('tmdb_id'), parent_params.get('season', 0), cache_only=cache_only) ftv_art = self.get_ftv_artwork(ListItem(parent_params=parent_params, **details)) # Build empty queue and thread pool self.items_queue, pool = [None] * len(items), [None] * len(items) # Start item build threads for x, i in enumerate(items): if not pagination and 'next_page' in i: continue if self.item_is_excluded(i): continue li = ListItem(parent_params=parent_params, **i) pool[x] = Thread(target=self._add_item, args=[x, li, cache_only, ftv_art]) pool[x].start() # Wait to join threads in pool first before adding item to directory for x, i in enumerate(pool): if not i: continue i.join() li = self.items_queue[x] if not li: continue li.set_episode_label() if check_is_aired and li.is_unaired(no_date=hide_nodate): continue li.set_details(details=self.get_kodi_details(li), reverse=True) # Quick because local db li.set_playcount(playcount=self.get_playcount_from_trakt(li)) # Quick because of agressive caching of Trakt object and pre-emptive dict comprehension if self.hide_watched and try_int(li.infolabels.get('playcount')) != 0: continue li.set_cast() li.set_context_menu() # Set the context menu items li.set_uids_to_info() # Add unique ids to properties so accessible in skins li.set_thumb_to_art(self.thumb_override == 2) if self.thumb_override else None li.set_params_reroute(self.ftv_forced_lookup, self.flatten_seasons) # Reroute details to proper end point li.set_params_to_info(self.plugin_category) # Set path params to properties for use in skins li.infoproperties.update(property_params or {}) if self.thumb_override: li.infolabels.pop('dbid', None) # Need to pop the DBID if overriding thumb otherwise Kodi overrides after item is created xbmcplugin.addDirectoryItem( handle=self.handle, url=li.get_url(), listitem=li.get_listitem(), isFolder=li.is_folder)
def _get_ftv_id(**kwargs): details = refresh_details(confirm=False, **kwargs) if not details: return return ListItem(**details).get_ftv_id()