def parse_profiles(data): """Parse profile information from Netflix response""" profiles_list = jgraph_get_list('profilesList', data) try: if not profiles_list: raise InvalidProfilesError('It has not been possible to obtain the list of profiles.') sort_order = 0 current_guids = [] for index, profile_data in iteritems(profiles_list): # pylint: disable=unused-variable summary = jgraph_get('summary', profile_data) guid = summary['guid'] current_guids.append(guid) common.debug('Parsing profile {}', summary['guid']) avatar_url = _get_avatar(profile_data, data, guid) is_active = summary.pop('isActive') g.LOCAL_DB.set_profile(guid, is_active, sort_order) g.SHARED_DB.set_profile(guid, sort_order) # Add profile language description translated from locale summary['language_desc'] = g.py2_decode(xbmc.convertLanguage(summary['language'][:2], xbmc.ENGLISH_NAME)) for key, value in iteritems(summary): if key in PROFILE_DEBUG_INFO: common.debug('Profile info {}', {key: value}) if key == 'profileName': # The profile name is coded as HTML value = parse_html(value) g.LOCAL_DB.set_profile_config(key, value, guid) g.LOCAL_DB.set_profile_config('avatar', avatar_url, guid) sort_order += 1 _delete_non_existing_profiles(current_guids) except Exception: import traceback common.error(g.py2_decode(traceback.format_exc(), 'latin-1')) common.error('Profile list data: {}', profiles_list) raise InvalidProfilesError
def _get_item(mediatype, filename): # To ensure compatibility with previously exported items, # make the filename legal fname = makeLegalFilename(filename) untranslated_path = os.path.dirname(g.py2_decode(fname)) translated_path = os.path.dirname(g.py2_decode(xbmc.translatePath(fname))) shortname = os.path.basename(g.py2_decode(xbmc.translatePath(fname))) # We get the data from Kodi library using filters. # This is much faster than loading all episodes in memory # First build the path filter, we may have to search in both special and translated path path_filter = {'field': 'path', 'operator': 'startswith', 'value': translated_path} \ if fname[:10] != 'special://' \ else {'or': [ {'field': 'path', 'operator': 'startswith', 'value': translated_path}, {'field': 'path', 'operator': 'startswith', 'value': untranslated_path} ]} # Now build the all request and call the json-rpc function through common.get_library_items library_item = common.get_library_items( mediatype, { 'and': [ path_filter, { 'field': 'filename', 'operator': 'is', 'value': shortname } ] })[0] if not library_item: raise ItemNotFound return common.get_library_item_details(mediatype, library_item[mediatype + 'id'])
def load(account_hash): """Load cookies for a given account and check them for validity""" filename = cookie_filename(account_hash) if not xbmcvfs.exists(xbmc.translatePath(filename)): common.debug('Cookies file does not exist') raise MissingCookiesError() common.debug('Loading cookies from {}', g.py2_decode(filename)) cookie_file = xbmcvfs.File(filename, 'rb') try: if g.PY_IS_VER2: # pickle.loads on py2 wants string cookie_jar = pickle.loads(cookie_file.read()) else: cookie_jar = pickle.loads(cookie_file.readBytes()) except Exception as exc: import traceback common.error('Failed to load cookies from file: {exc}', exc=exc) common.error(g.py2_decode(traceback.format_exc(), 'latin-1')) raise MissingCookiesError() finally: cookie_file.close() # Clear flwssn cookie if present, as it is trouble with early expiration try: cookie_jar.clear(domain='.netflix.com', path='/', name='flwssn') except KeyError: pass log_cookie(cookie_jar) return cookie_jar
def _lib_folders(section): section_dir = g.py2_decode( xbmc.translatePath( makeLegalFilename('/'.join([library_path(), section])))) return [ g.py2_decode( makeLegalFilename('/'.join([section_dir, folder.decode('utf-8')]))) for folder in xbmcvfs.listdir(section_dir)[0] ]
def delete_cache_folder(): # Delete cache folder in the add-on userdata (no more needed with the new cache management) cache_path = os.path.join(g.DATA_PATH, 'cache') if not os.path.exists(g.py2_decode(xbmc.translatePath(cache_path))): return debug('Deleting the cache folder from add-on userdata folder') try: delete_folder_contents(cache_path, True) xbmc.sleep(80) xbmcvfs.rmdir(cache_path) except Exception: # pylint: disable=broad-except import traceback error(g.py2_decode(traceback.format_exc(), 'latin-1'))
def load_manifest(self, viewable_id): """ Loads the manifests for the given viewable_id and returns a mpd-XML-Manifest :param viewable_id: The id of of the viewable :return: MPD XML Manifest or False if no success """ try: manifest = self._load_manifest(viewable_id, g.get_esn()) except MSLError as exc: if 'Email or password is incorrect' in g.py2_decode(str(exc)): # Known cases when MSL error "Email or password is incorrect." can happen: # - If user change the password when the nf session was still active # - Netflix has reset the password for suspicious activity when the nf session was still active # Then clear the credentials and also user tokens. common.purge_credentials() self.msl_requests.crypto.clear_user_id_tokens() raise # Disable 1080p Unlock for now, as it is broken due to Netflix changes # if (g.ADDON.getSettingBool('enable_1080p_unlock') and # not g.ADDON.getSettingBool('enable_vp9_profiles') and # not has_1080p(manifest)): # common.debug('Manifest has no 1080p viewables, trying unlock') # manifest = self.get_edge_manifest(viewable_id, manifest) return self.__tranform_to_dash(manifest)
def execute_tasks(title, tasks, task_handler, **kwargs): """ Run all tasks through task_handler and display a progress dialog in the GUI. Additional kwargs will be passed into task_handler on each invocation. Returns a list of errors that occured during execution of tasks. """ errors = [] notify_errors = kwargs.pop('notify_errors', False) progress = xbmcgui.DialogProgress() progress.create(title) for task_num, task in enumerate(tasks): task_title = task.get('title', 'Unknown Task') progress.update(int(task_num * 100 / len(tasks)), task_title) # xbmc.sleep(25) if progress.iscanceled(): break if not task: continue try: task_handler(task, **kwargs) except Exception as exc: # pylint: disable=broad-except import traceback common.error(g.py2_decode(traceback.format_exc(), 'latin-1')) errors.append({ 'task_title': task_title, 'error': '{}: {}'.format(type(exc).__name__, exc)}) show_library_task_errors(notify_errors, errors) return errors
def __init__(self): super(AndroidMSLCrypto, self).__init__() self.crypto_session = None self.keyset_id = None self.key_id = None self.hmac_key_id = None try: self.crypto_session = xbmcdrm.CryptoSession( 'edef8ba9-79d6-4ace-a3c8-27dcd51d21ed', 'AES/CBC/NoPadding', 'HmacSHA256') common.debug('Widevine CryptoSession successful constructed') except Exception: # pylint: disable=broad-except import traceback common.error(g.py2_decode(traceback.format_exc(), 'latin-1')) raise MSLError('Failed to construct Widevine CryptoSession') drm_info = { 'version': self.crypto_session.GetPropertyString('version'), 'system_id': self.crypto_session.GetPropertyString('systemId'), # 'device_unique_id': self.crypto_session.GetPropertyByteArray('deviceUniqueId') 'hdcp_level': self.crypto_session.GetPropertyString('hdcpLevel'), 'hdcp_level_max': self.crypto_session.GetPropertyString('maxHdcpLevel'), 'security_level': self.crypto_session.GetPropertyString('securityLevel') } if not drm_info['version']: # Possible cases where no data is obtained: # - Device with custom ROM or without Widevine support # - Using Kodi debug build with a InputStream Adaptive release build (yes users do it) raise MSLError( 'It was not possible to get the data from Widevine CryptoSession.\r\n' 'Your system is not Widevine certified or you have a wrong Kodi version installed.' ) g.LOCAL_DB.set_value('drm_system_id', drm_info['system_id'], TABLE_SESSION) g.LOCAL_DB.set_value('drm_security_level', drm_info['security_level'], TABLE_SESSION) g.LOCAL_DB.set_value('drm_hdcp_level', drm_info['hdcp_level'], TABLE_SESSION) common.debug('Widevine version: {}', drm_info['version']) if drm_info['system_id']: common.debug('Widevine CryptoSession system id: {}', drm_info['system_id']) else: common.warn('Widevine CryptoSession system id not obtained!') common.debug('Widevine CryptoSession security level: {}', drm_info['security_level']) common.debug('Widevine CryptoSession current hdcp level: {}', drm_info['hdcp_level']) common.debug('Widevine CryptoSession max hdcp level supported: {}', drm_info['hdcp_level_max']) common.debug('Widevine CryptoSession algorithms: {}', self.crypto_session.GetPropertyString('algorithms'))
def parse_profiles(profiles_list_data): """Parse profile information from Netflix response""" try: profiles_list = OrderedDict(resolve_refs(profiles_list_data['profilesList'], profiles_list_data)) if not profiles_list: raise InvalidProfilesError('It has not been possible to obtain the list of profiles.') _delete_non_existing_profiles(profiles_list) sort_order = 0 for guid, profile in list(profiles_list.items()): common.debug('Parsing profile {}', guid) avatar_url = _get_avatar(profiles_list_data, profile) profile = profile['summary'] is_active = profile.pop('isActive') g.LOCAL_DB.set_profile(guid, is_active, sort_order) g.SHARED_DB.set_profile(guid, sort_order) for key, value in list(profile.items()): if key in PROFILE_DEBUG_INFO: common.debug('Profile info {}', {key: value}) if key == 'profileName': # The profile name is coded as HTML value = parse_html(value) g.LOCAL_DB.set_profile_config(key, value, guid) g.LOCAL_DB.set_profile_config('avatar', avatar_url, guid) sort_order += 1 except Exception: import traceback common.error(g.py2_decode(traceback.format_exc(), 'latin-1')) common.error('Profile list data: {}', profiles_list_data) raise InvalidProfilesError
def make_http_call(callname, data): """Make an IPC call via HTTP and wait for it to return. The contents of data will be expanded to kwargs and passed into the target function.""" from collections import OrderedDict try: # Python 3 from urllib.request import build_opener, install_opener, ProxyHandler, HTTPError, URLError, urlopen except ImportError: # Python 2 from urllib2 import build_opener, install_opener, ProxyHandler, HTTPError, URLError, urlopen import json debug('Handling HTTP IPC call to {}'.format(callname)) # Note: On python 3, using 'localhost' slowdown the call (Windows OS is affected) not sure if it is an urllib issue url = 'http://127.0.0.1:{}/{}'.format( g.LOCAL_DB.get_value('ns_service_port', 8001), callname) install_opener(build_opener(ProxyHandler( {}))) # don't use proxy for localhost try: result = json.loads(urlopen(url=url, data=json.dumps(data).encode('utf-8'), timeout=IPC_TIMEOUT_SECS).read(), object_pairs_hook=OrderedDict) except HTTPError as exc: result = json.loads(exc.reason) except URLError as exc: # On PY2 the exception message have to be decoded with latin-1 for system with symbolic characters err_msg = g.py2_decode(str(exc), 'latin-1') if '10049' in err_msg: err_msg += '\r\nPossible cause is wrong localhost settings in your operative system.' error(err_msg) raise BackendNotReady(g.py2_encode(err_msg, encoding='latin-1')) _raise_for_error(callname, result) return result
def make_http_call_cache(callname, params, data): """Make an IPC call via HTTP and wait for it to return. The contents of data will be expanded to kwargs and passed into the target function.""" try: # Python 3 from urllib.request import build_opener, install_opener, ProxyHandler, HTTPError, URLError, Request, urlopen except ImportError: # Python 2 from urllib2 import build_opener, install_opener, ProxyHandler, HTTPError, URLError, Request, urlopen import json # debug('Handling HTTP IPC call to {}'.format(callname)) # Note: On python 3, using 'localhost' slowdown the call (Windows OS is affected) not sure if it is an urllib issue url = 'http://127.0.0.1:{}/{}'.format( g.LOCAL_DB.get_value('cache_service_port', 8002), callname) install_opener(build_opener(ProxyHandler( {}))) # don't use proxy for localhost r = Request(url=url, data=data, headers={'Params': json.dumps(params)}) try: result = urlopen(r, timeout=IPC_TIMEOUT_SECS).read() except HTTPError as exc: try: raise apierrors.__dict__[exc.reason]() except KeyError: raise Exception('The service has returned: {}'.format(exc.reason)) except URLError as exc: # On PY2 the exception message have to be decoded with latin-1 for system with symbolic characters err_msg = g.py2_decode(str(exc), 'latin-1') if '10049' in err_msg: err_msg += '\r\nPossible cause is wrong localhost settings in your operative system.' error(err_msg) raise BackendNotReady(g.py2_encode(err_msg, encoding='latin-1')) return result
def onNotification(self, sender, method, data): # pylint: disable=unused-argument """ Callback for Kodi notifications that handles and dispatches playback events """ if not self.tracking: return try: if method == 'Player.OnAVStart': # WARNING: Do not get playerid from 'data', # Because when Up Next add-on play a video while we are inside Netflix add-on and # not externally like Kodi library, the playerid become -1 this id does not exist self._on_playback_started() elif method == 'Player.OnSeek': self._on_playback_seek() elif method == 'Player.OnPause': self._on_playback_pause() elif method == 'Player.OnResume': self._on_playback_resume() elif method == 'Player.OnStop': # When Up Next add-on starts the next video, the 'Player.OnStop' notification WILL BE NOT PERFORMED # then is manually generated by _play_callback method self._on_playback_stopped() except Exception: # pylint: disable=broad-except import traceback common.error(g.py2_decode(traceback.format_exc(), 'latin-1'))
def get_upnext_info(videoid, current_episode, metadata): """Determine next episode and send an AddonSignal to UpNext addon""" try: next_episode_id = _find_next_episode(videoid, metadata) except (TypeError, KeyError): # import traceback # common.debug(traceback.format_exc()) common.debug('There is no next episode, not setting up Up Next') return {} common.debug('Next episode is {}', next_episode_id) next_episode = infolabels.get_info_for_playback(next_episode_id, True) next_info = { 'current_episode': _upnext_info(videoid, *current_episode), 'next_episode': _upnext_info(next_episode_id, *next_episode) } if (xbmc.getInfoLabel('Container.PluginName') != g.ADDON.getAddonInfo('id') and library.is_in_library(next_episode_id)): filepath = g.SHARED_DB.get_episode_filepath(next_episode_id.tvshowid, next_episode_id.seasonid, next_episode_id.episodeid) # next_info['play_info'] = {'play_path': g.py2_decode(xbmc.translatePath(filepath))} next_info['play_url'] = g.py2_decode(xbmc.translatePath(filepath)) else: # next_info['play_info'] = {'play_path': common.build_url(videoid=next_episode_id, # mode=g.MODE_PLAY)} next_info['play_url'] = common.build_url(videoid=next_episode_id, mode=g.MODE_PLAY) if 'creditsOffset' in metadata[0]: next_info['notification_offset'] = metadata[0]['creditsOffset'] return next_info
def export_item(item_task, library_home): """Create strm file for an item and add it to the library""" # Paths must be legal to ensure NFS compatibility destination_folder = g.py2_decode(xbmc.makeLegalFilename('/'.join( [library_home, item_task['section'], item_task['destination']]))) _create_destination_folder(destination_folder) if item_task['is_strm']: export_filename = g.py2_decode(xbmc.makeLegalFilename('/'.join( [destination_folder, item_task['filename'] + '.strm']))) _add_to_library(item_task['videoid'], export_filename, (item_task['nfo_data'] is not None)) _write_strm_file(item_task, export_filename) if item_task['nfo_data'] is not None: nfo_filename = g.py2_decode(xbmc.makeLegalFilename('/'.join( [destination_folder, item_task['filename'] + '.nfo']))) _write_nfo_file(item_task['nfo_data'], nfo_filename) common.debug('Exported {}', item_task['title'])
def run(argv): # pylint: disable=broad-except,ungrouped-imports # Initialize globals right away to avoid stale values from the last addon invocation. # Otherwise Kodi's reuseLanguageInvoker will cause some really quirky behavior! # PR: https://github.com/xbmc/xbmc/pull/13814 g.init_globals(argv) reset_log_level_global_var() info('Started (Version {})'.format(g.VERSION_RAW)) info('URL is {}'.format(g.URL)) success = True window_cls = Window(10000) # Kodi home window # If you use multiple Kodi profiles you need to distinguish the property of current profile prop_nf_service_status = g.py2_encode('nf_service_status_' + get_current_kodi_profile_name()) is_external_call = _check_addon_external_call(window_cls, prop_nf_service_status) service_status = _get_service_status(window_cls, prop_nf_service_status) if service_status.get('status') != 'running': if not is_external_call: if service_status.get('status') == 'error': # The services are not started due to an error exception from resources.lib.kodi.ui import show_error_info show_error_info( get_local_string(30105), get_local_string(30240).format( service_status.get('message')), False, False) else: # The services are not started yet from resources.lib.kodi.ui import show_backend_not_ready show_backend_not_ready() success = False if success: try: if _check_valid_credentials(): if g.IS_ADDON_FIRSTRUN: if check_addon_upgrade(): from resources.lib.config_wizard import run_addon_configuration run_addon_configuration() route([part for part in g.PATH.split('/') if part]) else: success = False except BackendNotReady: from resources.lib.kodi.ui import show_backend_not_ready show_backend_not_ready() success = False except Exception as exc: import traceback from resources.lib.kodi.ui import show_addon_error_info error(g.py2_decode(traceback.format_exc(), 'latin-1')) show_addon_error_info(exc) success = False if not success: _handle_endofdirectory() log_time_trace()
def _login(self, modal_error_message=False): """Perform account login""" # If exists get the current esn value before extract a new session data current_esn = g.get_esn() try: # First we get the authentication url without logging in, required for login API call react_context = website.extract_json(self._get('login'), 'reactContext') auth_url = website.extract_api_data(react_context)['auth_url'] common.debug('Logging in...') login_response = self._post( 'login', data=_login_payload(common.get_credentials(), auth_url)) try: website.extract_session_data(login_response, validate=True, update_profiles=True) common.info('Login successful') ui.show_notification(common.get_local_string(30109)) self.update_session_data(current_esn) return True except (LoginValidateError, LoginValidateErrorIncorrectPassword) as exc: self.session.cookies.clear() common.purge_credentials() if not modal_error_message: raise ui.show_ok_dialog(common.get_local_string(30008), unicode(exc)) except InvalidMembershipStatusError: ui.show_error_info(common.get_local_string(30008), common.get_local_string(30180), False, True) except Exception: # pylint: disable=broad-except import traceback common.error(g.py2_decode(traceback.format_exc(), 'latin-1')) self.session.cookies.clear() raise return False
def extract_json(content, name): """Extract json from netflix content page""" common.debug('Extracting {} JSON', name) json_str = None try: json_array = recompile(JSON_REGEX.format(name), DOTALL).findall(content.decode('utf-8')) json_str = json_array[0] json_str_replace = json_str.replace('\\"', '\\\\"') # Escape double-quotes json_str_replace = json_str_replace.replace('\\s', '\\\\s') # Escape \s json_str_replace = json_str_replace.replace( '\\n', '\\\\n') # Escape line feed json_str_replace = json_str_replace.replace('\\t', '\\\\t') # Escape tab json_str_replace = json_str_replace.encode().decode( 'unicode_escape') # Decode the string as unicode json_str_replace = sub( r'\\(?!["])', r'\\\\', json_str_replace ) # Escape backslash (only when is not followed by double quotation marks \") return json.loads(json_str_replace) except Exception: if json_str: common.error('JSON string trying to load: {}', json_str) import traceback common.error(g.py2_decode(traceback.format_exc(), 'latin-1')) raise WebsiteParsingError('Unable to extract {}'.format(name))
def _load_msl_data(self, msl_data): try: self.crypto.load_msl_data(msl_data) self.crypto.load_crypto_session(msl_data) except Exception: # pylint: disable=broad-except import traceback common.error(g.py2_decode(traceback.format_exc(), 'latin-1'))
def validate_login(react_context): path_code_list = PAGE_ITEM_ERROR_CODE_LIST.split('\\') path_error_code = PAGE_ITEM_ERROR_CODE.split('/') if common.check_path_exists(path_error_code, react_context): # If the path exists, a login error occurs try: error_code_list = common.get_path(path_code_list, react_context) error_code = common.get_path(path_error_code, react_context) common.error('Login not valid, error code {}', error_code) error_description = common.get_local_string(30102) + error_code if error_code in error_code_list: error_description = error_code_list[error_code] if 'email_' + error_code in error_code_list: error_description = error_code_list['email_' + error_code] if 'login_' + error_code in error_code_list: error_description = error_code_list['login_' + error_code] if 'incorrect_password' in error_code: raise LoginValidateErrorIncorrectPassword( common.remove_html_tags(error_description)) raise LoginValidateError( common.remove_html_tags(error_description)) except (AttributeError, KeyError): import traceback common.error(g.py2_decode(traceback.format_exc(), 'latin-1')) error_msg = ( 'Something is wrong in PAGE_ITEM_ERROR_CODE or PAGE_ITEM_ERROR_CODE_LIST paths.' 'react_context data may have changed.') common.error(error_msg) raise LoginValidateError(error_msg)
def get_upnext_info(videoid, videoid_next_episode, info_data, metadata, is_played_from_addon): """Get the data to send to Up Next add-on""" upnext_info = { 'current_episode': _upnext_info(videoid, *info_data[videoid.value]), 'next_episode': _upnext_info(videoid_next_episode, *info_data[videoid_next_episode.value]) } if is_played_from_addon: url = common.build_url( videoid=videoid_next_episode, mode=g.MODE_PLAY, params={'profile_guid': g.LOCAL_DB.get_active_profile_guid()}) else: # Played from Kodi library get the strm file path file_path = g.SHARED_DB.get_episode_filepath( videoid_next_episode.tvshowid, videoid_next_episode.seasonid, videoid_next_episode.episodeid) url = g.py2_decode(xbmc.translatePath(file_path)) upnext_info['play_info'] = {'play_path': url} if 'creditsOffset' in metadata[0]: upnext_info['notification_offset'] = metadata[0]['creditsOffset'] return upnext_info
def autoselect_profile_set(self, pathitems): # pylint: disable=unused-argument """Save the GUID for profile auto-selection""" g.LOCAL_DB.set_value('autoselect_profile_guid', self.params['profile_guid']) g.settings_monitor_suspend(True) g.ADDON.setSetting('autoselect_profile_name', self.params['profile_name']) g.ADDON.setSettingBool('autoselect_profile_enabled', True) g.settings_monitor_suspend(False) ui.show_notification(common.get_local_string(30058).format(g.py2_decode(self.params['profile_name'])))
def try_refresh_session_data(self, raise_exception=False): """Refresh session_data from the Netflix website""" # pylint: disable=broad-except try: self.auth_url = website.extract_session_data( self._get('browse'))['auth_url'] self.update_session_data() common.debug('Successfully refreshed session data') return True except InvalidMembershipStatusError: raise except (WebsiteParsingError, InvalidMembershipStatusAnonymous, LoginValidateErrorIncorrectPassword) as exc: # Possible known causes: # -Cookies may not work anymore most likely due to updates in the website # -Login password has been changed # -Expired cookie profiles? might cause InvalidMembershipStatusAnonymous (i am not really sure) import traceback common.warn( 'Failed to refresh session data, login can be expired or the password has been changed ({})', type(exc).__name__) common.debug(g.py2_decode(traceback.format_exc(), 'latin-1')) self.session.cookies.clear() if isinstance(exc, (InvalidMembershipStatusAnonymous, LoginValidateErrorIncorrectPassword)): # This prevent the MSL error: No entity association record found for the user common.send_signal(signal=common.Signals.CLEAR_USER_ID_TOKENS) return self._login() except requests.exceptions.RequestException: import traceback common.warn( 'Failed to refresh session data, request error (RequestException)' ) common.warn(g.py2_decode(traceback.format_exc(), 'latin-1')) if raise_exception: raise except Exception: import traceback common.warn( 'Failed to refresh session data, login expired (Exception)') common.debug(g.py2_decode(traceback.format_exc(), 'latin-1')) self.session.cookies.clear() if raise_exception: raise return False
def _subtitle_profiles(): from xbmcaddon import Addon isa_version = g.remove_ver_suffix( g.py2_decode(Addon('inputstream.adaptive').getAddonInfo('version'))) subtitle_profile = ['webvtt-lssdh-ios8'] if g.ADDON.getSettingBool('disable_webvtt_subtitle') \ or not common.is_minimum_version(isa_version, '2.3.8'): subtitle_profile = ['simplesdh'] return subtitle_profile
def callback_event_video_queue(self, data=None): """Callback to add a video event""" try: self.add_event_to_queue(data['event_type'], data['event_data'], data['player_state']) except Exception as exc: # pylint: disable=broad-except import traceback from resources.lib.kodi.ui import show_addon_error_info common.error(g.py2_decode(traceback.format_exc(), 'latin-1')) show_addon_error_info(exc)
def ask_credentials(): """ Show some dialogs and ask the user for account credentials """ email = g.py2_decode( xbmcgui.Dialog().input(heading=common.get_local_string(30005), type=xbmcgui.INPUT_ALPHANUM)) or None common.verify_credentials(email) password = ask_for_password() common.verify_credentials(password) common.set_credentials(email, password)
def remove_item(item_task, library_home=None): """Remove an item from the library and delete if from disk""" # pylint: disable=unused-argument, broad-except common.info('Removing {} from library', item_task['title']) exported_filename = xbmc.translatePath(item_task['filepath']) videoid = item_task['videoid'] common.debug('VideoId: {}', videoid) try: parent_folder = xbmc.translatePath(os.path.dirname(exported_filename)) if xbmcvfs.exists(exported_filename): xbmcvfs.delete(exported_filename) else: common.warn('Cannot delete {}, file does not exist', g.py2_decode(exported_filename)) # Remove the NFO files if exists nfo_file = os.path.splitext( g.py2_decode(exported_filename))[0] + '.nfo' if xbmcvfs.exists(nfo_file): xbmcvfs.delete(nfo_file) dirs, files = xbmcvfs.listdir(parent_folder) tvshow_nfo_file = xbmc.makeLegalFilename('/'.join( [g.py2_decode(parent_folder), 'tvshow.nfo'])) # Remove tvshow_nfo_file only when is the last file # (users have the option of removing even single seasons) if xbmcvfs.exists(tvshow_nfo_file) and not dirs and len(files) == 1: xbmcvfs.delete(tvshow_nfo_file) # Delete parent folder xbmcvfs.rmdir(parent_folder) # Delete parent folder when empty if not dirs and not files: xbmcvfs.rmdir(parent_folder) _remove_videoid_from_db(videoid) except ItemNotFound: common.warn('The video with id {} not exists in the database', videoid) except Exception as exc: import traceback common.error(traceback.format_exc()) ui.show_addon_error_info(exc)
def try_refresh_session_data(self, raise_exception=False): """Refresh session_data from the Netflix website""" from requests import exceptions try: self.auth_url = website.extract_session_data( self._get('browse'))['auth_url'] cookies.save(self.account_hash, self.session.cookies) common.debug('Successfully refreshed session data') return True except InvalidMembershipStatusError: raise except (WebsiteParsingError, InvalidMembershipStatusAnonymous, LoginValidateErrorIncorrectPassword) as exc: import traceback common.warn( 'Failed to refresh session data, login can be expired or the password has been changed ({})', type(exc).__name__) common.debug(g.py2_decode(traceback.format_exc(), 'latin-1')) self.session.cookies.clear() if isinstance(exc, (InvalidMembershipStatusAnonymous, LoginValidateErrorIncorrectPassword)): # This prevent the MSL error: No entity association record found for the user common.send_signal(signal=common.Signals.CLEAR_USER_ID_TOKENS) return self._login() except exceptions.RequestException: import traceback common.warn( 'Failed to refresh session data, request error (RequestException)' ) common.warn(g.py2_decode(traceback.format_exc(), 'latin-1')) if raise_exception: raise except Exception: # pylint: disable=broad-except import traceback common.warn( 'Failed to refresh session data, login expired (Exception)') common.debug(g.py2_decode(traceback.format_exc(), 'latin-1')) self.session.cookies.clear() if raise_exception: raise return False
def _tick_and_wait_for_abort(self): try: self.controller.on_service_tick() self.library_updater.on_service_tick() g.CACHE_MANAGEMENT.on_service_tick() except Exception as exc: # pylint: disable=broad-except import traceback from resources.lib.kodi.ui import show_notification error(g.py2_decode(traceback.format_exc(), 'latin-1')) show_notification(': '.join( (exc.__class__.__name__, unicode(exc)))) return self.controller.waitForAbort(1)
def error_catching_wrapper(*args, **kwargs): try: return func(*args, **kwargs) except Exception as exc: if isinstance(exc, MSLError): message = g.py2_decode(str(exc)) else: message = str(exc) ui.show_error_info(common.get_local_string(30028), message, unknown_error=not message, netflix_error=isinstance(exc, MSLError)) raise
def convert_to_dash(manifest): """Convert a Netflix style manifest to MPEG-DASH manifest""" from xbmcaddon import Addon isa_version = g.remove_ver_suffix( g.py2_decode(Addon('inputstream.adaptive').getAddonInfo('version'))) # If a CDN server has stability problems it may cause errors with streaming, # we allow users to select a different CDN server # (should be managed by ISA but is currently is not implemented) cdn_index = int(g.ADDON.getSettingString('cdn_server')[-1]) - 1 seconds = manifest['duration'] / 1000 init_length = int(seconds / 2 * 12 + 20 * 1000) duration = "PT" + str(int(seconds)) + ".00S" root = _mpd_manifest_root(duration) period = ET.SubElement(root, 'Period', start='PT0S', duration=duration) has_video_drm_streams = manifest['video_tracks'][0].get( 'hasDrmStreams', False) video_protection_info = _get_protection_info( manifest['video_tracks'][0]) if has_video_drm_streams else None for video_track in manifest['video_tracks']: _convert_video_track(video_track, period, init_length, video_protection_info, has_video_drm_streams, cdn_index) common.fix_locale_languages(manifest['audio_tracks']) common.fix_locale_languages(manifest['timedtexttracks']) has_audio_drm_streams = manifest['audio_tracks'][0].get( 'hasDrmStreams', False) default_audio_language_index = _get_default_audio_language(manifest) for index, audio_track in enumerate(manifest['audio_tracks']): _convert_audio_track(audio_track, period, init_length, (index == default_audio_language_index), has_audio_drm_streams, cdn_index) default_subtitle_language_index = _get_default_subtitle_language(manifest) for index, text_track in enumerate(manifest['timedtexttracks']): if text_track['isNoneTrack']: continue _convert_text_track(text_track, period, (index == default_subtitle_language_index), cdn_index, isa_version) xml = ET.tostring(root, encoding='utf-8', method='xml') if common.is_debug_verbose(): common.save_file('manifest.mpd', xml) return xml.decode('utf-8').replace('\n', '').replace('\r', '').encode('utf-8')