Пример #1
0
    def login(self, credentials=None):
        """Perform account login"""
        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']
            LOG.debug('Logging in...')
            login_response = self.post(
                'login',
                headers={'Accept-Language': _get_accept_language_string(react_context)},
                data=_login_payload(credentials or common.get_credentials(), auth_url, react_context))

            website.extract_session_data(login_response, validate=True, update_profiles=True)
            if credentials:
                # Save credentials only when login has succeeded
                common.set_credentials(credentials)
            LOG.info('Login successful')
            ui.show_notification(common.get_local_string(30109))
            cookies.save(self.account_hash, self.session.cookies)
            return True
        except LoginValidateError as exc:
            self.session.cookies.clear()
            common.purge_credentials()
            raise_from(LoginError(unicode(exc)), exc)
        except (MbrStatusNeverMemberError, MbrStatusFormerMemberError) as exc:
            self.session.cookies.clear()
            LOG.warn('Membership status {} not valid for login', exc)
            raise_from(LoginError(common.get_local_string(30180)), exc)
        except Exception:  # pylint: disable=broad-except
            self.session.cookies.clear()
            import traceback
            LOG.error(G.py2_decode(traceback.format_exc(), 'latin-1'))
            raise
Пример #2
0
 def login_auth_data(self, data=None, password=None):
     """Perform account login with authentication data"""
     from requests import exceptions
     LOG.debug('Logging in with authentication data')
     # Add the cookies to the session
     self.session.cookies.clear()
     for cookie in data['cookies']:
         self.session.cookies.set(cookie[0], cookie[1], **cookie[2])
     cookies.log_cookie(self.session.cookies)
     # Try access to website
     try:
         website.extract_session_data(self.get('browse'),
                                      validate=True,
                                      update_profiles=True)
     except MbrStatusAnonymousError:
         # Access not valid
         return False
     # Get the account e-mail
     page_response = self.get('your_account').decode('utf-8')
     email_match = re.search(r'account-email[^<]+>([^<]+@[^</]+)</',
                             page_response)
     email = email_match.group(1).strip() if email_match else None
     if not email:
         raise WebsiteParsingError('E-mail field not found')
     # Verify the password (with parental control api)
     try:
         response = self.post_safe('profile_hub',
                                   data={
                                       'destination':
                                       'contentRestrictions',
                                       'guid':
                                       G.LOCAL_DB.get_active_profile_guid(),
                                       'password':
                                       password,
                                       'task':
                                       'auth'
                                   })
         if response.get('status') != 'ok':
             raise LoginError(common.get_local_string(
                 12344))  # 12344=Passwords entered did not match.
     except exceptions.HTTPError as exc:
         if exc.response.status_code == 500:
             # This endpoint raise HTTP error 500 when the password is wrong
             raise LoginError(common.get_local_string(12344)) from exc
         raise
     common.set_credentials({'email': email, 'password': password})
     LOG.info('Login successful')
     ui.show_notification(common.get_local_string(30109))
     cookies.save(self.session.cookies)
     return True
Пример #3
0
 def __init__(self):
     super().__init__()
     # Slot allocation for IPC
     self.slots = [
         self.get_safe, self.post_safe, self.login, self.login_auth_data,
         self.logout, self.path_request, self.perpetual_path_request,
         self.callpath_request, self.fetch_initial_page,
         self.refresh_session_data, self.activate_profile,
         self.parental_control_data, self.get_metadata,
         self.update_loco_context, self.update_videoid_bookmark,
         self.get_videoid_info
     ]
     # 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
             LOG.warn('Prefetch initial page failed: {}', exc)
