def migrate_library():
    # Migrate the Kodi library to the new format of STRM path
    # - Old STRM: '/play/show/xxxxxxxx/season/xxxxxxxx/episode/xxxxxxxx/' (used before ver 1.7.0)
    # - New STRM: '/play_strm/show/xxxxxxxx/season/xxxxxxxx/episode/xxxxxxxx/' (used from ver 1.7.0)
    folders = get_library_subfolders(
        FOLDER_NAME_MOVIES) + get_library_subfolders(FOLDER_NAME_SHOWS)
    if not folders:
        return
    LOG.debug('Start migrating STRM files')
    try:
        with ui.ProgressDialog(True,
                               title='Migrating library to new format',
                               max_value=len(folders)) as progress_bar:
            for folder_path in folders:
                folder_name = os.path.basename(
                    G.py2_decode(xbmc.translatePath(folder_path)))
                progress_bar.set_message('PLEASE WAIT - Migrating: ' +
                                         folder_name)
                _migrate_strm_files(folder_path)
    except Exception as exc:  # pylint: disable=broad-except
        LOG.error('Migrating failed: {}', exc)
        import traceback
        LOG.error(G.py2_decode(traceback.format_exc(), 'latin-1'))
        ui.show_ok_dialog('Migrating library to new format', (
            'Library migration has failed.[CR]'
            'Before try play a Netflix video from library, you must run manually the library migration, '
            'otherwise you will have add-on malfunctions.[CR][CR]'
            'Open add-on settings on "Library" section, and select "Import existing library".'
        ))
Пример #2
0
def parse_profiles(data):
    """Parse profile information from Netflix response"""
    profiles_list = jgraph_get_list('profilesList', data)
    try:
        if not profiles_list:
            raise InvalidProfilesError('It has not been possible to obtain the list of profiles.')
        sort_order = 0
        current_guids = []
        for index, profile_data in iteritems(profiles_list):  # pylint: disable=unused-variable
            summary = jgraph_get('summary', profile_data)
            guid = summary['guid']
            current_guids.append(guid)
            LOG.debug('Parsing profile {}', summary['guid'])
            avatar_url = _get_avatar(profile_data, data, guid)
            is_active = summary.pop('isActive')
            G.LOCAL_DB.set_profile(guid, is_active, sort_order)
            G.SHARED_DB.set_profile(guid, sort_order)
            # Add profile language description translated from locale
            summary['language_desc'] = G.py2_decode(xbmc.convertLanguage(summary['language'][:2], xbmc.ENGLISH_NAME))
            if LOG.level == LOG.LEVEL_VERBOSE:
                for key, value in iteritems(summary):
                    if key in PROFILE_DEBUG_INFO:
                        LOG.debug('Profile info {}', {key: value})
            # Translate the profile name, is coded as HTML
            summary['profileName'] = parse_html(summary['profileName'])
            summary['avatar'] = avatar_url
            G.LOCAL_DB.insert_profile_configs(summary, guid)
            sort_order += 1
        _delete_non_existing_profiles(current_guids)
    except Exception as exc:  # pylint: disable=broad-except
        import traceback
        LOG.error(G.py2_decode(traceback.format_exc(), 'latin-1'))
        LOG.error('Profile list data: {}', profiles_list)
        raise_from(InvalidProfilesError, exc)
Пример #3
0
def load(account_hash):
    """Load cookies for a given account and check them for validity"""
    filename = cookie_filename(account_hash)
    if not xbmcvfs.exists(filename):
        common.debug('Cookies file does not exist')
        raise MissingCookiesError()
    common.debug('Loading cookies from {}', G.py2_decode(filename))
    cookie_file = xbmcvfs.File(filename, 'rb')
    try:
        if G.PY_IS_VER2:
            # pickle.loads on py2 wants string
            cookie_jar = pickle.loads(cookie_file.read())
        else:
            cookie_jar = pickle.loads(cookie_file.readBytes())
    except Exception as exc:  # pylint: disable=broad-except
        import traceback
        common.error('Failed to load cookies from file: {exc}', exc=exc)
        common.error(G.py2_decode(traceback.format_exc(), 'latin-1'))
        raise MissingCookiesError()
    finally:
        cookie_file.close()
    # Clear flwssn cookie if present, as it is trouble with early expiration
    if 'flwssn' in cookie_jar:
        cookie_jar.clear(domain='.netflix.com', path='/', name='flwssn')
    log_cookie(cookie_jar)
    return cookie_jar
