def __init__(self): self.handle = int(sys.argv[1]) self.paramstring = try_decode(sys.argv[2][1:]) self.params = parse_paramstring(self.paramstring) self.parent_params = self.params # self.container_path = u'{}?{}'.format(sys.argv[0], self.paramstring) self.update_listing = False self.plugin_category = '' self.container_content = '' self.container_update = None self.container_refresh = False self.item_type = None self.kodi_db = None self.kodi_db_tv = {} self.library = None self.tmdb_cache_only = True self.tmdb_api = TMDb() self.trakt_watchedindicators = ADDON.getSettingBool('trakt_watchedindicators') self.trakt_api = TraktAPI() self.is_widget = True if self.params.pop('widget', '').lower() == 'true' else False self.hide_watched = ADDON.getSettingBool('widgets_hidewatched') if self.is_widget else False self.flatten_seasons = ADDON.getSettingBool('flatten_seasons') self.ftv_forced_lookup = self.params.pop('fanarttv', '').lower() self.ftv_api = FanartTV(cache_only=self.ftv_is_cache_only()) self.filter_key = self.params.pop('filter_key', None) self.filter_value = split_items(self.params.pop('filter_value', None))[0] self.exclude_key = self.params.pop('exclude_key', None) self.exclude_value = split_items(self.params.pop('exclude_value', None))[0] self.pagination = self.pagination_is_allowed() self.params = reconfigure_legacy_params(**self.params)
def play(self, folder_path=None, reset_focus=None, handle=None): # Get some info about current container for container update hack if not folder_path: folder_path = xbmc.getInfoLabel("Container.FolderPath") if not reset_focus and folder_path: containerid = xbmc.getInfoLabel("System.CurrentControlID") current_pos = xbmc.getInfoLabel("Container({}).CurrentItem".format(containerid)) reset_focus = 'SetFocus({},{},absolute)'.format(containerid, try_int(current_pos) - 1) # Get the resolved path listitem = self.get_resolved_path() # Reset folder hack self._update_listing_hack(folder_path=folder_path, reset_focus=reset_focus) # Check we have an actual path to open if not listitem.getPath() or listitem.getPath() == PLUGINPATH: return action = self.configure_action(listitem, handle) # Kodi launches busy dialog on home screen that needs to be told to close # Otherwise the busy dialog will prevent window activation for folder path xbmc.executebuiltin('Dialog.Close(busydialog)') # If a folder we need to resolve to dummy and then open folder if listitem.getProperty('is_folder') == 'true': if self.is_strm or not ADDON.getSettingBool('only_resolve_strm'): resolve_to_dummy(handle, self.dummy_duration, self.dummy_delay) xbmc.executebuiltin(action) kodi_log(['lib.player - finished executing action\n', action], 1) return # Set our playerstring for player monitor to update kodi watched status if self.playerstring: get_property('PlayerInfoString', set_property=self.playerstring) # If PlayMedia method chosen re-route to Player() unless expert settings on if action: if self.is_strm or not ADDON.getSettingBool('only_resolve_strm'): resolve_to_dummy(handle, self.dummy_duration, self.dummy_delay) # If we're calling external we need to resolve to dummy xbmc.Player().play(action, listitem) if self.force_xbmcplayer else xbmc.executebuiltin(u'PlayMedia({})'.format(action)) kodi_log([ 'lib.player - playing path with {}\n'.format('xbmc.Player()' if self.force_xbmcplayer else 'PlayMedia'), listitem.getPath()], 1) return # Otherwise we have a url we can resolve to xbmcplugin.setResolvedUrl(handle, True, listitem) kodi_log(['lib.player - finished resolving path to url\n', listitem.getPath()], 1) # Re-send local files to player due to "bug" (or maybe "feature") of setResolvedUrl # Because setResolvedURL doesn't set id/type (sets None, "unknown" instead) to player for plugins # If id/type not set to Player.GetItem things like Trakt don't work correctly. # Looking for better solution than this hack. if ADDON.getSettingBool('trakt_localhack') and listitem.getProperty('is_local') == 'true': xbmc.Player().play(listitem.getPath(), listitem) if self.force_xbmcplayer else xbmc.executebuiltin(u'PlayMedia({})'.format(listitem.getPath())) kodi_log([ 'Finished executing {}\n'.format('xbmc.Player()' if self.force_xbmcplayer else 'PlayMedia'), listitem.getPath()], 1)
def ftv_is_cache_only(self): if self.ftv_forced_lookup == 'true': return False if self.ftv_forced_lookup == 'false': return True if self.is_widget and ADDON.getSettingBool('widget_fanarttv_lookup'): return False if not self.is_widget and ADDON.getSettingBool('fanarttv_lookup'): return False return True
def __init__(self, busy_spinner=True): self.kodi_db_movies = rpc.get_kodi_library('movie') self.kodi_db_tv = rpc.get_kodi_library('tv') self.p_dialog = xbmcgui.DialogProgressBG() if busy_spinner else None self.auto_update = ADDON.getSettingBool('auto_update') self._log = _LibraryLogger() self.tv = None self.hide_unaired = ADDON.getSettingBool('hide_unaired_episodes') # self.debug_logging = ADDON.getSettingBool('debug_logging') self.debug_logging = True
def get_playingitem(self): if not self.isPlayingVideo(): return # Not a video so don't get info if self.getVideoInfoTag().getMediaType() not in ['movie', 'episode']: return # Not a movie or episode so don't get info TODO Maybe get PVR details also? self.playerstring = get_property('PlayerInfoString') self.playerstring = loads( self.playerstring) if self.playerstring else None self.total_time = self.getTotalTime() self.dbtype = self.getVideoInfoTag().getMediaType() self.dbid = self.getVideoInfoTag().getDbId() self.imdb_id = self.getVideoInfoTag().getIMDBNumber() self.query = self.getVideoInfoTag().getTVShowTitle( ) if self.dbtype == 'episode' else self.getVideoInfoTag().getTitle() self.year = self.getVideoInfoTag().getYear( ) if self.dbtype == 'movie' else None self.epyear = self.getVideoInfoTag().getYear( ) if self.dbtype == 'episodes' else None self.season = self.getVideoInfoTag().getSeason( ) if self.dbtype == 'episodes' else None self.episode = self.getVideoInfoTag().getEpisode( ) if self.dbtype == 'episodes' else None self.query = try_decode(self.query) self.tmdb_type = 'movie' if self.dbtype == 'movie' else 'tv' self.tmdb_id = self.get_tmdb_id(self.tmdb_type, self.imdb_id, self.query, self.year, self.epyear) self.details = self.tmdb_api.get_details(self.tmdb_type, self.tmdb_id, self.season, self.episode) # Clear everything if we didn't get details because nothing to compare if not self.details: return self.reset_properties() # Get ratings (no need for threading since we're only getting one item in player ever) if xbmc.getCondVisibility( "!Skin.HasSetting(TMDbHelper.DisableRatings)"): self.details = self.get_omdb_ratings(self.details) if self.tmdb_type == 'movie': self.details = self.get_imdb_top250_rank(self.details) if self.tmdb_type in ['movie', 'tv']: self.details = self.get_trakt_ratings( self.details, 'movie' if self.tmdb_type == 'movie' else 'show', season=self.season, episode=self.episode) self.set_iter_properties(self.details.get('infoproperties', {}), SETPROP_RATINGS) # Get artwork (no need for threading since we're only getting one item in player ever) # No need for merging Kodi DB artwork as we should have access to that via normal player properties if xbmc.getCondVisibility( "!Skin.HasSetting(TMDbHelper.DisableArtwork)"): if ADDON.getSettingBool('service_fanarttv_lookup'): self.details = self.get_fanarttv_artwork( self.details, self.tmdb_type) self.set_iter_properties(self.details, SETMAIN_ARTWORK) self.set_properties(self.details)
def run(self): self.xbmc_monitor.waitForAbort( 600) # Wait 10 minutes before doing updates to give boot time if self.xbmc_monitor.abortRequested(): del self.xbmc_monitor return self.next_time = datetime.datetime.combine( datetime.datetime.today(), datetime.time(try_int(self.update_hour))) # Get today at hour self.last_time = xbmc.getInfoLabel( 'Skin.String(TMDbHelper.AutoUpdate.LastTime)') # Get last update self.last_time = convert_timestamp( self.last_time) if self.last_time else None if self.last_time and self.last_time > self.next_time: self.next_time += datetime.timedelta( hours=24) # Already updated today so set for tomorrow while not self.xbmc_monitor.abortRequested( ) and not self.exit and self.poll_time: if ADDON.getSettingBool('library_autoupdate'): if datetime.datetime.now( ) > self.next_time: # Scheduled time has past so lets update xbmc.executebuiltin( 'RunScript(plugin.video.themoviedb.helper,library_autoupdate)' ) xbmc.executebuiltin( 'Skin.SetString(TMDbHelper.AutoUpdate.LastTime,{})'. format(datetime.datetime.now().strftime( "%Y-%m-%dT%H:%M:%S"))) self.next_time += datetime.timedelta( hours=24) # Set next update for tomorrow self.xbmc_monitor.waitForAbort(self.poll_time) del self.xbmc_monitor
def __init__(self, tmdb_type, tmdb_id=None, season=None, episode=None, ignore_default=False, **kwargs): 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 self.tmdb_type, self.tmdb_id, self.season, self.episode = tmdb_type, tmdb_id, season, episode self.dummy_duration = try_float( ADDON.getSettingString('dummy_duration')) or 1.0 self.dummy_delay = try_float( ADDON.getSettingString('dummy_delay')) or 1.0 self.force_xbmcplayer = ADDON.getSettingBool('force_xbmcplayer')
def _set_params_reroute_details(self, flatten_seasons): if (self.parent_params.get('info') == 'library_nextaired' and ADDON.getSettingBool('nextaired_linklibrary') and self.infoproperties.get('tvshow.dbid')): self.path = u'videodb://tvshows/titles/{}/'.format(self.infoproperties['tvshow.dbid']) self.params = {} self.is_folder = True return self._set_params_reroute_default()
def _set_params_reroute_default(self): if not ADDON.getSettingInt('default_select'): self.params['info'] = 'play' if not ADDON.getSettingBool('only_resolve_strm'): self.infoproperties['isPlayable'] = 'true' else: self.params['info'] = 'related' self.is_folder = False self.infoproperties['tmdbhelper.context.playusing'] = u'{}&ignore_default=true'.format(self.get_url())
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 make_path(path, warn_dialog=False): if xbmcvfs.exists(path): return xbmc.translatePath(path) if xbmcvfs.mkdirs(path): return xbmc.translatePath(path) if ADDON.getSettingBool('ignore_folderchecking'): kodi_log(u'Ignored xbmcvfs folder check error\n{}'.format(path), 2) return xbmc.translatePath(path) kodi_log(u'XBMCVFS unable to create path:\n{}'.format(path), 2) if not warn_dialog: return xbmcgui.Dialog().ok( 'XBMCVFS', u'{} [B]{}[/B]\n{}'.format(ADDON.getLocalizedString(32122), path, ADDON.getLocalizedString(32123)))
def process_artwork(self, details, tmdb_type): self.clear_property_list(SETMAIN_ARTWORK) if self.dbtype not in ['movies', 'tvshows', 'episodes']: if tmdb_type not in ['movie', 'tv']: return if ADDON.getSettingBool('service_fanarttv_lookup'): details = self.get_fanarttv_artwork(details, tmdb_type) if not self.is_same_item(): return self.set_iter_properties(details.get('art', {}), SETMAIN_ARTWORK) # Crop Image if details.get('clearlogo'): if xbmc.getCondVisibility("Skin.HasSetting(TMDbHelper.EnableCrop)"): self.crop_img = ImageFunctions(method='crop', artwork=details.get('clearlogo')) self.crop_img.setName('crop_img') self.crop_img.start()
def get_default_player(self): """ Returns default player """ if self.ignore_default: return # Check local first if we have the setting if ADDON.getSettingBool('default_player_local') and self.dialog_players[0].get('is_local'): player = self.dialog_players[0] player['idx'] = 0 return player if not self.default_player: return all_players = [u'{} {}'.format(i.get('file'), i.get('mode')) for i in self.dialog_players] try: x = all_players.index(self.default_player) except Exception: return player = self.dialog_players[x] player['idx'] = x return player
def __init__(self, tmdb_type, tmdb_id=None, season=None, episode=None, ignore_default=False, islocal=False, **kwargs): with ProgressDialog('TMDbHelper', u'{}...'.format(ADDON.getLocalizedString(32374)), total=3) as _p_dialog: self.api_language = None self.players = get_players_from_file() _p_dialog.update(u'{}...'.format(ADDON.getLocalizedString(32375))) 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 {} _p_dialog.update(u'{}...'.format(ADDON.getLocalizedString(32376))) 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 self.tmdb_type, self.tmdb_id, self.season, self.episode = tmdb_type, tmdb_id, season, episode self.dummy_duration = try_float( ADDON.getSettingString('dummy_duration')) or 1.0 self.dummy_delay = try_float( ADDON.getSettingString('dummy_delay')) or 1.0 self.force_xbmcplayer = ADDON.getSettingBool('force_xbmcplayer') self.is_strm = islocal
def get_players_from_file(): players = {} basedirs = [PLAYERS_BASEDIR_USER] if ADDON.getSettingBool('bundled_players'): basedirs += [PLAYERS_BASEDIR_BUNDLED] basedirs += [PLAYERS_BASEDIR_SAVE ] # Add saved players last so they overwrite for basedir in basedirs: files = get_files_in_folder(basedir, r'.*\.json') for file in files: meta = loads(read_file(basedir + file)) or {} 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 i in plugins: if not xbmc.getCondVisibility( u'System.HasAddon({0})'.format(i)): break # System doesn't have a required plugin so skip this player else: meta['plugin'] = plugins[0] players[try_decode(file)] = meta return players
def pagination_is_allowed(self): if self.params.pop('nextpage', '').lower() == 'false': return False if self.is_widget and not ADDON.getSettingBool('widgets_nextpage'): return False return True
def _start(self): if self.p_dialog: self.p_dialog.create('TMDbHelper', ADDON.getLocalizedString(32166)) if not ADDON.getSettingBool('legacy_conversion'): self.legacy_conversion()
def get_kodi_database(self, tmdb_type): if ADDON.getSettingBool('local_db'): return get_kodi_library(tmdb_type)
def list_seasons(self, tmdb_id, **kwargs): items = self.tmdb_api.get_season_list( tmdb_id, hide_specials=ADDON.getSettingBool('hide_special_seasons')) self.container_content = convert_type('season', 'container') return items
def unaired_bool(self): if ADDON.getSettingBool('hide_unaired_episodes'): return True
import xbmcgui from resources.lib.addon.decorators import busy_dialog from resources.lib.addon.plugin import kodi_log, ADDON from resources.lib.addon.parser import try_int from resources.lib.files.utils import validify_filename, make_path, write_to_file, get_tmdb_id_nfo from resources.lib.trakt.api import TraktAPI STRM_MOVIE = 'plugin://plugin.video.themoviedb.helper/?info=play&tmdb_id={}&tmdb_type=movie&islocal=True' STRM_EPISODE = 'plugin://plugin.video.themoviedb.helper/?info=play&tmdb_type=tv&islocal=True&tmdb_id={}&season={}&episode={}' BASEDIR_MOVIE = ADDON.getSettingString( 'movies_library' ) or 'special://profile/addon_data/plugin.video.themoviedb.helper/movies/' BASEDIR_TV = ADDON.getSettingString( 'tvshows_library' ) or 'special://profile/addon_data/plugin.video.themoviedb.helper/tvshows/' NFOFILE_MOVIE = u'movie-tmdbhelper' if ADDON.getSettingBool( 'alternative_nfo') else u'movie' NFOFILE_TV = u'tvshow-tmdbhelper' if ADDON.getSettingBool( 'alternative_nfo') else u'tvshow' """ IMPORTANT: These limits are set to prevent excessive API data usage. Please respect the APIs that provide this data for free. """ LIBRARY_ADD_LIMIT_TVSHOWS = 500 LIBRARY_ADD_LIMIT_MOVIES = 2500 def replace_content(content, old, new): content = content.replace(old, new) return replace_content(content, old, new) if old in content else content