Пример #4
0
    def activate_profile(self, guid):
        """Set the profile identified by guid as active"""
        LOG.debug('Switching to profile {}', guid)
        current_active_guid = G.LOCAL_DB.get_active_profile_guid()
        if guid == current_active_guid:
            LOG.info('The profile guid {} is already set, activation not needed.', guid)
            return
        if xbmc.Player().isPlayingVideo():
            # Change the current profile while a video is playing can cause problems with outgoing HTTP requests
            # (MSL/NFSession) causing a failure in the HTTP request or sending data on the wrong profile
            raise Warning('It is not possible select a profile while a video is playing.')
        timestamp = time.time()
        LOG.info('Activating profile {}', guid)
        # 20/05/2020 - The method 1 not more working for switching PIN locked profiles
        # INIT Method 1 - HTTP mode
        # response = self._get('switch_profile', params={'tkn': guid})
        # self.nfsession.auth_url = self.website_extract_session_data(response)['auth_url']
        # END Method 1
        # INIT Method 2 - API mode
        try:
            self.get_safe(endpoint='activate_profile',
                          params={'switchProfileGuid': guid,
                                  '_': int(timestamp * 1000),
                                  'authURL': self.auth_url})
        except HttpError401 as exc:
            # Profile guid not more valid
            raise InvalidProfilesError('Unable to access to the selected profile.') from exc
        # Retrieve browse page to update authURL
        response = self.get_safe('browse')
        self.auth_url = website.extract_session_data(response)['auth_url']
        # END Method 2

        G.LOCAL_DB.switch_active_profile(guid)
        G.CACHE_MANAGEMENT.identifier_prefix = guid
        cookies.save(self.session.cookies)
 def website_extract_session_data(self, content, **kwargs):
     """Extract session data and handle errors"""
     try:
         return website.extract_session_data(content, **kwargs)
     except WebsiteParsingError as exc:
         LOG.error('An error occurs in extract session data: {}', exc)
         raise
     except (LoginValidateError, MbrStatusAnonymousError) as exc:
         LOG.warn('The session data is not more valid ({})', type(exc).__name__)
         common.purge_credentials()
         self.session.cookies.clear()
         common.send_signal(signal=common.Signals.CLEAR_USER_ID_TOKENS)
         raise_from(NotLoggedInError, exc)
Пример #6
0
 def login(self, modal_error_message=True):
     """Perform account login"""
     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']
         LOG.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)
             LOG.info('Login successful')
             ui.show_notification(common.get_local_string(30109))
             cookies.save(self.account_hash, self.session.cookies)
             return True
         except LoginValidateError 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 (MbrStatusNeverMemberError, MbrStatusFormerMemberError):
             if not modal_error_message:
                 raise
             ui.show_error_info(common.get_local_string(30008),
                                common.get_local_string(30180), False, True)
     except Exception:  # pylint: disable=broad-except
         import traceback
         LOG.error(G.py2_decode(traceback.format_exc(), 'latin-1'))
         self.session.cookies.clear()
         raise
     return False
Пример #7
0
 def website_extract_session_data(self, content, **kwargs):
     """Extract session data and handle errors"""
     try:
         return website.extract_session_data(content, **kwargs)
     except WebsiteParsingError as exc:
         LOG.error('An error occurs in extract session data: {}', exc)
         raise
     except (LoginValidateError, MbrStatusAnonymousError) as exc:
         LOG.warn('The session data is not more valid ({})',
                  type(exc).__name__)
         common.purge_credentials()
         self.session.cookies.clear()
         # Clear the user ID tokens are tied to the credentials
         self.msl_handler.clear_user_id_tokens()
         raise NotLoggedInError from 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.session.cookies)
         LOG.debug('Successfully refreshed session data')
         return True
     except MbrStatusError:
         raise
     except (WebsiteParsingError, MbrStatusAnonymousError) as exc:
         import traceback
         LOG.warn(
             'Failed to refresh session data, login can be expired or the password has been changed ({})',
             type(exc).__name__)
         LOG.debug(G.py2_decode(traceback.format_exc(), 'latin-1'))
         self.session.cookies.clear()
         if isinstance(exc, MbrStatusAnonymousError):
             # This prevent the MSL error: No entity association record found for the user
             common.send_signal(signal=common.Signals.CLEAR_USER_ID_TOKENS)
         # Needed to do a new login
         common.purge_credentials()
         ui.show_notification(common.get_local_string(30008))
         raise_from(NotLoggedInError, exc)
     except exceptions.RequestException:
         import traceback
         LOG.warn(
             'Failed to refresh session data, request error (RequestException)'
         )
         LOG.warn(G.py2_decode(traceback.format_exc(), 'latin-1'))
         if raise_exception:
             raise
     except Exception:  # pylint: disable=broad-except
         import traceback
         LOG.warn(
             'Failed to refresh session data, login expired (Exception)')
         LOG.debug(G.py2_decode(traceback.format_exc(), 'latin-1'))
         self.session.cookies.clear()
         if raise_exception:
             raise
     return False