Пример #4
0
def rename_cookie_file():
    # The file "COOKIE_xxxxxx..." will be renamed to "COOKIES"
    list_files = list_dir(G.DATA_PATH)[1]
    for filename in list_files:
        if 'COOKIE_' in G.py2_decode(filename):
            copy_file(join_folders_paths(G.DATA_PATH, G.py2_decode(filename)),
                      join_folders_paths(G.DATA_PATH, 'COOKIES'))
            xbmc.sleep(80)
            delete_file(G.py2_decode(filename))
def delete_cache_folder():
    # Delete cache folder in the add-on userdata (no more needed with the new cache management)
    cache_path = os.path.join(G.DATA_PATH, 'cache')
    if not os.path.exists(G.py2_decode(xbmc.translatePath(cache_path))):
        return
    LOG.debug('Deleting the cache folder from add-on userdata folder')
    try:
        delete_folder_contents(cache_path, True)
        xbmc.sleep(80)
        xbmcvfs.rmdir(cache_path)
    except Exception:  # pylint: disable=broad-except
        import traceback
        LOG.error(G.py2_decode(traceback.format_exc(), 'latin-1'))
def get_upnext_info(videoid, videoid_next_episode, info_data, metadata,
                    is_played_from_strm):
    """Get the data to send to Up Next add-on"""
    upnext_info = {
        'current_episode':
        _upnext_info(videoid, *info_data[videoid.value]),
        'next_episode':
        _upnext_info(videoid_next_episode,
                     *info_data[videoid_next_episode.value])
    }

    if is_played_from_strm:
        # The current video played is a STRM, then generate the path of next STRM file
        file_path = G.SHARED_DB.get_episode_filepath(
            videoid_next_episode.tvshowid, videoid_next_episode.seasonid,
            videoid_next_episode.episodeid)
        url = G.py2_decode(translatePath(file_path))
    else:
        url = common.build_url(
            videoid=videoid_next_episode,
            mode=G.MODE_PLAY,
            params={'profile_guid': G.LOCAL_DB.get_active_profile_guid()})
    upnext_info['play_url'] = url

    if 'creditsOffset' in metadata[0]:
        upnext_info['notification_offset'] = metadata[0]['creditsOffset']
    return upnext_info
Пример #7
0
def get_library_subfolders(folder_name):
    """Returns all the subfolders contained in a folder of library path"""
    section_path = common.join_folders_paths(get_library_path(), folder_name)
    return [
        common.join_folders_paths(section_path, G.py2_decode(folder))
        for folder in common.list_dir(section_path)[0]
    ]
Пример #8
0
 def _load_msl_data(self, msl_data):
     try:
         self.crypto.load_msl_data(msl_data)
         self.crypto.load_crypto_session(msl_data)
     except Exception:  # pylint: disable=broad-except
         import traceback
         LOG.error(G.py2_decode(traceback.format_exc(), 'latin-1'))
Пример #9
0
def get_local_db_path(db_filename):
    # First ensure database folder exists
    db_folder = G.py2_decode(
        xbmc.translatePath(os.path.join(G.DATA_PATH, 'database')))
    if not folder_exists(db_folder):
        xbmcvfs.mkdirs(db_folder)
    return os.path.join(db_folder, db_filename)
