Exemple #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
Exemple #2
0
 def root(self, pathitems=None):  # pylint: disable=unused-argument
     """Show profiles or home listing when profile auto-selection is enabled"""
     # Fetch initial page to refresh all session data
     current_directory = common.WndHomeProps[
         common.WndHomeProps.CURRENT_DIRECTORY]
     if not current_directory:
         # Note when the profiles are updated to the database (by fetch_initial_page call),
         # the update sanitize also relative settings to profiles (see _delete_non_existing_profiles in website.py)
         common.make_call('fetch_initial_page')
     # When the add-on is used in a browser window, we do not have to execute the auto profile selection
     if not G.IS_ADDON_EXTERNAL_CALL:
         autoselect_profile_guid = G.LOCAL_DB.get_value(
             'autoselect_profile_guid', '')
         if autoselect_profile_guid and not common.WndHomeProps[
                 common.WndHomeProps.IS_CONTAINER_REFRESHED]:
             if not current_directory:
                 LOG.info('Performing auto-selection of profile {}',
                          autoselect_profile_guid)
                 self.params[
                     'switch_profile_guid'] = autoselect_profile_guid
             self.home(None)
             return
     list_data, extra_data = common.make_call('get_profiles',
                                              {'request_update': False})
     self._profiles(list_data, extra_data)
Exemple #3
0
 def root(self, pathitems=None):  # pylint: disable=unused-argument
     """Show profiles or home listing when profile auto-selection is enabled"""
     # Get the URL parent path of the navigation: xbmc.getInfoLabel('Container.FolderPath')
     #   it can be found in Kodi log as "ParentPath = [xyz]" but not always return the exact value
     is_parent_root_path = xbmc.getInfoLabel(
         'Container.FolderPath') == G.BASE_URL + '/'
     # Fetch initial page to refresh all session data
     if is_parent_root_path and not G.IS_CONTAINER_REFRESHED:
         common.make_call('fetch_initial_page')
     # Note when the profiles are updated to the database (by fetch_initial_page call),
     #   the update sanitize also relative settings to profiles (see _delete_non_existing_profiles in website.py)
     autoselect_profile_guid = G.LOCAL_DB.get_value(
         'autoselect_profile_guid', '')
     if autoselect_profile_guid and not G.IS_CONTAINER_REFRESHED:
         if is_parent_root_path:
             LOG.info('Performing auto-selection of profile {}',
                      autoselect_profile_guid)
         # Do not perform the profile switch if navigation come from a page that is not the root url,
         # prevents profile switching when returning to the main menu from one of the sub-menus
         if not is_parent_root_path or activate_profile(
                 autoselect_profile_guid):
             self.home(None, True)
             return
     # IS_CONTAINER_REFRESHED is temporary set from the profiles context menu actions
     #   to avoid perform the fetch_initial_page/auto-selection every time when the container will be refreshed
     G.IS_CONTAINER_REFRESHED = False
     list_data, extra_data = common.make_call('get_profiles',
                                              {'request_update': False})
     self._profiles(list_data, extra_data)
Exemple #4
0
 def _process_event_request(self, event):
     """Do the event post request"""
     event.status = Event.STATUS_REQUESTED
     # Request attempts can be made up to a maximum of 3 times per event
     LOG.info('EVENT [{}] - Executing request', event)
     endpoint_url = ENDPOINTS['events'] + create_req_params(
         20 if event.event_type == EVENT_START else 0,
         'events/{}'.format(event))
     try:
         response = self.chunked_request(endpoint_url,
                                         event.request_data,
                                         get_esn(),
                                         disable_msl_switch=False)
         # Malformed/wrong content in requests are ignored without returning error feedback in the response
         event.set_response(response)
     except Exception as exc:  # pylint: disable=broad-except
         LOG.error('EVENT [{}] - The request has failed: {}', event, exc)
         event.set_response('RequestError')
     if event.event_type == EVENT_STOP and event.status == Event.STATUS_SUCCESS:
         self.clear_queue()
         if event.event_data['allow_request_update_loco']:
             # Calls to nfsession
             common.make_http_call('update_loco_context',
                                   {'context_name': 'continueWatching'})
             common.make_http_call('update_videoid_bookmark',
                                   {'video_id': event.get_video_id()})
     return True