Пример #9
0
 def try_refresh_session_data(self, raise_exception=False):
     """Refresh session data from the Netflix website"""
     try:
         self.auth_url = website.extract_session_data(
             self.get('browse'))['auth_url']
         cookies.save(self.session.cookies.jar)
         LOG.debug('Successfully refreshed session data')
         return True
     except MbrStatusError:
         raise
     except (WebsiteParsingError, MbrStatusAnonymousError) as exc:
         import traceback
         LOG.warn(
             'Failed to refresh session data, login can be expired or the password has been changed ({})',
             type(exc).__name__)
         LOG.debug(traceback.format_exc())
         self.session.cookies.clear()
         if isinstance(exc, MbrStatusAnonymousError):
             # This prevent the MSL error: No entity association record found for the user
             self.msl_handler.clear_user_id_tokens()
         # Needed to do a new login
         common.purge_credentials()
         ui.show_notification(common.get_local_string(30008))
         raise NotLoggedInError from exc
     except httpx.RequestError:
         import traceback
         LOG.warn(
             'Failed to refresh session data, request error (RequestError)')
         LOG.warn(traceback.format_exc())
         if raise_exception:
             raise
     except Exception:  # pylint: disable=broad-except
         import traceback
         LOG.warn(
             'Failed to refresh session data, login expired (Exception)')
         LOG.debug(traceback.format_exc())
         self.session.cookies.clear()
         if raise_exception:
             raise
     return False
Пример #10
0
    def activate_profile(self, guid):
        """Set the profile identified by guid as active"""
        LOG.debug('Switching to profile {}', guid)
        current_active_guid = G.LOCAL_DB.get_active_profile_guid()
        if guid == current_active_guid:
            LOG.info(
                'The profile guid {} is already set, activation not needed.',
                guid)
            return
        timestamp = time.time()
        LOG.info('Activating profile {}', guid)
        # 20/05/2020 - The method 1 not more working for switching PIN locked profiles
        # INIT Method 1 - HTTP mode
        # response = self._get('switch_profile', params={'tkn': guid})
        # self.nfsession.auth_url = self.website_extract_session_data(response)['auth_url']
        # END Method 1
        # INIT Method 2 - API mode
        try:
            self.get_safe(endpoint='activate_profile',
                          params={
                              'switchProfileGuid': guid,
                              '_': int(timestamp * 1000),
                              'authURL': self.auth_url
                          })
        except HttpError401 as exc:
            # Profile guid not more valid
            raise_from(
                InvalidProfilesError(
                    'Unable to access to the selected profile.'), exc)
        # Retrieve browse page to update authURL
        response = self.get_safe('browse')
        self.auth_url = website.extract_session_data(response)['auth_url']
        # END Method 2

        G.LOCAL_DB.switch_active_profile(guid)
        G.CACHE_MANAGEMENT.identifier_prefix = guid
        cookies.save(self.session.cookies)
Пример #11
0
 def login_auth_data(self, data=None, password=None):
     """Perform account login with authentication data"""
     LOG.debug('Logging in with authentication data')
     # Add the cookies to the session
     self.session.cookies.clear()
     for cookie in data['cookies']:
         # The code below has been adapted from httpx.Cookies.set() method
         kwargs = {
             'version': 0,
             'name': cookie['name'],
             'value': cookie['value'],
             'port': None,
             'port_specified': False,
             'domain': cookie['domain'],
             'domain_specified': bool(cookie['domain']),
             'domain_initial_dot': cookie['domain'].startswith('.'),
             'path': cookie['path'],
             'path_specified': bool(cookie['path']),
             'secure': cookie['secure'],
             'expires': cookie['expires'],
             'discard': True,
             'comment': None,
             'comment_url': None,
             'rest': cookie['rest'],
             'rfc2109': False,
         }
         cookie = Cookie(**kwargs)
         self.session.cookies.jar.set_cookie(cookie)
     cookies.log_cookie(self.session.cookies.jar)
     # Try access to website
     try:
         website.extract_session_data(self.get('browse'),
                                      validate=True,
                                      update_profiles=True)
     except MbrStatusAnonymousError:
         # Access not valid
         return False
     # Get the account e-mail
     page_response = self.get('your_account').decode('utf-8')
     email_match = re.search(r'account-email[^<]+>([^<]+@[^</]+)</',
                             page_response)
     email = email_match.group(1).strip() if email_match else None
     if not email:
         raise WebsiteParsingError('E-mail field not found')
     # Verify the password (with parental control api)
     try:
         response = self.post_safe('profile_hub',
                                   data={
                                       'destination':
                                       'contentRestrictions',
                                       'guid':
                                       G.LOCAL_DB.get_active_profile_guid(),
                                       'password':
                                       password,
                                       'task':
                                       'auth'
                                   })
         if response.get('status') != 'ok':
             raise LoginError(common.get_local_string(
                 12344))  # 12344=Passwords entered did not match.
     except httpx.HTTPStatusError as exc:
         if exc.response.status_code == 500:
             # This endpoint raise HTTP error 500 when the password is wrong
             raise LoginError(common.get_local_string(12344)) from exc
         raise
     common.set_credentials({'email': email, 'password': password})
     LOG.info('Login successful')
     ui.show_notification(common.get_local_string(30109))
     cookies.save(self.session.cookies.jar)
     return True