Пример #10
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
Пример #11
0
def make_http_call_cache(callname, params, data):
    """Make an IPC call via HTTP and wait for it to return.
    The contents of data will be expanded to kwargs and passed into the target function."""
    try:  # Python 3
        from urllib.request import build_opener, install_opener, ProxyHandler, HTTPError, URLError, Request, urlopen
    except ImportError:  # Python 2
        from urllib2 import build_opener, install_opener, ProxyHandler, HTTPError, URLError, Request, urlopen
    import json
    # debug('Handling HTTP IPC call to {}'.format(callname))
    # Note: On python 3, using 'localhost' slowdown the call (Windows OS is affected) not sure if it is an urllib issue
    url = 'http://127.0.0.1:{}/{}'.format(G.LOCAL_DB.get_value('cache_service_port', 8002), callname)
    install_opener(build_opener(ProxyHandler({})))  # don't use proxy for localhost
    r = Request(url=url, data=data, headers={'Params': json.dumps(params)})
    try:
        result = urlopen(r, timeout=IPC_TIMEOUT_SECS).read()
    except HTTPError as exc:
        if exc.reason in exceptions.__dict__:
            raise_from(exceptions.__dict__[exc.reason], exc)
        raise_from(Exception('The service has returned: {}'.format(exc.reason)), exc)
    except URLError as exc:
        # On PY2 the exception message have to be decoded with latin-1 for system with symbolic characters
        err_msg = G.py2_decode(str(exc), 'latin-1')
        if '10049' in err_msg:
            err_msg += '\r\nPossible cause is wrong localhost settings in your operative system.'
        LOG.error(err_msg)
        raise_from(exceptions.BackendNotReady(G.py2_encode(err_msg, encoding='latin-1')), exc)
    return result
Пример #12
0
def validate_login(react_context):
    path_code_list = PAGE_ITEM_ERROR_CODE_LIST.split('\\')
    path_error_code = PAGE_ITEM_ERROR_CODE.split('/')
    if common.check_path_exists(path_error_code, react_context):
        # If the path exists, a login error occurs
        try:
            error_code_list = common.get_path(path_code_list, react_context)
            error_code = common.get_path(path_error_code, react_context)
            LOG.error('Login not valid, error code {}', error_code)
            error_description = common.get_local_string(30102) + error_code
            if error_code in error_code_list:
                error_description = error_code_list[error_code]
            if 'email_' + error_code in error_code_list:
                error_description = error_code_list['email_' + error_code]
            if 'login_' + error_code in error_code_list:
                error_description = error_code_list['login_' + error_code]
            raise LoginValidateError(common.remove_html_tags(error_description))
        except (AttributeError, KeyError) as exc:
            import traceback
            LOG.error(G.py2_decode(traceback.format_exc(), 'latin-1'))
            error_msg = (
                'Something is wrong in PAGE_ITEM_ERROR_CODE or PAGE_ITEM_ERROR_CODE_LIST paths.'
                'react_context data may have changed.')
            LOG.error(error_msg)
            raise_from(WebsiteParsingError(error_msg), exc)
Пример #13
0
def delete_folder_contents(path, delete_subfolders=False):
    """
    Delete all files in a folder
    :param path: Path to perform delete contents
    :param delete_subfolders: If True delete also all subfolders
    """
    directories, files = list_dir(xbmc.translatePath(path))
    for filename in files:
        xbmcvfs.delete(os.path.join(path, G.py2_decode(filename)))
    if not delete_subfolders:
        return
    for directory in directories:
        delete_folder_contents(os.path.join(path, G.py2_decode(directory)), True)
        # Give time because the system performs previous op. otherwise it can't delete the folder
        xbmc.sleep(80)
        xbmcvfs.rmdir(os.path.join(path, G.py2_decode(directory)))