Exemple #5
0
 def perform_key_handshake(self):
     """Perform a key handshake and initialize crypto keys"""
     esn = get_esn()
     if not esn:
         LOG.error('Cannot perform key handshake, missing ESN')
         return False
     LOG.info('Performing key handshake with ESN: {}',
              common.censure(esn) if len(esn) > 50 else esn)
     try:
         header, _ = _process_json_response(
             self._post(ENDPOINTS['manifest'], self.handshake_request(esn)))
         header_data = self.decrypt_header_data(header['headerdata'], False)
         self.crypto.parse_key_response(header_data, esn, True)
     except MSLError as exc:
         if exc.err_number == 207006 and common.get_system_platform(
         ) == 'android':
             msg = (
                 'Request failed validation during key exchange\r\n'
                 'To try to solve this problem read the Wiki FAQ on add-on GitHub.'
             )
             raise MSLError(msg) from exc
         raise
     # Delete all the user id tokens (are correlated to the previous mastertoken)
     self.crypto.clear_user_id_tokens()
     LOG.debug('Key handshake successful')
     return True
def _compute_next_schedule(date_last_start=None):
    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:
                LOG.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 = date_last_start or 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():
            LOG.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
        # LOG.debug(G.py2_decode(traceback.format_exc(), 'latin-1'))
        LOG.warn('Managed error at _compute_next_schedule')
        return None
 def on_playback_started(self, player_state):
     if self.resume_position:
         # Due to a bug on Kodi the resume on STRM files not works correctly,
         # so we force the skip to the resume point
         LOG.info('AMPlayback has forced resume point to {}',
                  self.resume_position)
         xbmc.Player().seekTime(int(self.resume_position))
Exemple #8
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 on_tick(self, player_state):
     # Stops playback when paused for more than one hour.
     # Some users leave the playback paused also for more than 12 hours,
     # this complicates things to resume playback, because the manifest data expires and with it also all
     # the streams urls are no longer guaranteed, so we force the stop of the playback.
     if self.is_player_in_pause and (time.time() - self.start_time) > 3600:
         LOG.info('The playback has been stopped because it has been exceeded 1 hour of pause')
         common.stop_playback()
 def shutdown(self):
     """Stop the background services"""
     self._set_service_status('stopped')
     self.nf_server_instance.shutdown()
     self.nf_server_instance.server_close()
     self.nf_server_instance = None
     self.nf_server_thread.join()
     self.nf_server_thread = None
     LOG.info('Stopped MSL Service')
Exemple #11
0
 def _init_msl_handler(self):
     self.msl_requests = None
     try:
         msl_data = json.loads(common.load_file_def(MSL_DATA_FILENAME))
         LOG.info('Loaded MSL data from disk')
     except Exception:  # pylint: disable=broad-except
         msl_data = None
     self.msl_requests = MSLRequests(msl_data)
     self.switch_events_handler()
 def sync_library_with_mylist(self):
     """
     Perform a full sync of Kodi library with Netflix "My List",
     by deleting everything that was previously exported
     """
     LOG.info('Performing sync of Kodi library with My list')
     # Clear all the library
     self.clear_library()
     # Start the sync
     self.auto_update_library(True, show_nfo_dialog=True, clear_on_cancel=True)
