Ejemplo n.º 1
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)
            common.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))
            for key, value in iteritems(summary):
                if key in PROFILE_DEBUG_INFO:
                    common.debug('Profile info {}', {key: value})
                if key == 'profileName':  # The profile name is coded as HTML
                    value = parse_html(value)
                g.LOCAL_DB.set_profile_config(key, value, guid)
            g.LOCAL_DB.set_profile_config('avatar', avatar_url, guid)
            sort_order += 1
        _delete_non_existing_profiles(current_guids)
    except Exception:
        import traceback
        common.error(g.py2_decode(traceback.format_exc(), 'latin-1'))
        common.error('Profile list data: {}', profiles_list)
        raise InvalidProfilesError
Ejemplo n.º 2
0
def _get_item(mediatype, filename):
    # To ensure compatibility with previously exported items,
    # make the filename legal
    fname = makeLegalFilename(filename)
    untranslated_path = os.path.dirname(g.py2_decode(fname))
    translated_path = os.path.dirname(g.py2_decode(xbmc.translatePath(fname)))
    shortname = os.path.basename(g.py2_decode(xbmc.translatePath(fname)))
    # We get the data from Kodi library using filters.
    # This is much faster than loading all episodes in memory

    # First build the path filter, we may have to search in both special and translated path
    path_filter = {'field': 'path', 'operator': 'startswith', 'value': translated_path} \
        if fname[:10] != 'special://' \
        else {'or': [
            {'field': 'path', 'operator': 'startswith', 'value': translated_path},
            {'field': 'path', 'operator': 'startswith', 'value': untranslated_path}
        ]}

    # Now build the all request and call the json-rpc function through common.get_library_items
    library_item = common.get_library_items(
        mediatype, {
            'and': [
                path_filter, {
                    'field': 'filename',
                    'operator': 'is',
                    'value': shortname
                }
            ]
        })[0]
    if not library_item:
        raise ItemNotFound
    return common.get_library_item_details(mediatype,
                                           library_item[mediatype + 'id'])