Пример #14
0
def make_http_call(callname, data):
    """Make an IPC call via HTTP and wait for it to return.
    The contents of data will be expanded to kwargs and passed into the target function."""
    from collections import OrderedDict
    try:  # Python 3
        from urllib.request import build_opener, install_opener, ProxyHandler, HTTPError, URLError, urlopen
    except ImportError:  # Python 2
        from urllib2 import build_opener, install_opener, ProxyHandler, HTTPError, URLError, urlopen
    import json
    LOG.debug('Handling HTTP IPC call to {}'.format(callname))
    # Note: On python 3, using 'localhost' slowdown the call (Windows OS is affected) not sure if it is an urllib issue
    url = 'http://127.0.0.1:{}/{}'.format(G.LOCAL_DB.get_value('ns_service_port', 8001), callname)
    install_opener(build_opener(ProxyHandler({})))  # don't use proxy for localhost
    try:
        result = json.loads(
            urlopen(url=url, data=json.dumps(data).encode('utf-8'), timeout=IPC_TIMEOUT_SECS).read(),
            object_pairs_hook=OrderedDict)
    except HTTPError as exc:
        result = json.loads(exc.reason)
    except URLError as exc:
        # On PY2 the exception message have to be decoded with latin-1 for system with symbolic characters
        err_msg = G.py2_decode(str(exc), 'latin-1')
        if '10049' in err_msg:
            err_msg += '\r\nPossible cause is wrong localhost settings in your operative system.'
        LOG.error(err_msg)
        raise_from(exceptions.BackendNotReady(G.py2_encode(err_msg, encoding='latin-1')),
                   exc)
    _raise_for_error(result)
    return result
Пример #15
0
def extract_json(content, name):
    """Extract json from netflix content page"""
    LOG.debug('Extracting {} JSON', name)
    json_str = None
    try:
        json_array = recompile(JSON_REGEX.format(name),
                               DOTALL).findall(content.decode('utf-8'))
        json_str = json_array[0]
        json_str_replace = json_str.replace('\\"',
                                            '\\\\"')  # Escape double-quotes
        json_str_replace = json_str_replace.replace('\\s',
                                                    '\\\\s')  # Escape \s
        json_str_replace = json_str_replace.replace(
            '\\n', '\\\\n')  # Escape line feed
        json_str_replace = json_str_replace.replace('\\t',
                                                    '\\\\t')  # Escape tab
        json_str_replace = json_str_replace.encode().decode(
            'unicode_escape')  # Decode the string as unicode
        json_str_replace = sub(
            r'\\(?!["])', r'\\\\', json_str_replace
        )  # Escape backslash (only when is not followed by double quotation marks \")
        return json.loads(json_str_replace)
    except Exception as exc:  # pylint: disable=broad-except
        if json_str:
            LOG.error('JSON string trying to load: {}', json_str)
        import traceback
        LOG.error(G.py2_decode(traceback.format_exc(), 'latin-1'))
        raise_from(WebsiteParsingError('Unable to extract {}'.format(name)),
                   exc)
Пример #16
0
def show_browse_dialog(title,
                       browse_type=0,
                       default_path=None,
                       multi_selection=False,
                       extensions=None):
    """
    Show a browse dialog to select files or folders
    :param title: The window title
    :param browse_type: Type of dialog as int value (0 = ShowAndGetDirectory, 1 = ShowAndGetFile, ..see doc)
    :param default_path: The initial path
    :param multi_selection: Allow multi selection
    :param extensions: extensions allowed e.g. '.jpg|.png'
    :return: The selected path as string (or tuple of selected items) if user pressed 'Ok', else None
    """
    ret = G.py2_decode(xbmcgui.Dialog().browse(browse_type,
                                               title,
                                               shares='local',
                                               useThumbs=False,
                                               treatAsFolder=False,
                                               defaultt=default_path,
                                               enableMultiple=multi_selection,
                                               mask=extensions))
    # Note: when defaultt is set and the user cancel the action (when enableMultiple is False),
    #       will be returned the defaultt value again, so we avoid this strange behavior...
    return None if not ret or ret == default_path else ret
Пример #17
0
 def autoselect_profile_set(self, pathitems):  # pylint: disable=unused-argument
     """Save the GUID for profile auto-selection"""
     G.LOCAL_DB.set_value('autoselect_profile_guid', self.params['profile_guid'])
     G.settings_monitor_suspend(True)
     G.ADDON.setSetting('autoselect_profile_name', self.params['profile_name'])
     G.ADDON.setSettingBool('autoselect_profile_enabled', True)
     G.settings_monitor_suspend(False)
     ui.show_notification(common.get_local_string(30058).format(G.py2_decode(self.params['profile_name'])))
