def execute_library_task_gui(self, videoid, task_type, title, nfo_settings=None, show_prg_dialog=True): """ Execute a library task for a videoid, by showing a GUI progress bar/dialog :param videoid: the videoid :param task_type: the type of task for the jobs (same used to execute the jobs) :param title: title for the progress dialog/background progress bar :param nfo_settings: the NFOSettings object containing the user's NFO settings :param show_prg_dialog: if True show progress dialog, otherwise, a background progress bar """ list_errors = [] # Preparation of jobs data for the task jobs_data = self.compile_jobs_data(videoid, task_type, nfo_settings) # Set a progress bar progress_class = ui.ProgressDialog if show_prg_dialog else ui.ProgressBarBG with progress_class(show_prg_dialog, title, max_value=len(jobs_data)) as progress_bar: # Execute the jobs for the task for job_data in jobs_data: self._execute_job(task_type, job_data, list_errors) progress_bar.perform_step() progress_bar.set_message('{} ({}/{})'.format( job_data['title'], progress_bar.value, progress_bar.max_value)) if progress_bar.is_cancelled(): common.warn('Library operations interrupted by User') break if self.monitor.abortRequested(): common.warn('Library operations interrupted by Kodi') break show_library_task_errors(show_prg_dialog, list_errors)
def add_event_to_queue(self, event_type, event_data, player_state): """Adds an event in the queue of events to be processed""" videoid = common.VideoId.from_dict(event_data['videoid']) # pylint: disable=unused-variable previous_data, previous_player_state = self.cache_data_events.get( videoid.value, ({}, None)) manifest = get_manifest(videoid) url = manifest['links']['events']['href'] if previous_data.get('xid') in self.banned_events_ids: common.warn( 'EVENT [{}] - Not added to the queue. The xid {} is banned due to a previous failed request', event_type, previous_data.get('xid')) return from resources.lib.services.msl.msl_request_builder import MSLRequestBuilder request_data = MSLRequestBuilder.build_request_data( url, self._build_event_params(event_type, event_data, player_state, manifest)) try: self.queue_events.put_nowait(Event(request_data, event_data)) except queue.Full: common.warn( 'EVENT [{}] - Not added to the queue. The event queue is full.', event_type)
def compile_tasks(videoid, task_handler, nfo_settings=None): """Compile a list of tasks for items based on the videoid""" common.debug('Compiling library tasks for {}', videoid) task = None try: if task_handler == export_item: metadata = api.metadata(videoid) if videoid.mediatype == common.VideoId.MOVIE: task = _create_export_movie_task(videoid, metadata[0], nfo_settings) elif videoid.mediatype in common.VideoId.TV_TYPES: task = _create_export_tv_tasks(videoid, metadata, nfo_settings) else: raise ValueError('Cannot handle {}'.format(videoid)) if task_handler == export_new_item: metadata = api.metadata(videoid, True) task = _create_new_episodes_tasks(videoid, metadata, nfo_settings) if task_handler == remove_item: if videoid.mediatype == common.VideoId.MOVIE: task = _create_remove_movie_task(videoid) if videoid.mediatype == common.VideoId.SHOW: task = _compile_remove_tvshow_tasks(videoid) if videoid.mediatype == common.VideoId.SEASON: task = _compile_remove_season_tasks(videoid) if videoid.mediatype == common.VideoId.EPISODE: task = _create_remove_episode_task(videoid) except MetadataNotAvailable: common.warn('compile_tasks: task_handler {} unavailable metadata for {} task skipped', task_handler, videoid) return [{}] if task is None: common.warn('compile_tasks: task_handler {} did not match any task for {}', task_handler, videoid) return task
def _compute_next_schedule(): try: if g.ADDON.getSettingBool('use_mysql'): client_uuid = g.LOCAL_DB.get_value('client_uuid') uuid = g.SHARED_DB.get_value('auto_update_device_uuid') if client_uuid != uuid: common.debug( 'The auto update has been disabled because another device ' 'has been set as the main update manager') return None time = g.ADDON.getSetting('lib_auto_upd_start') or '00:00' last_run = g.SHARED_DB.get_value('library_auto_update_last_start', datetime.utcfromtimestamp(0)) update_frequency = g.ADDON.getSettingInt('lib_auto_upd_freq') last_run = last_run.replace(hour=int(time[0:2]), minute=int(time[3:5])) next_run = last_run + timedelta(days=[1, 2, 5, 7][update_frequency]) if next_run >= datetime.now(): common.info('Next library auto update is scheduled for {}', next_run) return next_run except Exception: # pylint: disable=broad-except # If settings.xml was not created yet, as at first service run # g.ADDON.getSettingBool('use_mysql') will thrown a TypeError # If any other error appears, we don't want the service to crash, # let's return None in all case # import traceback # common.debug(g.py2_decode(traceback.format_exc(), 'latin-1')) common.warn('Managed error at _compute_next_schedule') return None
def _request(self, method, component, session_refreshed, **kwargs): url = (_api_url(component) if URLS[component]['is_api_call'] else _document_url(component)) common.debug('Executing {verb} request to {url}', verb='GET' if method == self.session.get else 'POST', url=url) data, headers, params = self._prepare_request_properties(component, kwargs) start = time.clock() response = method( url=url, verify=self.verify_ssl, headers=headers, params=params, data=data) common.debug('Request took {}s', time.clock() - start) common.debug('Request returned statuscode {}', response.status_code) if response.status_code in [404, 401] and not session_refreshed: # 404 - It may happen when Netflix update the build_identifier version and causes the api address to change # 401 - It may happen when authURL is not more valid (Unauthorized for url) # So let's try refreshing the session data (just once) common.warn('Try refresh session data due to {} http error', response.status_code) if self.try_refresh_session_data(): return self._request(method, component, True, **kwargs) response.raise_for_status() return (_raise_api_error(response.json() if response.content else {}) if URLS[component]['is_api_call'] else response.content)
def get_resume_info_from_library(videoid): """Retrieve the resume value from the Kodi library""" try: return get_info_from_library(videoid)[0].get('resume', {}) except library.ItemNotFound: common.warn('Can not get resume value from the library') return {}
def _verify_session_cookies(self): """Verify that the session cookies have not expired""" # pylint: disable=broad-except fallback_to_validate = False if not self.session.cookies: return False for cookie_name in LOGIN_COOKIES: if cookie_name not in list(self.session.cookies.keys()): common.error( 'The cookie "{}" do not exist. It is not possible to check expiration. ' 'Fallback to old validate method.', cookie_name) fallback_to_validate = True break for cookie in list(self.session.cookies): if cookie.name != cookie_name: continue if cookie.expires <= int(time.time()): common.info('Login is expired') return False if fallback_to_validate: # Old method, makes a request at every time an user change page on Kodi # and try to re-extract all, working but slows down navigation. # If we can get session data, cookies are still valid try: website.validate_session_data(self._get('profiles')) self.update_session_data() except Exception: import traceback common.warn( 'Failed to validate session data, login is expired') common.debug(traceback.format_exc()) self.session.cookies.clear() return False return True
def _request(self, method, component, session_refreshed, **kwargs): url = (_api_url(component) if URLS[component]['is_api_call'] else _document_url(component)) common.debug('Executing {verb} request to {url}', verb='GET' if method == self.session.get else 'POST', url=url) data, headers, params = self._prepare_request_properties( component, kwargs) start = time.clock() response = method(url=url, verify=self.verify_ssl, headers=headers, params=params, data=data) common.debug('Request took {}s', time.clock() - start) common.debug('Request returned statuscode {}', response.status_code) if response.status_code == 404 and not session_refreshed: # It may happen that netflix updates the build_identifier version # when you are watching a video or browsing the menu, # this causes the api address to change, and return 'url not found' error # So let's try updating the session data (just once) common.warn( 'Try refresh session data to update build_identifier version') if self._refresh_session_data(): return self._request(method, component, True, **kwargs) response.raise_for_status() return (_raise_api_error(response.json() if response.content else {}) if URLS[component]['is_api_call'] else response.content)
def parental_control_data(self, password): # Ask to the service if password is right and get the PIN status try: pin_response = self._post('pin_reset', data={ 'task': 'auth', 'authURL': self.auth_url, 'password': password }) if pin_response.get('status') != 'ok': common.warn('Parental control status issue: {}', pin_response) raise MissingCredentialsError pin = pin_response.get('pin') except requests.exceptions.HTTPError as exc: if exc.response.status_code == 401: # Unauthorized for url ... raise MissingCredentialsError raise # Parse web page to get the current maturity level # I have not found how to get it through the API pin_response = self._get('pin', data={'password': password}) # so counts the number of occurrences of the "maturity-input-item included" class from re import findall num_items = len( findall(r'<div class="maturity-input-item.*?included.*?<\/div>', pin_response.decode('utf-8'))) if not num_items: raise WebsiteParsingError('Unable to find maturity level div tag') return {'pin': pin, 'maturity_level': num_items - 1}
def _home_autoselect_profile(self): """ Show home listing if profile auto-selection is enabled :return: True when the auto-selection is done correctly """ autoselect_profile_guid = g.LOCAL_DB.get_value( 'autoselect_profile_guid', '') if autoselect_profile_guid: # Check if the GUID still exists in the profile list if autoselect_profile_guid not in g.LOCAL_DB.get_guid_profiles(): common.warn( 'Auto-selection of profile not performed, the GUID {} not exist', autoselect_profile_guid) g.LOCAL_DB.set_value('autoselect_profile_guid', '') g.settings_monitor_suspend(True) g.ADDON.setSetting('autoselect_profile_name', '') g.ADDON.setSettingBool('autoselect_profile_enabled', False) g.settings_monitor_suspend(False) else: common.info('Performing auto-selection of profile {}', autoselect_profile_guid) api.activate_profile(autoselect_profile_guid) self.home(None, False) return True return False
def _refresh_session_data(self): """Refresh session_data from the Netflix website""" # pylint: disable=broad-except try: website.extract_session_data(self._get('profiles')) self.update_session_data() except InvalidMembershipStatusError: raise except WebsiteParsingError: # it is possible that cookies may not work anymore, # it should be due to updates in the website, # this can happen when opening the addon while executing update_profiles_data import traceback common.debug(traceback.format_exc()) common.warn( 'Failed to refresh session data, login expired (WebsiteParsingError)' ) self.session.cookies.clear() return self._login() except Exception: import traceback common.debug(traceback.format_exc()) common.warn( 'Failed to refresh session data, login expired (Exception)') self.session.cookies.clear() return False common.debug('Successfully refreshed session data') return True
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 __init__(self): super(NFSessionOperations, self).__init__() # Slot allocation for IPC self.slots = [ self.get_safe, self.post_safe, self.login, self.logout, self.path_request, self.perpetual_path_request, self.callpath_request, self.fetch_initial_page, self.activate_profile, self.parental_control_data, self.get_metadata, self.update_loco_context, self.update_videoid_bookmark ] # Share the activate profile function to SessionBase class self.external_func_activate_profile = self.activate_profile self.dt_initial_page_prefetch = None # Try prefetch login if self.prefetch_login(): try: # Try prefetch initial page response = self.get_safe('browse') api_data = website.extract_session_data(response, update_profiles=True) self.auth_url = api_data['auth_url'] self.dt_initial_page_prefetch = datetime.now() except Exception as exc: # pylint: disable=broad-except common.warn('Prefetch initial page failed: {}', exc)
def _get_avatar(profiles_list_data, profile): try: return common.get_path(profile['avatar'] + AVATAR_SUBPATH, profiles_list_data) except (KeyError, TypeError): common.warn('Cannot find avatar for profile {}', profile['summary']['guid']) common.debug('Profile list data: {}', profiles_list_data) return g.ICON
def initialize(self, data): if not data['event_data']: common.warn('AMVideoEvents: disabled due to no event data') self.enabled = False return self.event_data = data['event_data'] self.videoid = common.VideoId.from_dict(data['videoid'])
def rate_thumb(self, videoid): """Rate an item on Netflix. Ask for a thumb rating""" # Get updated user rating info for this videoid from resources.lib.api.paths import VIDEO_LIST_RATING_THUMB_PATHS video_list = api.custom_video_list([videoid.value], VIDEO_LIST_RATING_THUMB_PATHS) if video_list.videos: videoid_value, video_data = list(video_list.videos.items())[0] # pylint: disable=unused-variable title = video_data.get('title') track_id_jaw = video_data.get('trackIds', {})['trackId_jaw'] is_thumb_rating = video_data.get('userRating', {}).get('type', '') == 'thumb' user_rating = video_data.get('userRating', {}).get('userRating') \ if is_thumb_rating else None ui.show_modal_dialog(False, ui.xmldialogs.RatingThumb, 'plugin-video-netflix-RatingThumb.xml', g.ADDON.getAddonInfo('path'), videoid=videoid, title=title, track_id_jaw=track_id_jaw, user_rating=user_rating) else: common.warn( 'Rating thumb video list api request no got results for {}', videoid)
def _delete_non_existing_profiles(current_guids): list_guid = g.LOCAL_DB.get_guid_profiles() for guid in list_guid: if guid not in current_guids: common.debug('Deleting non-existing profile {}', guid) g.LOCAL_DB.delete_profile(guid) g.SHARED_DB.delete_profile(guid) # Ensures at least one active profile try: g.LOCAL_DB.get_active_profile_guid() except ProfilesMissing: g.LOCAL_DB.switch_active_profile(g.LOCAL_DB.get_guid_owner_profile()) g.settings_monitor_suspend(True) # Verify if auto select profile exists autoselect_profile_guid = g.LOCAL_DB.get_value('autoselect_profile_guid', '') if autoselect_profile_guid and autoselect_profile_guid not in current_guids: common.warn('Auto-selection disabled, the GUID {} not more exists', autoselect_profile_guid) g.LOCAL_DB.set_value('autoselect_profile_guid', '') g.ADDON.setSetting('autoselect_profile_name', '') g.ADDON.setSettingBool('autoselect_profile_enabled', False) # Verify if profile for library playback exists library_playback_profile_guid = g.LOCAL_DB.get_value( 'library_playback_profile_guid') if library_playback_profile_guid and library_playback_profile_guid not in current_guids: common.warn( 'Profile set for playback from library cleared, the GUID {} not more exists', library_playback_profile_guid) # Save the selected profile guid g.LOCAL_DB.set_value('library_playback_profile_guid', '') # Save the selected profile name g.ADDON.setSetting('library_playback_profile', '') g.settings_monitor_suspend(False)
def parental_control_data(self, password): # Ask to the service if password is right and get the PIN status try: pin_response = self._post('pin_reset', data={ 'task': 'auth', 'authURL': self.auth_url, 'password': password }) if pin_response.get('status') != 'ok': common.warn('Parental control status issue: {}', pin_response) raise MissingCredentialsError pin = pin_response.get('pin') except requests.exceptions.HTTPError as exc: if exc.response.status_code == 401: # Unauthorized for url ... raise MissingCredentialsError raise # Warning - parental control levels vary by country or region, no fixed values can be used # I have not found how to get it through the API, so parse web page to get all info # Note: The language of descriptions change in base of the language of selected profile response_content = self._get('pin', data={'password': password}) extracted_content = website.extract_parental_control_data( response_content) extracted_content['pin'] = pin return extracted_content
def parental_control_data(self, password): # Ask to the service if password is right and get the PIN status from requests import exceptions profile_guid = g.LOCAL_DB.get_active_profile_guid() try: response = self._post('profile_hub', data={ 'destination': 'contentRestrictions', 'guid': profile_guid, 'password': password, 'task': 'auth' }) if response.get('status') != 'ok': common.warn('Parental control status issue: {}', response) raise MissingCredentialsError except exceptions.HTTPError as exc: if exc.response.status_code == 500: # This endpoint raise HTTP error 500 when the password is wrong raise MissingCredentialsError raise # Warning - parental control levels vary by country or region, no fixed values can be used # Note: The language of descriptions change in base of the language of selected profile response_content = self._get('restrictions', data={'password': password}, append_to_address=profile_guid) extracted_content = website.extract_parental_control_data( response_content, response['maturity']) response['profileInfo']['profileName'] = website.parse_html( response['profileInfo']['profileName']) extracted_content['data'] = response return extracted_content
def rate_thumb(self, videoid): """Rate an item on Netflix. Ask for a thumb rating""" # Get updated user rating info for this videoid raw_data = api.get_video_raw_data([videoid], VIDEO_LIST_RATING_THUMB_PATHS) if raw_data.get('videos', {}).get(videoid.value): video_data = raw_data['videos'][videoid.value] title = video_data.get('title') track_id_jaw = video_data.get('trackIds', {})['trackId_jaw'] is_thumb_rating = video_data.get('userRating', {}).get('type', '') == 'thumb' user_rating = video_data.get( 'userRating', {}).get('userRating') if is_thumb_rating else None ui.show_modal_dialog(False, ui.xmldialogs.RatingThumb, 'plugin-video-netflix-RatingThumb.xml', g.ADDON.getAddonInfo('path'), videoid=videoid, title=title, track_id_jaw=track_id_jaw, user_rating=user_rating) else: common.warn( 'Rating thumb video list api request no got results for {}', videoid)
def _persist_bucket(self, bucket, contents): if not self.is_safe_to_persist(bucket): common.warn( '{} is locked by another instance. Discarding changes'.format( bucket)) return try: if g.PY_IS_VER2: self.window.setProperty(self._window_property(bucket), pickle.dumps(contents)) else: # Note: On python 3 pickle.dumps produces byte not str cannot be passed as is in # setProperty because cannot receive arbitrary byte sequences if they contain # null bytes \x00, the stored value will be truncated by this null byte (Kodi bug). # To store pickled data in Python 3, you should use protocol 0 explicitly and decode # the resulted value with latin-1 encoding to str and then pass it to setPropety. self.window.setProperty( self._window_property(bucket), pickle.dumps(contents, protocol=0).decode('latin-1')) except Exception as exc: # pylint: disable=broad-except common.error('Failed to persist {} to wnd properties: {}', bucket, exc) self.window.clearProperty(self._window_property(bucket)) finally: common.debug('Released lock on {}', bucket)
def update_lolomo_context(context_name): """Update the lolomo list by context""" # 01/06/2020: refreshListByContext often return HTTP error 500, currently i have seen that in the website is # performed only when the video played is not added to "my list", but with a strange mixed data: # context_id: the id of continueWatching # context_index: that seem to point to "My list" id context index # This api is no more needed to update the continueWatching lolomo list lolomo_root = g.LOCAL_DB.get_value('lolomo_root_id', '', TABLE_SESSION) context_index = g.LOCAL_DB.get_value( 'lolomo_{}_index'.format(context_name.lower()), '', TABLE_SESSION) context_id = g.LOCAL_DB.get_value( 'lolomo_{}_id'.format(context_name.lower()), '', TABLE_SESSION) if not context_index: common.warn( 'Update lolomo context {} skipped due to missing lolomo index', context_name) return path = [['lolomos', lolomo_root, 'refreshListByContext']] # The fourth parameter is like a request-id, but it doesn't seem to match to # serverDefs/date/requestId of reactContext (g.LOCAL_DB.get_value('request_id', table=TABLE_SESSION)) # nor to request_id of the video event request # has a kind of relationship with renoMessageId suspect with the logblob but i'm not sure because my debug crashed, # and i am no longer able to trace the source. # I noticed also that this request can also be made with the fourth parameter empty, # but it still doesn't update the continueWatching list of lolomo, that is strange because of no error params = [ common.enclose_quotes(context_id), context_index, common.enclose_quotes(context_name), '' ] # path_suffixs = [ # [['trackIds', 'context', 'length', 'genreId', 'videoId', 'displayName', 'isTallRow', 'isShowAsARow', # 'impressionToken', 'showAsARow', 'id', 'requestId']], # [{'from': 0, 'to': 100}, 'reference', 'summary'], # [{'from': 0, 'to': 100}, 'reference', 'title'], # [{'from': 0, 'to': 100}, 'reference', 'titleMaturity'], # [{'from': 0, 'to': 100}, 'reference', 'userRating'], # [{'from': 0, 'to': 100}, 'reference', 'userRatingRequestId'], # [{'from': 0, 'to': 100}, 'reference', 'boxarts', '_342x192', 'jpg'], # [{'from': 0, 'to': 100}, 'reference', 'promoVideo'] # ] callargs = { 'callpaths': path, 'params': params, # 'path_suffixs': path_suffixs } try: response = common.make_http_call('callpath_request', callargs) common.debug('refreshListByContext response: {}', response) # The call response return the new context id of the previous invalidated lolomo context_id # and if path_suffixs is added return also the new video list data except Exception: # pylint: disable=broad-except if not common.is_debug_verbose(): return ui.show_notification( title=common.get_local_string(30105), msg='An error prevented the update the lolomo context on netflix', time=10000)
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(traceback.format_exc()) 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: # I don't know if this is really the cause, but seem that using Kodi debug build with a # InputStream Adaptive release build (yes users do it) cause problems raise MSLError( 'It was not possible to get the data from Widevine CryptoSession, ' 'try to reinstall Kodi with latest version') 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 _get_avatar(profile_data, data, guid): try: avatar = jgraph_get('avatar', profile_data, data) return jgraph_get_path(AVATAR_SUBPATH, avatar) except (KeyError, TypeError): common.warn('Cannot find avatar for profile {}', guid) common.debug('Profile list data: {}', profile_data) return g.ICON
def _get_avatar(falkor_cache, profile): try: profile['avatar']['value'].extend(AVATAR_SUBPATH) return common.get_path(profile['avatar']['value'], falkor_cache) except KeyError: common.warn('Cannot find avatar for profile {guid}'.format( guid=profile['summary']['value']['guid'])) return ''
def play(videoid): """Play an episode or movie as specified by the path""" common.info('Playing {}', videoid) is_up_next_enabled = g.ADDON.getSettingBool('UpNextNotifier_enabled') metadata = [{}, {}] try: metadata = api.metadata(videoid) common.debug('Metadata is {}', metadata) except MetadataNotAvailable: common.warn('Metadata not available for {}', videoid) # Parental control PIN pin_result = _verify_pin(metadata[0].get('requiresPin', False)) if not pin_result: if pin_result is not None: ui.show_notification(common.get_local_string(30106), time=10000) xbmcplugin.endOfDirectory(g.PLUGIN_HANDLE, succeeded=False) return list_item = get_inputstream_listitem(videoid) infos, art = infolabels.add_info_for_playback(videoid, list_item, is_up_next_enabled) # Workaround for resuming strm files from library resume_position = infos.get('resume', {}).get('position') \ if g.IS_SKIN_CALL and g.ADDON.getSettingBool('ResumeManager_enabled') else None if resume_position: index_selected = ui.ask_for_resume( resume_position) if g.ADDON.getSettingBool( 'ResumeManager_dialog') else None if index_selected == -1: xbmcplugin.setResolvedUrl(handle=g.PLUGIN_HANDLE, succeeded=False, listitem=list_item) return if index_selected == 1: resume_position = None xbmcplugin.setResolvedUrl(handle=g.PLUGIN_HANDLE, succeeded=True, listitem=list_item) upnext_info = get_upnext_info(videoid, (infos, art), metadata) if is_up_next_enabled else None common.debug('Sending initialization signal') common.send_signal(common.Signals.PLAYBACK_INITIATED, { 'videoid': videoid.to_dict(), 'infos': infos, 'art': art, 'timeline_markers': get_timeline_markers(metadata[0]), 'upnext_info': upnext_info, 'resume_position': resume_position }, non_blocking=True) xbmcplugin.setResolvedUrl(handle=g.PLUGIN_HANDLE, succeeded=True, listitem=list_item)
def onSettingsChanged(self): status = g.settings_monitor_suspend_status() if status == 'First': common.warn('SettingsMonitor: triggered but in suspend status (at first change)') g.settings_monitor_suspend(False) return if status == 'True': common.warn('SettingsMonitor: triggered but in suspend status (permanent)') return self._on_change()
def _send_event(self, event_type, event_data, player_state): if not player_state: common.warn('AMVideoEvents: the event [{}] cannot be sent, missing player_state data', event_type) return event_data['allow_request_update_loco'] = self.allow_request_update_loco common.send_signal(common.Signals.QUEUE_VIDEO_EVENT, { 'event_type': event_type, 'event_data': event_data, 'player_state': player_state }, non_blocking=True)
def _update_running(): update = g.ADDON.getSetting('update_running') or None if update: starttime = common.strp(update, '%Y-%m-%d %H:%M') if (starttime + timedelta(hours=6)) <= datetime.now(): g.ADDON.setSetting('update_running', 'false') common.warn('Canceling previous library update: duration >6 hours') else: common.debug('DB Update already running') return True return False
def _is_auto_update_library_running(): update = g.SHARED_DB.get_value('library_auto_update_is_running', False) if update: start_time = g.SHARED_DB.get_value('library_auto_update_start_time', datetime.utcfromtimestamp(0)) if datetime.now() >= start_time + timedelta(hours=6): g.SHARED_DB.set_value('library_auto_update_is_running', False) common.warn('Canceling previous library update: duration >6 hours') else: common.debug('Library auto update is already running') return True return False