Ejemplo n.º 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(xbmc.translatePath(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:
        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
    try:
        cookie_jar.clear(domain='.netflix.com', path='/', name='flwssn')
    except KeyError:
        pass
    log_cookie(cookie_jar)
    return cookie_jar
Ejemplo n.º 4
0
def _lib_folders(section):
    section_dir = g.py2_decode(
        xbmc.translatePath(
            makeLegalFilename('/'.join([library_path(), section]))))
    return [
        g.py2_decode(
            makeLegalFilename('/'.join([section_dir,
                                        folder.decode('utf-8')])))
        for folder in xbmcvfs.listdir(section_dir)[0]
    ]
Ejemplo n.º 5
0
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
    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
        error(g.py2_decode(traceback.format_exc(), 'latin-1'))
    def load_manifest(self, viewable_id):
        """
        Loads the manifests for the given viewable_id and returns a mpd-XML-Manifest

        :param viewable_id: The id of of the viewable
        :return: MPD XML Manifest or False if no success
        """
        try:
            manifest = self._load_manifest(viewable_id, g.get_esn())
        except MSLError as exc:
            if 'Email or password is incorrect' in g.py2_decode(str(exc)):
                # Known cases when MSL error "Email or password is incorrect." can happen:
                # - If user change the password when the nf session was still active
                # - Netflix has reset the password for suspicious activity when the nf session was still active
                # Then clear the credentials and also user tokens.
                common.purge_credentials()
                self.msl_requests.crypto.clear_user_id_tokens()
            raise
        # Disable 1080p Unlock for now, as it is broken due to Netflix changes
        # if (g.ADDON.getSettingBool('enable_1080p_unlock') and
        #         not g.ADDON.getSettingBool('enable_vp9_profiles') and
        #         not has_1080p(manifest)):
        #     common.debug('Manifest has no 1080p viewables, trying unlock')
        #     manifest = self.get_edge_manifest(viewable_id, manifest)
        return self.__tranform_to_dash(manifest)
Ejemplo n.º 7
0
def execute_tasks(title, tasks, task_handler, **kwargs):
    """
    Run all tasks through task_handler and display a progress dialog in the GUI. Additional kwargs will be
    passed into task_handler on each invocation.
    Returns a list of errors that occured during execution of tasks.
    """
    errors = []
    notify_errors = kwargs.pop('notify_errors', False)
    progress = xbmcgui.DialogProgress()
    progress.create(title)
    for task_num, task in enumerate(tasks):
        task_title = task.get('title', 'Unknown Task')
        progress.update(int(task_num * 100 / len(tasks)), task_title)
#        xbmc.sleep(25)
        if progress.iscanceled():
            break
        if not task:
            continue
        try:
            task_handler(task, **kwargs)
        except Exception as exc:  # pylint: disable=broad-except
            import traceback
            common.error(g.py2_decode(traceback.format_exc(), 'latin-1'))
            errors.append({
                'task_title': task_title,
                'error': '{}: {}'.format(type(exc).__name__, exc)})
    show_library_task_errors(notify_errors, errors)
    return errors
Ejemplo n.º 8
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'))
Ejemplo n.º 9
0
def parse_profiles(profiles_list_data):
    """Parse profile information from Netflix response"""
    try:
        profiles_list = OrderedDict(resolve_refs(profiles_list_data['profilesList'], profiles_list_data))
        if not profiles_list:
            raise InvalidProfilesError('It has not been possible to obtain the list of profiles.')
        _delete_non_existing_profiles(profiles_list)
        sort_order = 0
        for guid, profile in list(profiles_list.items()):
            common.debug('Parsing profile {}', guid)
            avatar_url = _get_avatar(profiles_list_data, profile)
            profile = profile['summary']
            is_active = profile.pop('isActive')
            g.LOCAL_DB.set_profile(guid, is_active, sort_order)
            g.SHARED_DB.set_profile(guid, sort_order)
            for key, value in list(profile.items()):
                if key in PROFILE_DEBUG_INFO:
                    common.debug('Profile info {}', {key: value})
                if key == 'profileName':  # The profile name is coded as HTML
                    value = parse_html(value)
                g.LOCAL_DB.set_profile_config(key, value, guid)
            g.LOCAL_DB.set_profile_config('avatar', avatar_url, guid)
            sort_order += 1
    except Exception:
        import traceback
        common.error(g.py2_decode(traceback.format_exc(), 'latin-1'))
        common.error('Profile list data: {}', profiles_list_data)
        raise InvalidProfilesError
Ejemplo n.º 10
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
    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.'
        error(err_msg)
        raise BackendNotReady(g.py2_encode(err_msg, encoding='latin-1'))
    _raise_for_error(callname, result)
    return result
Ejemplo n.º 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:
        try:
            raise apierrors.__dict__[exc.reason]()
        except KeyError:
            raise Exception('The service has returned: {}'.format(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.'
        error(err_msg)
        raise BackendNotReady(g.py2_encode(err_msg, encoding='latin-1'))
    return result
Ejemplo n.º 12
0
 def onNotification(self, sender, method, data):  # pylint: disable=unused-argument
     """
     Callback for Kodi notifications that handles and dispatches playback events
     """
     if not self.tracking:
         return
     try:
         if method == 'Player.OnAVStart':
             # WARNING: Do not get playerid from 'data',
             # Because when Up Next add-on play a video while we are inside Netflix add-on and
             # not externally like Kodi library, the playerid become -1 this id does not exist
             self._on_playback_started()
         elif method == 'Player.OnSeek':
             self._on_playback_seek()
         elif method == 'Player.OnPause':
             self._on_playback_pause()
         elif method == 'Player.OnResume':
             self._on_playback_resume()
         elif method == 'Player.OnStop':
             # When Up Next add-on starts the next video, the 'Player.OnStop' notification WILL BE NOT PERFORMED
             # then is manually generated by _play_callback method
             self._on_playback_stopped()
     except Exception:  # pylint: disable=broad-except
         import traceback
         common.error(g.py2_decode(traceback.format_exc(), 'latin-1'))
Ejemplo n.º 13
0
def get_upnext_info(videoid, current_episode, metadata):
    """Determine next episode and send an AddonSignal to UpNext addon"""
    try:
        next_episode_id = _find_next_episode(videoid, metadata)
    except (TypeError, KeyError):
        # import traceback
        # common.debug(traceback.format_exc())
        common.debug('There is no next episode, not setting up Up Next')
        return {}

    common.debug('Next episode is {}', next_episode_id)
    next_episode = infolabels.get_info_for_playback(next_episode_id, True)
    next_info = {
        'current_episode': _upnext_info(videoid, *current_episode),
        'next_episode': _upnext_info(next_episode_id, *next_episode)
    }

    if (xbmc.getInfoLabel('Container.PluginName') != g.ADDON.getAddonInfo('id')
            and library.is_in_library(next_episode_id)):
        filepath = g.SHARED_DB.get_episode_filepath(next_episode_id.tvshowid,
                                                    next_episode_id.seasonid,
                                                    next_episode_id.episodeid)
        # next_info['play_info'] = {'play_path': g.py2_decode(xbmc.translatePath(filepath))}
        next_info['play_url'] = g.py2_decode(xbmc.translatePath(filepath))
    else:
        # next_info['play_info'] = {'play_path': common.build_url(videoid=next_episode_id,
        #                                                         mode=g.MODE_PLAY)}
        next_info['play_url'] = common.build_url(videoid=next_episode_id,
                                                 mode=g.MODE_PLAY)
    if 'creditsOffset' in metadata[0]:
        next_info['notification_offset'] = metadata[0]['creditsOffset']
    return next_info
Ejemplo n.º 14
0
def export_item(item_task, library_home):
    """Create strm file for an item and add it to the library"""
    # Paths must be legal to ensure NFS compatibility
    destination_folder = g.py2_decode(xbmc.makeLegalFilename('/'.join(
        [library_home, item_task['section'], item_task['destination']])))
    _create_destination_folder(destination_folder)
    if item_task['is_strm']:
        export_filename = g.py2_decode(xbmc.makeLegalFilename('/'.join(
            [destination_folder, item_task['filename'] + '.strm'])))
        _add_to_library(item_task['videoid'], export_filename, (item_task['nfo_data'] is not None))
        _write_strm_file(item_task, export_filename)
    if item_task['nfo_data'] is not None:
        nfo_filename = g.py2_decode(xbmc.makeLegalFilename('/'.join(
            [destination_folder, item_task['filename'] + '.nfo'])))
        _write_nfo_file(item_task['nfo_data'], nfo_filename)
    common.debug('Exported {}', item_task['title'])
Ejemplo n.º 15
0
def run(argv):
    # pylint: disable=broad-except,ungrouped-imports
    # 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)

    reset_log_level_global_var()
    info('Started (Version {})'.format(g.VERSION_RAW))
    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:
        try:
            if _check_valid_credentials():
                if g.IS_ADDON_FIRSTRUN:
                    if check_addon_upgrade():
                        from resources.lib.config_wizard import run_addon_configuration
                        run_addon_configuration()
                route([part for part in g.PATH.split('/') if part])
            else:
                success = False
        except BackendNotReady:
            from resources.lib.kodi.ui import show_backend_not_ready
            show_backend_not_ready()
            success = False
        except Exception as exc:
            import traceback
            from resources.lib.kodi.ui import show_addon_error_info
            error(g.py2_decode(traceback.format_exc(), 'latin-1'))
            show_addon_error_info(exc)
            success = False

    if not success:
        _handle_endofdirectory()
    log_time_trace()
Ejemplo n.º 16
0
 def _login(self, modal_error_message=False):
     """Perform account login"""
     # If exists get the current esn value before extract a new session data
     current_esn = g.get_esn()
     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']
         common.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)
             common.info('Login successful')
             ui.show_notification(common.get_local_string(30109))
             self.update_session_data(current_esn)
             return True
         except (LoginValidateError, LoginValidateErrorIncorrectPassword) 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 InvalidMembershipStatusError:
         ui.show_error_info(common.get_local_string(30008),
                            common.get_local_string(30180),
                            False, True)
     except Exception:  # pylint: disable=broad-except
         import traceback
         common.error(g.py2_decode(traceback.format_exc(), 'latin-1'))
         self.session.cookies.clear()
         raise
     return False
Ejemplo n.º 17
0
def extract_json(content, name):
    """Extract json from netflix content page"""
    common.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:
        if json_str:
            common.error('JSON string trying to load: {}', json_str)
        import traceback
        common.error(g.py2_decode(traceback.format_exc(), 'latin-1'))
        raise WebsiteParsingError('Unable to extract {}'.format(name))
Ejemplo n.º 18
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
         common.error(g.py2_decode(traceback.format_exc(), 'latin-1'))
Ejemplo n.º 19
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)
            common.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]
            if 'incorrect_password' in error_code:
                raise LoginValidateErrorIncorrectPassword(
                    common.remove_html_tags(error_description))
            raise LoginValidateError(
                common.remove_html_tags(error_description))
        except (AttributeError, KeyError):
            import traceback
            common.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.')
            common.error(error_msg)
            raise LoginValidateError(error_msg)
Ejemplo n.º 20
0
def get_upnext_info(videoid, videoid_next_episode, info_data, metadata,
                    is_played_from_addon):
    """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_addon:
        url = common.build_url(
            videoid=videoid_next_episode,
            mode=g.MODE_PLAY,
            params={'profile_guid': g.LOCAL_DB.get_active_profile_guid()})
    else:
        # Played from Kodi library get the strm file path
        file_path = g.SHARED_DB.get_episode_filepath(
            videoid_next_episode.tvshowid, videoid_next_episode.seasonid,
            videoid_next_episode.episodeid)
        url = g.py2_decode(xbmc.translatePath(file_path))
    upnext_info['play_info'] = {'play_path': url}

    if 'creditsOffset' in metadata[0]:
        upnext_info['notification_offset'] = metadata[0]['creditsOffset']
    return upnext_info
Ejemplo n.º 21
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'])))
Ejemplo n.º 22
0
 def try_refresh_session_data(self, raise_exception=False):
     """Refresh session_data from the Netflix website"""
     # pylint: disable=broad-except
     try:
         self.auth_url = website.extract_session_data(
             self._get('browse'))['auth_url']
         self.update_session_data()
         common.debug('Successfully refreshed session data')
         return True
     except InvalidMembershipStatusError:
         raise
     except (WebsiteParsingError, InvalidMembershipStatusAnonymous,
             LoginValidateErrorIncorrectPassword) as exc:
         # Possible known causes:
         # -Cookies may not work anymore most likely due to updates in the website
         # -Login password has been changed
         # -Expired cookie profiles? might cause InvalidMembershipStatusAnonymous (i am not really sure)
         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, (InvalidMembershipStatusAnonymous,
                             LoginValidateErrorIncorrectPassword)):
             # 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._login()
     except requests.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:
         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
Ejemplo n.º 23
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
Ejemplo n.º 24
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)
Ejemplo n.º 25
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)
    common.set_credentials(email, password)
Ejemplo n.º 26
0
def remove_item(item_task, library_home=None):
    """Remove an item from the library and delete if from disk"""
    # pylint: disable=unused-argument, broad-except

    common.info('Removing {} from library', item_task['title'])

    exported_filename = xbmc.translatePath(item_task['filepath'])
    videoid = item_task['videoid']
    common.debug('VideoId: {}', videoid)
    try:
        parent_folder = xbmc.translatePath(os.path.dirname(exported_filename))
        if xbmcvfs.exists(exported_filename):
            xbmcvfs.delete(exported_filename)
        else:
            common.warn('Cannot delete {}, file does not exist',
                        g.py2_decode(exported_filename))
        # Remove the NFO files if exists
        nfo_file = os.path.splitext(
            g.py2_decode(exported_filename))[0] + '.nfo'
        if xbmcvfs.exists(nfo_file):
            xbmcvfs.delete(nfo_file)
        dirs, files = xbmcvfs.listdir(parent_folder)
        tvshow_nfo_file = xbmc.makeLegalFilename('/'.join(
            [g.py2_decode(parent_folder), 'tvshow.nfo']))
        # Remove tvshow_nfo_file only when is the last file
        # (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_from_db(videoid)
    except ItemNotFound:
        common.warn('The video with id {} not exists in the database', videoid)
    except Exception as exc:
        import traceback
        common.error(traceback.format_exc())
        ui.show_addon_error_info(exc)
Ejemplo n.º 27
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.account_hash, self.session.cookies)
         common.debug('Successfully refreshed session data')
         return True
     except InvalidMembershipStatusError:
         raise
     except (WebsiteParsingError, InvalidMembershipStatusAnonymous,
             LoginValidateErrorIncorrectPassword) 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, (InvalidMembershipStatusAnonymous,
                             LoginValidateErrorIncorrectPassword)):
             # 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._login()
     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
Ejemplo n.º 28
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
         error(g.py2_decode(traceback.format_exc(), 'latin-1'))
         show_notification(': '.join(
             (exc.__class__.__name__, unicode(exc))))
     return self.controller.waitForAbort(1)
Ejemplo n.º 29
0
 def error_catching_wrapper(*args, **kwargs):
     try:
         return func(*args, **kwargs)
     except Exception as exc:
         if isinstance(exc, MSLError):
             message = g.py2_decode(str(exc))
         else:
             message = str(exc)
         ui.show_error_info(common.get_local_string(30028), message,
                            unknown_error=not message,
                            netflix_error=isinstance(exc, MSLError))
         raise
Ejemplo n.º 30
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 common.is_debug_verbose():
        common.save_file('manifest.mpd', xml)
    return xml.decode('utf-8').replace('\n', '').replace('\r',
                                                         '').encode('utf-8')