Example #1
0
 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)
Example #6
0
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 {}
Example #7
0
 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
Example #8
0
 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}
Example #10
0
 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
Example #12
0
    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'))
Example #13
0
 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)
Example #14
0
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
Example #15
0
 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'])
Example #16
0
 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)
Example #18
0
 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
Example #20
0
 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)
Example #22
0
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
Example #25
0
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 ''
Example #26
0
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()
Example #28
0
 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)
Example #29
0
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