Пример #18
0
    def remove_item(self, job_data, library_home=None):  # pylint: disable=unused-argument
        """Remove an item from the Kodi library, delete it from disk, remove add-on database references"""
        videoid = job_data['videoid']
        LOG.debug('Removing {} ({}) from add-on library', videoid,
                  job_data['title'])
        try:
            # Remove the STRM file exported
            exported_file_path = G.py2_decode(
                xbmc.translatePath(job_data['file_path']))
            common.delete_file_safe(exported_file_path)

            parent_folder = G.py2_decode(
                xbmc.translatePath(os.path.dirname(exported_file_path)))

            # Remove the NFO file of the related STRM file
            nfo_file = os.path.splitext(exported_file_path)[0] + '.nfo'
            common.delete_file_safe(nfo_file)

            dirs, files = common.list_dir(parent_folder)

            # Remove the tvshow NFO file (only when it is the last file in the folder)
            tvshow_nfo_file = common.join_folders_paths(
                parent_folder, 'tvshow.nfo')

            # (users have the option of removing even single seasons)
            if xbmcvfs.exists(tvshow_nfo_file) and not dirs and len(
                    files) == 1:
                xbmcvfs.delete(tvshow_nfo_file)
                # Delete parent folder
                xbmcvfs.rmdir(parent_folder)

            # Delete parent folder when empty
            if not dirs and not files:
                xbmcvfs.rmdir(parent_folder)

            # Remove videoid records from add-on database
            remove_videoid_from_db(videoid)
        except ItemNotFound:
            LOG.warn(
                'The videoid {} not exists in the add-on library database',
                videoid)
        except Exception as exc:  # pylint: disable=broad-except
            import traceback
            LOG.error(G.py2_decode(traceback.format_exc(), 'latin-1'))
            ui.show_addon_error_info(exc)
Пример #19
0
def _subtitle_profiles():
    from xbmcaddon import Addon
    isa_version = G.remove_ver_suffix(
        G.py2_decode(Addon('inputstream.adaptive').getAddonInfo('version')))
    subtitle_profile = ['webvtt-lssdh-ios8']
    if G.ADDON.getSettingBool('disable_webvtt_subtitle') \
       or not common.is_minimum_version(isa_version, '2.3.8'):
        subtitle_profile = ['simplesdh']
    return subtitle_profile
Пример #20
0
def _perform_ipc_return_call(func, data, func_name=None):
    try:
        result = _call(func, data)
    except Exception as exc:  # pylint: disable=broad-except
        if exc.__class__.__name__ not in ['CacheMiss', 'MetadataNotAvailable']:
            LOG.error('IPC callback raised exception: {exc}', exc=exc)
            import traceback
            LOG.error(G.py2_decode(traceback.format_exc(), 'latin-1'))
        result = ipc_convert_exc_to_json(exc)
    return _execute_addonsignals_return_call(result, func_name)
Пример #21
0
 def callback_event_video_queue(self, data=None):
     """Callback to add a video event"""
     try:
         self.add_event_to_queue(data['event_type'], data['event_data'],
                                 data['player_state'])
     except Exception as exc:  # pylint: disable=broad-except
         import traceback
         from resources.lib.kodi.ui import show_addon_error_info
         common.error(G.py2_decode(traceback.format_exc(), 'latin-1'))
         show_addon_error_info(exc)
Пример #22
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)
Пример #23
0
 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
def _get_item_details_from_kodi(mediatype, file_path):
    """Get a Kodi library item with details (from Kodi database) by searching with the file path"""
    # To ensure compatibility with previously exported items, make the filename legal
    file_path = makeLegalFilename(file_path)
    dir_path = os.path.dirname(G.py2_decode(xbmc.translatePath(file_path)))
    filename = os.path.basename(G.py2_decode(xbmc.translatePath(file_path)))
    # We get the data from Kodi library using filters, this is much faster than loading all episodes in memory.
    if file_path[:10] == 'special://':
        # If the path is special, search with real directory path and also special path
        special_dir_path = os.path.dirname(G.py2_decode(file_path))
        path_filter = {
            'or': [{
                'field': 'path',
                'operator': 'startswith',
                'value': dir_path
            }, {
                'field': 'path',
                'operator': 'startswith',
                'value': special_dir_path
            }]
        }
    else:
        path_filter = {
            'field': 'path',
            'operator': 'startswith',
            'value': dir_path
        }
    # Now build the all request and call the json-rpc function through get_library_items
    library_items = get_library_items(
        mediatype, {
            'and': [
                path_filter, {
                    'field': 'filename',
                    'operator': 'is',
                    'value': filename
                }
            ]
        })
    if not library_items:
        raise ItemNotFound
    return get_library_item_details(mediatype,
                                    library_items[0][mediatype + 'id'])
