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 on_authenticated(self, auth_dialog=True): """Triggered when device authentication has been completed""" kodi_log(u'Trakt authenticated successfully!', 1) ADDON.setSettingString('trakt_token', dumps(self.authorization)) self.headers['Authorization'] = u'Bearer {0}'.format(self.authorization.get('access_token')) if auth_dialog: self.auth_dialog.close()
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 add_userlist(self, user_slug=None, list_slug=None, confirm=True, force=False, **kwargs): request = get_userlist(user_slug=user_slug, list_slug=list_slug, confirm=confirm, busy_spinner=self.p_dialog) if not request: return i_total = len(request) i_added = {'movie': [], 'show': []} for x, i in enumerate(request): self._update(x, i_total, message=u'Updating {}...'.format( i.get(i.get('type'), {}).get('title'))) playlist_rule = self._add_userlist_item(i, force=force) if not playlist_rule: continue i_added[i.get('type')].append(playlist_rule) if i_added.get('movie'): self._update(1, 3, message=ADDON.getLocalizedString(32349)) create_playlist(i_added['movie'], 'movies', user_slug, list_slug) if i_added.get('show'): self._update(2, 3, message=ADDON.getLocalizedString(32350)) create_playlist(i_added['show'], 'tvshows', user_slug, list_slug)
def fivehundred_error(self, request): self.req_500_err[request] = set_timestamp() get_property(self.req_500_err_prop, dumps(self.req_500_err)) kodi_log(u'ConnectionError: {}\nSuppressing retries for 1 minute'.format(dumps(self.req_500_err)), 1) xbmcgui.Dialog().notification( ADDON.getLocalizedString(32308).format(self.req_api_name), ADDON.getLocalizedString(32307))
def connection_error(self, err): self.req_connect_err = set_timestamp() get_property(self.req_connect_err_prop, self.req_connect_err) kodi_log(u'ConnectionError: {}\nSuppressing retries for 1 minute'.format(err), 1) xbmcgui.Dialog().notification( ADDON.getLocalizedString(32308).format(self.req_api_name), ADDON.getLocalizedString(32307))
def delete_list(delete_list, **kwargs): if not xbmcgui.Dialog().yesno( ADDON.getLocalizedString(32358), ADDON.getLocalizedString(32357).format(delete_list)): return TraktAPI().delete_response('users/me/lists', delete_list) container_refresh()
def _get_basedir_top(tmdb_type): return [{ 'label': ADDON.getLocalizedString(32238).format( convert_type(tmdb_type, 'plural')), 'art': { 'thumb': '{}/resources/poster.png'.format(ADDONPATH) }, 'params': { 'info': 'user_discover', 'tmdb_type': tmdb_type, 'method': 'open' } }, { 'label': ADDON.getLocalizedString(32239), 'art': { 'thumb': '{}/resources/poster.png'.format(ADDONPATH) }, 'params': { 'info': 'user_discover', 'tmdb_type': tmdb_type, 'method': 'with_separator' } }, { 'label': ADDON.getLocalizedString(32240), 'art': { 'thumb': '{}/resources/poster.png'.format(ADDONPATH) }, 'params': { 'info': 'user_discover', 'tmdb_type': tmdb_type, 'method': 'sort_by' } }]
def _get_basedir_rules_movies(): return [{ 'label': ADDON.getLocalizedString(32247), 'method': 'with_cast' }, { 'label': ADDON.getLocalizedString(32248), 'method': 'with_crew' }, { 'label': ADDON.getLocalizedString(32249), 'method': 'with_people' }, { 'label': ADDON.getLocalizedString(32250), 'method': 'primary_release_year' }, { 'label': ADDON.getLocalizedString(32251), 'method': 'primary_release_date.gte' }, { 'label': ADDON.getLocalizedString(32252), 'method': 'primary_release_date.lte' }, { 'label': ADDON.getLocalizedString(32253), 'method': 'release_date.gte' }, { 'label': ADDON.getLocalizedString(32254), 'method': 'release_date.lte' }, { 'label': ADDON.getLocalizedString(32255), 'method': 'with_release_type' }, { 'label': ADDON.getLocalizedString(32256), 'method': 'region' }]
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 get_season_list(self, tmdb_id, hide_specials=False): request = self.get_request_sc('tv/{}'.format(tmdb_id)) if not request: return [] base_item = self.mapper.get_info(request, 'tv') items, items_end = [], [] for i in request.get('seasons', []): item = self.mapper.get_info(i, 'season', base_item, definition=TMDB_PARAMS_SEASONS, tmdb_id=tmdb_id) # TODO: Fix play all # Might be issue with resolving to dummy file that resets playlist to 1 # item['context_menu'] += [( # xbmc.getLocalizedString(22083), # 'RunScript(plugin.video.themoviedb.helper,play_season={},tmdb_id={})'.format( # item['infolabels']['season'], tmdb_id))] items.append(item) if i.get('season_number') != 0 else items_end.append(item) if hide_specials: return items egroups = self.get_request_sc('tv/{}/episode_groups'.format(tmdb_id)) if egroups and egroups.get('results'): egroup_item = self.mapper.get_info({ 'title': ADDON.getLocalizedString(32345)}, 'season', base_item, tmdb_id=tmdb_id, definition={ 'info': 'episode_groups', 'tmdb_type': 'tv', 'tmdb_id': tmdb_id}) egroup_item['art']['thumb'] = egroup_item['art']['poster'] = '{}/resources/icons/trakt/groupings.png'.format(ADDONPATH) items_end.append(egroup_item) if get_property('TraktIsAuth') == 'True': upnext_item = self.mapper.get_info({ 'title': ADDON.getLocalizedString(32043)}, 'season', base_item, tmdb_id=tmdb_id, definition={ 'info': 'trakt_upnext', 'tmdb_type': 'tv', 'tmdb_id': tmdb_id}) upnext_item['art']['thumb'] = upnext_item['art']['poster'] = '{}/resources/icons/trakt/up-next.png'.format(ADDONPATH) items_end.append(upnext_item) return items + items_end
def _get_method(tmdb_type, method, header=None, use_details=False, confirmation=True): # If there's already values set then ask if the user wants to clear them instead of adding more if confirmation and not _confirm_add(method): return _clear_properties([method]) # User inputs query, we look it up on TMDb and then ask them to select a choice properties = _get_query(tmdb_type, method, header=header, use_details=use_details) # Ask user if they want to try again if nothing selected or no results returned for query if not properties: if xbmcgui.Dialog().yesno( ADDON.getLocalizedString(32103), ADDON.getLocalizedString(32104).format(tmdb_type)): return _get_method(tmdb_type, method, header, use_details, confirmation=False) return return properties
def _get_separator(): if xbmcgui.Dialog().yesno(ADDON.getLocalizedString(32107), ADDON.getLocalizedString(32108), yeslabel=ADDON.getLocalizedString(32109), nolabel=ADDON.getLocalizedString(32110)): return {'value': 'OR', 'label': 'ANY', 'method': 'with_separator'} return {'value': 'AND', 'label': 'ALL', 'method': 'with_separator'}
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 list_discoverdir(self, **kwargs): items = [] params = merge_two_dicts(kwargs, {'info': 'user_discover'}) artwork = {'thumb': u'{}/resources/poster.png'.format(ADDONPATH)} for i in ['movie', 'tv']: item = { 'label': u'{} {}'.format(ADDON.getLocalizedString(32174), convert_type(i, 'plural')), 'params': merge_two_dicts(params, {'tmdb_type': i}), 'infoproperties': {'specialsort': 'top'}, 'art': artwork} items.append(item) history = get_search_history('discover') history.reverse() for x, i in enumerate(history): item_params = merge_two_dicts(kwargs, i.get('params', {})) edit_params = {'info': 'user_discover', 'tmdb_type': item_params.get('tmdb_type'), 'method': 'edit', 'idx': x} name_params = {'info': 'dir_discover', 'tmdb_type': item_params.get('tmdb_type'), 'method': 'rename', 'idx': x} dele_params = {'info': 'dir_discover', 'tmdb_type': item_params.get('tmdb_type'), 'method': 'delete', 'idx': x} item = { 'label': i.get('label'), 'params': item_params, 'art': artwork, 'context_menu': [ (xbmc.getLocalizedString(21435), u'Container.Update({})'.format(encode_url(PLUGINPATH, **edit_params))), (xbmc.getLocalizedString(118), u'Container.Update({})'.format(encode_url(PLUGINPATH, **name_params))), (xbmc.getLocalizedString(117), u'Container.Update({})'.format(encode_url(PLUGINPATH, **dele_params)))]} items.append(item) if history: item = { 'label': ADDON.getLocalizedString(32237), 'art': artwork, 'params': merge_two_dicts(params, {'info': 'dir_discover', 'clear_cache': 'True'})} items.append(item) return items
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 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 get_list_of_lists(self, path, page=1, limit=250, authorize=False, next_page=True): response = self.get_response(path, page=page, limit=limit) like_list = True if path.startswith('lists/') else False delete_like = True if path.startswith('users/likes') else False if not response: return items = [] for i in response.json(): if i.get('list', {}).get('name'): i = i.get('list', {}) elif not i.get('name'): continue item = {} item['label'] = i.get('name') item['infolabels'] = {'plot': i.get('description')} item['infoproperties'] = {k: v for k, v in i.items() if v and type(v) not in [list, dict]} item['art'] = {} item['params'] = { 'info': 'trakt_userlist', 'list_slug': i.get('ids', {}).get('slug'), 'user_slug': i.get('user', {}).get('ids', {}).get('slug')} item['unique_ids'] = { 'trakt': i.get('ids', {}).get('trakt'), 'slug': i.get('ids', {}).get('slug'), 'user': i.get('user', {}).get('ids', {}).get('slug')} item['infoproperties']['tmdbhelper.context.sorting'] = dumps(item['params']) # Add library context menu item['context_menu'] = [( xbmc.getLocalizedString(20444), u'Runscript(plugin.video.themoviedb.helper,{})'.format( u'user_list={list_slug},user_slug={user_slug}'.format(**item['params'])))] # Unlike list context menu if path.startswith('users/likes'): item['context_menu'] += [( ADDON.getLocalizedString(32319), u'Runscript(plugin.video.themoviedb.helper,{},delete)'.format( u'like_list={list_slug},user_slug={user_slug}'.format(**item['params'])))] # Like list context menu elif path.startswith('lists/'): item['context_menu'] += [( ADDON.getLocalizedString(32315), u'Runscript(plugin.video.themoviedb.helper,{})'.format( u'like_list={list_slug},user_slug={user_slug}'.format(**item['params'])))] # Owner of list so set param to allow deleting later else: item['params']['owner'] = 'true' item['context_menu'] += [( xbmc.getLocalizedString(118), u'Runscript(plugin.video.themoviedb.helper,{})'.format( u'rename_list={list_slug}'.format(**item['params'])))] item['context_menu'] += [( xbmc.getLocalizedString(117), u'Runscript(plugin.video.themoviedb.helper,{})'.format( u'delete_list={list_slug}'.format(**item['params'])))] items.append(item) if not next_page: return items return items + pages.get_next_page(response.headers)
def _get_release_types(): return [ {'id': 1, 'name': ADDON.getLocalizedString(32242)}, {'id': 2, 'name': ADDON.getLocalizedString(32243)}, {'id': 3, 'name': ADDON.getLocalizedString(32244)}, {'id': 4, 'name': ADDON.getLocalizedString(32245)}, {'id': 5, 'name': ADDON.getLocalizedString(32246)}, {'id': 6, 'name': xbmc.getLocalizedString(36037)}]
def _get_monitor_userlists(list_slugs=None, user_slugs=None): saved_lists = list_slugs or ADDON.getSettingString( 'monitor_userlist') or '' saved_users = user_slugs or ADDON.getSettingString( 'monitor_userslug') or '' saved_lists = saved_lists.split(' | ') or [] saved_users = saved_users.split(' | ') or [] return [(i, saved_users[x]) for x, i in enumerate(saved_lists)]
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 _confirm_add(method): old_label = _win_prop(method, 'Label') old_value = _win_prop(method) if old_value or old_label: if xbmcgui.Dialog().yesno( method, '\n'.join([ADDON.getLocalizedString(32099), old_label, ADDON.getLocalizedString(32100)]), yeslabel=ADDON.getLocalizedString(32101), nolabel=ADDON.getLocalizedString(32102)): 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 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 connection_error(self, err, wait_time=30, msg_affix=''): self.req_connect_err = set_timestamp(wait_time) get_property(self.req_connect_err_prop, self.req_connect_err) kodi_log( u'ConnectionError: {} {}\nSuppressing retries for 30 seconds'. format(msg_affix, err), 1) xbmcgui.Dialog().notification( ADDON.getLocalizedString(32308).format(' '.join( [self.req_api_name, msg_affix])), ADDON.getLocalizedString(32307).format('30'))
def list_nextepisodes(self, info, tmdb_type, page=None, **kwargs): if tmdb_type != 'tv': return sort_by_premiered = True if ADDON.getSettingString('trakt_nextepisodesort') == 'airdate' else False items = self.trakt_api.get_upnext_episodes_list(page=page, sort_by_premiered=sort_by_premiered) self.tmdb_cache_only = False # self.kodi_db = self.get_kodi_database(tmdb_type) self.library = 'video' self.container_content = 'episodes' self.thumb_override = ADDON.getSettingInt('calendar_art') return items
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 login(self): self.code = self.get_api_request_json('https://api.trakt.tv/oauth/device/code', postdata={'client_id': self.client_id}) if not self.code.get('user_code') or not self.code.get('device_code'): return # TODO: DIALOG: Authentication Error self.progress = 0 self.interval = self.code.get('interval', 5) self.expires_in = self.code.get('expires_in', 0) self.auth_dialog = xbmcgui.DialogProgress() self.auth_dialog.create(ADDON.getLocalizedString(32097), u'{}\n{}: [B]{}[/B]'.format( ADDON.getLocalizedString(32096), ADDON.getLocalizedString(32095), self.code.get('user_code'))) self.poller()
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 _user_list_check(self): if xbmc.getInfoLabel("ListItem.Property(param.owner)") == 'true': return [{ 'name': ADDON.getLocalizedString(32355), 'method': 'userlist', 'remove': True }] return [{ 'name': ADDON.getLocalizedString(32298), 'method': 'userlist' }]