Exemple #13
0
 def import_library(self, path):
     """
     Imports an already existing exported STRM library into the add-on library database,
     allows you to restore an existing library, by avoiding to recreate it from scratch.
     This operations also update the missing tv shows seasons and episodes, and automatically
     converts old STRM format type from add-on version 0.13.x or before 1.7.0 to new format.
     """
     # If set ask to user if want to export NFO files
     nfo_settings = nfo.NFOSettings()
     nfo_settings.show_export_dialog()
     LOG.info('Start importing Kodi library')
     remove_folders = [
     ]  # List of failed imports paths to be optionally removed
     remove_titles = [
     ]  # List of failed imports titles to be optionally removed
     # Start importing STRM files
     folders = get_library_subfolders(FOLDER_NAME_MOVIES,
                                      path) + get_library_subfolders(
                                          FOLDER_NAME_SHOWS, path)
     with ui.ProgressDialog(True, max_value=len(folders)) as progress_bar:
         for folder_path in folders:
             folder_name = os.path.basename(
                 G.py2_decode(translatePath(folder_path)))
             progress_bar.set_message(folder_name)
             try:
                 videoid = self.import_videoid_from_existing_strm(
                     folder_path, folder_name)
                 if videoid is None:
                     # Failed to import, add folder to remove list
                     remove_folders.append(folder_path)
                     remove_titles.append(folder_name)
                     continue
                 # Successfully imported, Execute the task
                 for index, total_tasks, title in self.execute_library_task(
                         videoid,
                         self.export_item,
                         nfo_settings=nfo_settings,
                         notify_errors=True):
                     label_partial_op = ' ({}/{})'.format(
                         index + 1, total_tasks) if total_tasks > 1 else ''
                     progress_bar.set_message(title + label_partial_op)
                 if progress_bar.is_cancelled():
                     LOG.warn('Import library interrupted by User')
                     return
                 if self.monitor.abortRequested():
                     LOG.warn('Import library interrupted by Kodi')
                     return
             except ImportWarning:
                 # Ignore it, something was wrong in STRM file (see _import_videoid in library_jobs.py)
                 pass
             progress_bar.perform_step()
             progress_bar.set_wait_message()
             delay_anti_ban()
     ret = self._import_library_remove(remove_titles, remove_folders)
     request_kodi_library_update(scan=True, clean=ret)
 def remove_from_library(self, videoid, show_prg_dialog=True):
     """
     Remove an item from the Kodi library
     :param videoid: the videoid
     :param show_prg_dialog: if True show progress dialog, otherwise, a background progress bar
     """
     LOG.info('Start removing {} from library', videoid)
     common.remove_videoid_from_kodi_library(videoid)
     self.execute_library_task_gui(videoid,
                                   self.remove_item,
                                   title=common.get_local_string(30030),
                                   show_prg_dialog=show_prg_dialog)
Exemple #15
0
def run(argv):
    # 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)

    LOG.info('Started (Version {})'.format(G.VERSION_RAW))
    LOG.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:
        cancel_playback = False
        pathitems = [part for part in G.REQUEST_PATH.split('/') if part]
        if G.IS_ADDON_FIRSTRUN:
            is_first_run_install, cancel_playback = check_addon_upgrade()
            if is_first_run_install:
                from resources.lib.config_wizard import run_addon_configuration
                run_addon_configuration()
        if cancel_playback and G.MODE_PLAY in pathitems[:1]:
            # Temporary for migration library STRM to new format. todo: to be removed in future releases
            # When a user do the add-on upgrade, the first time that the add-on will be opened will be executed
            # the library migration. But if a user instead to open the add-on, try to play a video from Kodi
            # library, Kodi will open the old STRM file because the migration is executed after.
            success = False
        else:
            success = route(pathitems)
    if not success:
        from xbmcplugin import endOfDirectory
        endOfDirectory(handle=G.PLUGIN_HANDLE, succeeded=False)
    LOG.log_time_trace()
Exemple #16
0
 def shutdown(self):
     """
     Stop the background services
     """
     self._set_service_status('stopped')
     for server in self.SERVERS:
         server['instance'].shutdown()
         server['instance'].server_close()
         server['instance'] = None
         server['thread'].join()
         server['thread'] = None
     LOG.info('Stopped MSL Service')