Пример #25
0
 def wrapper(*args, **kwargs):
     # pylint: disable=broad-except, ungrouped-imports
     success = False
     try:
         func(*args, **kwargs)
         success = True
     except BackendNotReady as exc_bnr:
         from resources.lib.kodi.ui import show_backend_not_ready
         show_backend_not_ready(G.py2_decode(str(exc_bnr), 'latin-1'))
     except InputStreamHelperError as exc:
         from resources.lib.kodi.ui import show_ok_dialog
         show_ok_dialog('InputStream Helper Add-on error', (
             'The operation has been cancelled.\r\n'
             'InputStream Helper has generated an internal error:\r\n{}\r\n\r\n'
             'Please report it to InputStream Helper github.'.format(exc)))
     except (
             HttpError401, HttpErrorTimeout
     ) as exc:  # HTTP error 401 Client Error: Unauthorized for url ...
         # HttpError401: This is a generic error, can happen when the http request for some reason has failed.
         # Known causes:
         # - Possible change of data format or wrong data in the http request (also in headers/params)
         # - Some current nf session data are not more valid (authURL/cookies/...)
         # HttpErrorTimeout: This error is raised by Requests ReadTimeout error, unknown causes
         from resources.lib.kodi.ui import show_ok_dialog
         show_ok_dialog(
             get_local_string(30105),
             ('There was a communication problem with Netflix.[CR]'
              'You can try the operation again or exit.[CR]'
              '(Error code: {})').format(exc.__class__.__name__))
     except (MbrStatusNeverMemberError, MbrStatusFormerMemberError):
         from resources.lib.kodi.ui import show_error_info
         show_error_info(get_local_string(30008), get_local_string(30180),
                         False, True)
     except Exception as exc:
         import traceback
         from resources.lib.kodi.ui import show_addon_error_info
         LOG.error(G.py2_decode(traceback.format_exc(), 'latin-1'))
         show_addon_error_info(exc)
     finally:
         if not success:
             from xbmcplugin import endOfDirectory
             endOfDirectory(handle=G.PLUGIN_HANDLE, succeeded=False)
Пример #26
0
 def _tick_and_wait_for_abort(self):
     try:
         self.controller.on_service_tick()
         self.library_updater.on_service_tick()
         G.CACHE_MANAGEMENT.on_service_tick()
     except Exception as exc:  # pylint: disable=broad-except
         import traceback
         from resources.lib.kodi.ui import show_notification
         LOG.error(G.py2_decode(traceback.format_exc(), 'latin-1'))
         show_notification(': '.join((exc.__class__.__name__, unicode(exc))))
     return self.controller.waitForAbort(1)
Пример #27
0
def ask_credentials():
    """
    Show some dialogs and ask the user for account credentials
    """
    email = G.py2_decode(
        xbmcgui.Dialog().input(heading=common.get_local_string(30005),
                               type=xbmcgui.INPUT_ALPHANUM)) or None
    common.verify_credentials(email)
    password = ask_for_password()
    common.verify_credentials(password)
    return {'email': email.strip(), 'password': password.strip()}