Exemple #17
0
 def _auto_skip(self, section):
     LOG.info('Auto-skipping {}', section)
     player = xbmc.Player()
     ui.show_notification(
         common.get_local_string(SKIPPABLE_SECTIONS[section]))
     if self.pause_on_skip:
         player.pause()
         xbmc.sleep(1000)  # give kodi the chance to execute
         player.seekTime(self.markers[section]['end'])
         xbmc.sleep(1000)  # give kodi the chance to execute
         player.pause()  # unpause playback at seek position
     else:
         player.seekTime(self.markers[section]['end'])
 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
 def export_to_library(self, videoid, show_prg_dialog=True):
     """
     Export an item to the Kodi library
     :param videoid: the videoid
     :param show_prg_dialog: if True show progress dialog, otherwise, a background progress bar
     """
     LOG.info('Start exporting {} to the library', videoid)
     nfo_settings = nfo.NFOSettings()
     nfo_settings.show_export_dialog(videoid.mediatype)
     self.execute_library_task_gui(videoid,
                                   self.export_item,
                                   title=common.get_local_string(30018),
                                   nfo_settings=nfo_settings,
                                   show_prg_dialog=show_prg_dialog)
 def _load_cookies(self):
     """Load stored cookies from disk"""
     # pylint: disable=broad-except
     if not self.session.cookies:
         try:
             self.session.cookies = cookies.load()
         except MissingCookiesError:
             return False
         except Exception as exc:
             import traceback
             LOG.error('Failed to load stored cookies: {}', type(exc).__name__)
             LOG.error(traceback.format_exc())
             return False
         LOG.info('Successfully loaded stored cookies')
     return True
 def _verify_session_cookies(self):
     """Verify that the session cookies have not expired"""
     if not self.session.cookies:
         return False
     for cookie_name in LOGIN_COOKIES:
         if cookie_name not in list(self.session.cookies.keys()):
             LOG.error('The cookie "{}" do not exist, it is not possible to check the expiration',
                       cookie_name)
             return False
         for cookie in self.session.cookies.jar:
             if cookie.name != cookie_name:
                 continue
             if cookie.expires <= int(time.time()):
                 LOG.info('Login is expired')
                 return False
     return True
    def start_services(self):
        """Start the background services"""
        from resources.lib.services.library_updater import LibraryUpdateService

        self.nf_server_instance.server_activate()
        self.nf_server_thread.start()
        LOG.info('[NF_SERVER] Thread started')

        self.library_updater = LibraryUpdateService()
        # We reset the value in case of any eventuality (add-on disabled, update, etc)
        WndHomeProps[WndHomeProps.CURRENT_DIRECTORY] = None
        # Mark the service as active
        self._set_service_status('running')
        if not G.ADDON.getSettingBool('disable_startup_notification'):
            from resources.lib.kodi.ui import show_notification
            show_notification(get_local_string(30110))
Exemple #23
0
 def _init_session(self):
     """Initialize the session to use for all future connections"""
     try:
         self.session.close()
         LOG.info('Session closed')
     except AttributeError:
         pass
     from requests import session
     self.session = session()
     self.session.max_redirects = 10  # Too much redirects should means some problem
     self.session.headers.update({
         'User-Agent': common.get_user_agent(enable_android_mediaflag_fix=True),
         'Accept-Encoding': 'gzip, deflate, br',
         'Host': 'www.netflix.com'
     })
     LOG.info('Initialized new session')
 def export_to_library_new_episodes(self, videoid, show_prg_dialog=True):
     """
     Export new episodes for a tv show by it's videoid
     :param videoid: The videoid of the tv show to process
     :param show_prg_dialog: if True show progress dialog, otherwise, a background progress bar
     """
     LOG.info('Start exporting new episodes for {}', videoid)
     if videoid.mediatype != common.VideoId.SHOW:
         LOG.warn('{} is not a tv show, the operation is cancelled', videoid)
         return
     nfo_settings = nfo.NFOSettings()
     nfo_settings.show_export_dialog(videoid.mediatype)
     self.execute_library_task_gui(videoid,
                                   self.export_new_item,
                                   title=common.get_local_string(30198),
                                   nfo_settings=nfo_settings,
                                   show_prg_dialog=show_prg_dialog)
    def logout(self):
        """Logout of the current account and reset the session"""
        LOG.debug('Logging out of current account')

        # Perform the website logout
        self.get('logout')

        G.settings_monitor_suspend(True)

        # Disable and reset auto-update / auto-sync features
        G.ADDON.setSettingInt('lib_auto_upd_mode', 1)
        G.ADDON.setSettingBool('lib_sync_mylist', False)
        G.SHARED_DB.delete_key('sync_mylist_profile_guid')

        # Disable and reset the auto-select profile
        G.LOCAL_DB.set_value('autoselect_profile_guid', '')
        G.ADDON.setSetting('autoselect_profile_name', '')
        G.ADDON.setSettingBool('autoselect_profile_enabled', False)

        # Reset of selected profile guid for library playback
        G.LOCAL_DB.set_value('library_playback_profile_guid', '')
        G.ADDON.setSetting('library_playback_profile', '')

        G.settings_monitor_suspend(False)

        # Delete cookie and credentials
        self.session.cookies.clear()
        cookies.delete(self.account_hash)
        common.purge_credentials()

        # Reset the ESN obtained from website/generated
        G.LOCAL_DB.set_value('esn', '', TABLE_SESSION)

        # Reinitialize the MSL handler (delete msl data file, then reset everything)
        common.send_signal(signal=common.Signals.REINITIALIZE_MSL_HANDLER,
                           data=True)

        G.CACHE.clear(clear_database=True)

        LOG.info('Logout successful')
        ui.show_notification(common.get_local_string(30113))
        self._init_session()
        common.container_update('path',
                                True)  # Go to a fake page to clear screen
        # Open root page
        common.container_update(G.BASE_URL, True)
    def perform_key_handshake(self, data=None):  # pylint: disable=unused-argument
        """Perform a key handshake and initialize crypto keys"""
        esn = get_esn()
        if not esn:
            LOG.warn('Cannot perform key handshake, missing ESN')
            return False

        LOG.info('Performing key handshake with ESN: {}',
                 common.censure(esn) if G.ADDON.getSetting('esn') else esn)
        response = _process_json_response(
            self._post(ENDPOINTS['manifest'], self.handshake_request(esn)))
        header_data = self.decrypt_header_data(response['headerdata'], False)
        self.crypto.parse_key_response(header_data, esn, True)

        # Delete all the user id tokens (are correlated to the previous mastertoken)
        self.crypto.clear_user_id_tokens()
        LOG.debug('Key handshake successful')
        return True
 def clear_library(self, show_prg_dialog=True):
     """
     Delete all exported items to the library
     :param show_prg_dialog: if True, will be show a progress dialog window
     """
     LOG.info('Start deleting exported library items')
     # This will clear all the add-on library data, to prevents possible inconsistencies when for some reason
     # such as improper use of the add-on, unexpected error or other has broken the library database data or files
     with ui.ProgressDialog(show_prg_dialog, common.get_local_string(30245), max_value=3) as progress_dlg:
         progress_dlg.perform_step()
         progress_dlg.set_wait_message()
         G.SHARED_DB.purge_library()
         for folder_name in [FOLDER_NAME_MOVIES, FOLDER_NAME_SHOWS]:
             progress_dlg.perform_step()
             progress_dlg.set_wait_message()
             section_root_dir = common.join_folders_paths(get_library_path(), folder_name)
             common.delete_folder_contents(section_root_dir, delete_subfolders=True)
     # Clean the Kodi library database
     common.clean_library(show_prg_dialog)
 def root(self, pathitems=None):  # pylint: disable=unused-argument
     """Show profiles or home listing when profile auto-selection is enabled"""
     # Fetch initial page to refresh all session data
     if G.CURRENT_LOADED_DIRECTORY is None:
         # Note when the profiles are updated to the database (by fetch_initial_page call),
         # the update sanitize also relative settings to profiles (see _delete_non_existing_profiles in website.py)
         common.make_call('fetch_initial_page')
     autoselect_profile_guid = G.LOCAL_DB.get_value(
         'autoselect_profile_guid', '')
     if autoselect_profile_guid and not G.IS_CONTAINER_REFRESHED:
         if G.CURRENT_LOADED_DIRECTORY is None:
             LOG.info('Performing auto-selection of profile {}',
                      autoselect_profile_guid)
             self.params['switch_profile_guid'] = autoselect_profile_guid
         self.home(None)
         return
     list_data, extra_data = common.make_call('get_profiles',
                                              {'request_update': False})
     self._profiles(list_data, extra_data)
 def _process_event_request(self, event_type, event_data, player_state):
     """Build and make the event post request"""
     if event_type == EVENT_START:
         # We get at every new video playback a fresh LoCo data
         self.loco_data = self.nfsession.get_loco_data()
     url = event_data['manifest']['links']['events']['href']
     from resources.lib.services.nfsession.msl.msl_request_builder import MSLRequestBuilder
     request_data = MSLRequestBuilder.build_request_data(
         url,
         self._build_event_params(event_type, event_data, player_state,
                                  event_data['manifest'], self.loco_data))
     # Request attempts can be made up to a maximum of 3 times per event
     LOG.info('EVENT [{}] - Executing request', event_type)
     endpoint_url = ENDPOINTS['events'] + create_req_params(
         20 if event_type == EVENT_START else 0, f'events/{event_type}')
     try:
         response = self.chunked_request(endpoint_url,
                                         request_data,
                                         get_esn(),
                                         disable_msl_switch=False)
         # Malformed/wrong content in requests are ignored without returning any error in the response or exception
         LOG.debug('EVENT [{}] - Request response: {}', event_type,
                   response)
         if event_type == EVENT_STOP:
             if event_data['allow_request_update_loco']:
                 if 'list_context_name' in self.loco_data:
                     self.nfsession.update_loco_context(
                         self.loco_data['root_id'],
                         self.loco_data['list_context_name'],
                         self.loco_data['list_id'],
                         self.loco_data['list_index'])
                 else:
                     LOG.warn(
                         'EventsHandler: LoCo list not updated due to missing list context data'
                     )
                 video_id = request_data['params']['sessionParams'][
                     'uiplaycontext']['video_id']
                 self.nfsession.update_videoid_bookmark(video_id)
             self.loco_data = None
     except Exception as exc:  # pylint: disable=broad-except
         LOG.error('EVENT [{}] - The request has failed: {}', event_type,
                   exc)
 def _import_library_remove(self, remove_titles, remove_folders):
     if not remove_folders:
         return False
     # If there are STRM files that it was not possible to import them,
     # we will ask to user if you want to delete them
     tot_folders = len(remove_folders)
     if tot_folders > 50:
         remove_titles = remove_titles[:50] + ['...']
     message = common.get_local_string(30246).format(tot_folders) + '[CR][CR]' + ', '.join(remove_titles)
     if not ui.ask_for_confirmation(common.get_local_string(30140), message):
         return False
     # Delete all folders
     LOG.info('Start deleting folders')
     with ui.ProgressDialog(True, max_value=tot_folders) as progress_bar:
         for file_path in remove_folders:
             progress_bar.set_message('{}/{}'.format(progress_bar.value, tot_folders))
             LOG.debug('Deleting folder: {}', file_path)
             common.delete_folder(file_path)
             progress_bar.perform_step()
     return True