Пример #28
0
def convert_to_dash(manifest):
    """Convert a Netflix style manifest to MPEG-DASH manifest"""
    from xbmcaddon import Addon
    isa_version = G.remove_ver_suffix(
        G.py2_decode(Addon('inputstream.adaptive').getAddonInfo('version')))

    # If a CDN server has stability problems it may cause errors with streaming,
    # we allow users to select a different CDN server
    # (should be managed by ISA but is currently is not implemented)
    cdn_index = int(G.ADDON.getSettingString('cdn_server')[-1]) - 1

    seconds = manifest['duration'] / 1000
    init_length = int(seconds / 2 * 12 + 20 * 1000)
    duration = "PT" + str(int(seconds)) + ".00S"

    root = _mpd_manifest_root(duration)
    period = ET.SubElement(root, 'Period', start='PT0S', duration=duration)

    has_video_drm_streams = manifest['video_tracks'][0].get(
        'hasDrmStreams', False)
    video_protection_info = _get_protection_info(
        manifest['video_tracks'][0]) if has_video_drm_streams else None

    for video_track in manifest['video_tracks']:
        _convert_video_track(video_track, period, init_length,
                             video_protection_info, has_video_drm_streams,
                             cdn_index)

    common.fix_locale_languages(manifest['audio_tracks'])
    common.fix_locale_languages(manifest['timedtexttracks'])

    has_audio_drm_streams = manifest['audio_tracks'][0].get(
        'hasDrmStreams', False)

    default_audio_language_index = _get_default_audio_language(manifest)
    for index, audio_track in enumerate(manifest['audio_tracks']):
        _convert_audio_track(audio_track, period, init_length,
                             (index == default_audio_language_index),
                             has_audio_drm_streams, cdn_index)

    default_subtitle_language_index = _get_default_subtitle_language(manifest)
    for index, text_track in enumerate(manifest['timedtexttracks']):
        if text_track['isNoneTrack']:
            continue
        _convert_text_track(text_track, period,
                            (index == default_subtitle_language_index),
                            cdn_index, isa_version)

    xml = ET.tostring(root, encoding='utf-8', method='xml')
    if LOG.level == LOG.LEVEL_VERBOSE:
        common.save_file_def('manifest.mpd', xml)
    return xml.decode('utf-8').replace('\n', '').replace('\r',
                                                         '').encode('utf-8')
 def try_refresh_session_data(self, raise_exception=False):
     """Refresh session data from the Netflix website"""
     from requests import exceptions
     try:
         self.auth_url = website.extract_session_data(
             self.get('browse'))['auth_url']
         cookies.save(self.account_hash, self.session.cookies)
         common.debug('Successfully refreshed session data')
         return True
     except MbrStatusError:
         raise
     except (WebsiteParsingError, MbrStatusAnonymousError) as exc:
         import traceback
         common.warn(
             'Failed to refresh session data, login can be expired or the password has been changed ({})',
             type(exc).__name__)
         common.debug(G.py2_decode(traceback.format_exc(), 'latin-1'))
         self.session.cookies.clear()
         if isinstance(exc, MbrStatusAnonymousError):
             # This prevent the MSL error: No entity association record found for the user
             common.send_signal(signal=common.Signals.CLEAR_USER_ID_TOKENS)
         return self.external_func_login(modal_error_message=False)  # pylint: disable=not-callable
     except exceptions.RequestException:
         import traceback
         common.warn(
             'Failed to refresh session data, request error (RequestException)'
         )
         common.warn(G.py2_decode(traceback.format_exc(), 'latin-1'))
         if raise_exception:
             raise
     except Exception:  # pylint: disable=broad-except
         import traceback
         common.warn(
             'Failed to refresh session data, login expired (Exception)')
         common.debug(G.py2_decode(traceback.format_exc(), 'latin-1'))
         self.session.cookies.clear()
         if raise_exception:
             raise
     return False
def _notify_managers(manager, notification, data):
    notify_method = getattr(manager, notification.__name__)
    try:
        if data is not None:
            notify_method(data)
        else:
            notify_method()
    except Exception as exc:  # pylint: disable=broad-except
        manager.enabled = False
        msg = '{} disabled due to exception: {}'.format(manager.name, exc)
        import traceback
        LOG.error(G.py2_decode(traceback.format_exc(), 'latin-1'))
        ui.show_notification(title=common.get_local_string(30105), msg=msg)