Ejemplo n.º 1
0
 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)
     try:
         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)
     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_from(MSLError(msg), 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
Ejemplo n.º 2
0
    def perform_key_handshake(self, data=None):  # pylint: disable=unused-argument
        """Perform a key handshake and initialize crypto keys"""
        esn = g.get_esn()
        if not esn:
            common.warn('Cannot perform key handshake, missing ESN')
            return False

        common.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()
        common.debug('Key handshake successful')
        return True
Ejemplo n.º 3
0
    def _load_manifest(self, viewable_id, esn):
        cache_identifier = esn + '_' + unicode(viewable_id)
        try:
            # The manifest must be requested once and maintained for its entire duration
            manifest = G.CACHE.get(CACHE_MANIFESTS, cache_identifier)
            expiration = int(manifest['expiration'] / 1000)
            if (expiration - time.time()) < 14400:
                # Some devices remain active even longer than 48 hours, if the manifest is at the limit of the deadline
                # when requested by am_stream_continuity.py / events_handler.py will cause problems
                # if it is already expired, so we guarantee a minimum of safety ttl of 4h (14400s = 4 hours)
                raise CacheMiss()
            if LOG.level == LOG.LEVEL_VERBOSE:
                LOG.debug('Manifest for {} obtained from the cache',
                          viewable_id)
                # Save the manifest to disk as reference
                common.save_file_def('manifest.json',
                                     json.dumps(manifest).encode('utf-8'))
            return manifest
        except CacheMiss:
            pass

        isa_addon = xbmcaddon.Addon('inputstream.adaptive')
        hdcp_override = isa_addon.getSettingBool('HDCPOVERRIDE')
        hdcp_4k_capable = common.is_device_4k_capable(
        ) or G.ADDON.getSettingBool('enable_force_hdcp')

        hdcp_version = []
        if not hdcp_4k_capable and hdcp_override:
            hdcp_version = ['1.4']
        if hdcp_4k_capable and hdcp_override:
            hdcp_version = ['2.2']

        LOG.info('Requesting manifest for {} with ESN {} and HDCP {}',
                 viewable_id,
                 common.censure(esn) if G.ADDON.getSetting('esn') else esn,
                 hdcp_version)

        profiles = enabled_profiles()
        from pprint import pformat
        LOG.info('Requested profiles:\n{}', pformat(profiles, indent=2))

        params = {
            'type':
            'standard',
            'viewableId': [viewable_id],
            'profiles':
            profiles,
            'flavor':
            'PRE_FETCH',
            'drmType':
            'widevine',
            'drmVersion':
            25,
            'usePsshBox':
            True,
            'isBranching':
            False,
            'isNonMember':
            False,
            'isUIAutoPlay':
            False,
            'useHttpsStreams':
            True,
            'imageSubtitleHeight':
            1080,
            'uiVersion':
            G.LOCAL_DB.get_value('ui_version', '', table=TABLE_SESSION),
            'uiPlatform':
            'SHAKTI',
            'clientVersion':
            G.LOCAL_DB.get_value('client_version', '', table=TABLE_SESSION),
            'desiredVmaf':
            'plus_lts',  # phone_plus_exp can be used to mobile, not tested
            'supportsPreReleasePin':
            True,
            'supportsWatermark':
            True,
            'supportsUnequalizedDownloadables':
            True,
            'showAllSubDubTracks':
            False,
            'titleSpecificData': {
                unicode(viewable_id): {
                    'unletterboxed': True
                }
            },
            'videoOutputInfo': [{
                'type': 'DigitalVideoOutputDescriptor',
                'outputType': 'unknown',
                'supportedHdcpVersions': hdcp_version,
                'isHdcpEngaged': hdcp_override
            }],
            'preferAssistiveAudio':
            False
        }

        if 'linux' in common.get_system_platform(
        ) and 'arm' in common.get_machine():
            # 24/06/2020 To get until to 1080P resolutions under arm devices (ChromeOS), android excluded,
            #   is mandatory to add the widevine challenge data (key request) to the manifest request.
            # Is not possible get the key request from the default_crypto, is needed to implement
            #   the wv crypto (used for android) but currently InputStreamAdaptive support this interface only
            #   under android OS.
            # As workaround: Initially we pass an hardcoded challenge data needed to play the first video,
            #   then when ISA perform the license callback we replace it with the fresh license challenge data.
            params['challenge'] = self.manifest_challenge

        endpoint_url = ENDPOINTS[
            'manifest'] + '?reqAttempt=1&reqPriority=0&reqName=prefetch/manifest'
        manifest = self.msl_requests.chunked_request(
            endpoint_url,
            self.msl_requests.build_request_data('/manifest', params),
            esn,
            disable_msl_switch=False)
        if LOG.level == LOG.LEVEL_VERBOSE:
            # Save the manifest to disk as reference
            common.save_file_def('manifest.json',
                                 json.dumps(manifest).encode('utf-8'))
        # Save the manifest to the cache to retrieve it during its validity
        expiration = int(manifest['expiration'] / 1000)
        G.CACHE.add(CACHE_MANIFESTS,
                    cache_identifier,
                    manifest,
                    expires=expiration)
        return manifest
Ejemplo n.º 4
0
    def _load_manifest(self, viewable_id, esn):
        cache_identifier = esn + '_' + unicode(viewable_id)
        try:
            # The manifest must be requested once and maintained for its entire duration
            manifest = g.CACHE.get(CACHE_MANIFESTS, cache_identifier)
            expiration = int(manifest['expiration'] / 1000)
            if (expiration - time.time()) < 14400:
                # Some devices remain active even longer than 48 hours, if the manifest is at the limit of the deadline
                # when requested by stream_continuity.py / events_handler.py will cause problems
                # if it is already expired, so we guarantee a minimum of safety ttl of 4h (14400s = 4 hours)
                raise CacheMiss()
            if common.is_debug_verbose():
                common.debug('Manifest for {} obtained from the cache',
                             viewable_id)
                # Save the manifest to disk as reference
                common.save_file('manifest.json',
                                 json.dumps(manifest).encode('utf-8'))
            return manifest
        except CacheMiss:
            pass

        isa_addon = xbmcaddon.Addon('inputstream.adaptive')
        hdcp_override = isa_addon is not None and isa_addon.getSettingBool(
            'HDCPOVERRIDE')
        hdcp_4k_capable = common.is_device_4k_capable(
        ) or g.ADDON.getSettingBool('enable_force_hdcp')

        hdcp_version = []
        if not hdcp_4k_capable and hdcp_override:
            hdcp_version = ['1.4']
        if hdcp_4k_capable and hdcp_override:
            hdcp_version = ['2.2']

        common.info('Requesting manifest for {} with ESN {} and HDCP {}',
                    viewable_id,
                    common.censure(esn) if g.ADDON.getSetting('esn') else esn,
                    hdcp_version)

        profiles = enabled_profiles()
        from pprint import pformat
        common.info('Requested profiles:\n{}', pformat(profiles, indent=2))

        params = {
            'type':
            'standard',
            'viewableId': [viewable_id],
            'profiles':
            profiles,
            'flavor':
            'PRE_FETCH',
            'drmType':
            'widevine',
            'drmVersion':
            25,
            'usePsshBox':
            True,
            'isBranching':
            False,
            'isNonMember':
            False,
            'isUIAutoPlay':
            False,
            'useHttpsStreams':
            True,
            'imageSubtitleHeight':
            1080,
            'uiVersion':
            'shakti-v93016808',
            'uiPlatform':
            'SHAKTI',
            'clientVersion':
            '6.0016.426.011',
            'desiredVmaf':
            'plus_lts',  # phone_plus_exp can be used to mobile, not tested
            'supportsPreReleasePin':
            True,
            'supportsWatermark':
            True,
            'supportsUnequalizedDownloadables':
            True,
            'showAllSubDubTracks':
            False,
            'titleSpecificData': {
                viewable_id: {
                    'unletterboxed': True
                }
            },
            'videoOutputInfo': [{
                'type': 'DigitalVideoOutputDescriptor',
                'outputType': 'unknown',
                'supportedHdcpVersions': hdcp_version,
                'isHdcpEngaged': hdcp_override
            }],
            'preferAssistiveAudio':
            False
        }

        manifest = self.msl_requests.chunked_request(
            ENDPOINTS['manifest'],
            self.msl_requests.build_request_data('/manifest', params),
            esn,
            disable_msl_switch=False)
        if common.is_debug_verbose():
            # Save the manifest to disk as reference
            common.save_file('manifest.json',
                             json.dumps(manifest).encode('utf-8'))
        # Save the manifest to the cache to retrieve it during its validity
        expiration = int(manifest['expiration'] / 1000)
        g.CACHE.add(CACHE_MANIFESTS,
                    cache_identifier,
                    manifest,
                    expires=expiration)
        if 'result' in manifest:
            return manifest['result']
        return manifest
def _save_system_info():
    # Ask to save to a file
    filename = 'NFSystemInfo.txt'
    path = ui.show_browse_dialog(
        common.get_local_string(30603) + ' - ' + filename)
    if not path:
        return
    # This collect the main data to allow verification checks for problems
    data = 'Netflix add-on version: ' + G.VERSION
    data += '\nDebug logging level: ' + LOG.level
    data += '\nSystem platform: ' + common.get_system_platform()
    data += '\nMachine architecture: ' + common.get_machine()
    data += '\nUser agent string: ' + common.get_user_agent()
    data += '\n\n' + '#### Widevine info ####\n'
    if common.get_system_platform() == 'android':
        data += '\nSystem ID: ' + G.LOCAL_DB.get_value(
            'drm_system_id', '--not obtained--', TABLE_SESSION)
        data += '\nSecurity level: ' + G.LOCAL_DB.get_value(
            'drm_security_level', '--not obtained--', TABLE_SESSION)
        data += '\nHDCP level: ' + G.LOCAL_DB.get_value(
            'drm_hdcp_level', '--not obtained--', TABLE_SESSION)
        wv_force_sec_lev = G.LOCAL_DB.get_value('widevine_force_seclev',
                                                WidevineForceSecLev.DISABLED,
                                                TABLE_SESSION)
        data += '\nForced security level setting is: ' + wv_force_sec_lev
    else:
        try:
            from ctypes import (CDLL, c_char_p)
            cdm_lib_file_path = _get_cdm_file_path()
            try:
                lib = CDLL(cdm_lib_file_path)
                data += '\nLibrary status: Correctly loaded'
                try:
                    lib.GetCdmVersion.restype = c_char_p
                    data += '\nVersion: ' + lib.GetCdmVersion().decode('utf-8')
                except Exception:  # pylint: disable=broad-except
                    # This can happen if the endpoint 'GetCdmVersion' is changed
                    data += '\nVersion: Reading error'
            except Exception as exc:  # pylint: disable=broad-except
                # This should not happen but currently InputStream Helper does not perform any verification checks on
                # downloaded and installed files, so if due to an problem it installs a CDM for a different architecture
                # or the files are corrupted, the user can no longer play videos and does not know what to do
                data += '\nLibrary status: Error loading failed'
                data += '\n>>> It is possible that is installed a CDM of a wrong architecture or is corrupted'
                data += '\n>>> Suggested solutions:'
                data += '\n>>> - Restore a previous version of Widevine library from InputStream Helper add-on settings'
                data += '\n>>> - Report the problem to the GitHub of InputStream Helper add-on'
                data += '\n>>> Error details: {}'.format(exc)
        except Exception as exc:  # pylint: disable=broad-except
            data += '\nThe data could not be obtained. Error details: {}'.format(
                exc)
    data += '\n\n' + '#### ESN ####\n'
    esn = get_esn() or '--not obtained--'
    data += '\nUsed ESN: ' + common.censure(esn) if len(esn) > 50 else esn
    data += '\nWebsite ESN: ' + (get_website_esn() or '--not obtained--')
    data += '\nAndroid generated ESN: ' + (generate_android_esn()
                                           or '--not obtained--')
    if common.get_system_platform() == 'android':
        data += '\n\n' + '#### Device system info ####\n'
        try:
            import subprocess
            info = subprocess.check_output(['/system/bin/getprop'
                                            ]).decode('utf-8')
            data += '\n' + info
        except Exception as exc:  # pylint: disable=broad-except
            data += '\nThe data could not be obtained. Error: {}'.format(exc)
    data += '\n'
    try:
        common.save_file(common.join_folders_paths(path, filename),
                         data.encode('utf-8'))
        ui.show_notification('{}: {}'.format(xbmc.getLocalizedString(35259),
                                             filename))  # 35259=Saved
    except Exception as exc:  # pylint: disable=broad-except
        LOG.error('save_file error: {}', exc)
        ui.show_notification('Error! Try another path')
Ejemplo n.º 6
0
    def _get_manifest(self, viewable_id, esn, challenge, sid):
        from pprint import pformat
        cache_identifier = f'{esn}_{viewable_id}'
        try:
            # The manifest must be requested once and maintained for its entire duration
            manifest = G.CACHE.get(CACHE_MANIFESTS, cache_identifier)
            expiration = int(manifest['expiration'] / 1000)
            if (expiration - time.time()) < 14400:
                # Some devices remain active even longer than 48 hours, if the manifest is at the limit of the deadline
                # when requested by am_stream_continuity.py / events_handler.py will cause problems
                # if it is already expired, so we guarantee a minimum of safety ttl of 4h (14400s = 4 hours)
                raise CacheMiss()
            if LOG.is_enabled:
                LOG.debug('Manifest for {} obtained from the cache',
                          viewable_id)
                # Save the manifest to disk as reference
                common.save_file_def('manifest.json',
                                     json.dumps(manifest).encode('utf-8'))
            return manifest
        except CacheMiss:
            pass

        isa_addon = xbmcaddon.Addon('inputstream.adaptive')
        hdcp_override = isa_addon.getSettingBool('HDCPOVERRIDE')
        hdcp_4k_capable = common.is_device_4k_capable(
        ) or G.ADDON.getSettingBool('enable_force_hdcp')

        hdcp_version = []
        if not hdcp_4k_capable and hdcp_override:
            hdcp_version = ['1.4']
        if hdcp_4k_capable and hdcp_override:
            hdcp_version = ['2.2']

        manifest_ver = G.ADDON.getSettingString('msl_manifest_version')
        profiles = enabled_profiles()

        LOG.info(
            'Requesting manifest (version {}) for\nVIDEO ID: {}\nESN: {}\nHDCP: {}\nPROFILES:\n{}',
            manifest_ver, viewable_id,
            common.censure(esn) if len(esn) > 50 else esn, hdcp_version,
            pformat(profiles, indent=2))
        if manifest_ver == 'v1':
            endpoint_url, request_data = self._build_manifest_v1(
                viewable_id=viewable_id,
                hdcp_version=hdcp_version,
                hdcp_override=hdcp_override,
                profiles=profiles,
                challenge=challenge)
        else:  # Default - most recent version
            endpoint_url, request_data = self._build_manifest_v2(
                viewable_id=viewable_id,
                hdcp_version=hdcp_version,
                hdcp_override=hdcp_override,
                profiles=profiles,
                challenge=challenge,
                sid=sid)
        manifest = self.msl_requests.chunked_request(endpoint_url,
                                                     request_data,
                                                     esn,
                                                     disable_msl_switch=False)
        if LOG.is_enabled:
            # Save the manifest to disk as reference
            common.save_file_def('manifest.json',
                                 json.dumps(manifest).encode('utf-8'))
        # Save the manifest to the cache to retrieve it during its validity
        expiration = int(manifest['expiration'] / 1000)
        G.CACHE.add(CACHE_MANIFESTS,
                    cache_identifier,
                    manifest,
                    expires=expiration)
        return manifest
Ejemplo n.º 7
0
    def _get_manifest(self, viewable_id, esn, challenge, sid):
        cache_identifier = f'{esn}_{viewable_id}'
        try:
            # The manifest must be requested once and maintained for its entire duration
            manifest = G.CACHE.get(CACHE_MANIFESTS, cache_identifier)
            expiration = int(manifest['expiration'] / 1000)
            if (expiration - time.time()) < 14400:
                # Some devices remain active even longer than 48 hours, if the manifest is at the limit of the deadline
                # when requested by am_stream_continuity.py / events_handler.py will cause problems
                # if it is already expired, so we guarantee a minimum of safety ttl of 4h (14400s = 4 hours)
                raise CacheMiss()
            if LOG.is_enabled:
                LOG.debug('Manifest for {} obtained from the cache',
                          viewable_id)
                # Save the manifest to disk as reference
                common.save_file_def('manifest.json',
                                     json.dumps(manifest).encode('utf-8'))
            return manifest
        except CacheMiss:
            pass

        isa_addon = xbmcaddon.Addon('inputstream.adaptive')
        hdcp_override = isa_addon.getSettingBool('HDCPOVERRIDE')
        hdcp_4k_capable = common.is_device_4k_capable(
        ) or G.ADDON.getSettingBool('enable_force_hdcp')

        hdcp_version = []
        if not hdcp_4k_capable and hdcp_override:
            hdcp_version = ['1.4']
        if hdcp_4k_capable and hdcp_override:
            hdcp_version = ['2.2']

        LOG.info('Requesting licensed manifest for {} with ESN {} and HDCP {}',
                 viewable_id,
                 common.censure(esn) if len(esn) > 50 else esn, hdcp_version)

        profiles = enabled_profiles()
        from pprint import pformat
        LOG.info('Requested profiles:\n{}', pformat(profiles, indent=2))

        params = {
            'type':
            'standard',
            'manifestVersion':
            'v2',
            'viewableId':
            viewable_id,
            'profiles':
            profiles,
            'flavor':
            'PRE_FETCH',
            'drmType':
            'widevine',
            'drmVersion':
            25,
            'usePsshBox':
            True,
            'isBranching':
            False,
            'useHttpsStreams':
            True,
            'supportsUnequalizedDownloadables':
            True,
            'imageSubtitleHeight':
            1080,
            'uiVersion':
            G.LOCAL_DB.get_value('ui_version', '', table=TABLE_SESSION),
            'uiPlatform':
            'SHAKTI',
            'clientVersion':
            G.LOCAL_DB.get_value('client_version', '', table=TABLE_SESSION),
            'supportsPreReleasePin':
            True,
            'supportsWatermark':
            True,
            'showAllSubDubTracks':
            False,
            'videoOutputInfo': [{
                'type': 'DigitalVideoOutputDescriptor',
                'outputType': 'unknown',
                'supportedHdcpVersions': hdcp_version,
                'isHdcpEngaged': hdcp_override
            }],
            'titleSpecificData': {
                str(viewable_id): {
                    'unletterboxed': True
                }
            },
            'preferAssistiveAudio':
            False,
            'isUIAutoPlay':
            False,
            'isNonMember':
            False,
            'desiredVmaf':
            'plus_lts',  # phone_plus_exp can be used to mobile, not tested
            'desiredSegmentVmaf':
            'plus_lts',
            'requestSegmentVmaf':
            False,
            'supportsPartialHydration':
            False,
            'contentPlaygraph': [],
            'profileGroups': [{
                'name': 'default',
                'profiles': profiles
            }],
            'licenseType':
            'limited'  # standard / limited
        }
        if challenge and sid:
            params['challenge'] = challenge
            params['challenges'] = {
                'default': [{
                    'drmSessionId': sid,
                    'clientTime': int(time.time()),
                    'challengeBase64': challenge
                }]
            }

        endpoint_url = ENDPOINTS['manifest'] + create_req_params(
            0, 'licensedManifest')
        manifest = self.msl_requests.chunked_request(
            endpoint_url,
            self.msl_requests.build_request_data('licensedManifest', params),
            esn,
            disable_msl_switch=False)
        if LOG.is_enabled:
            # Save the manifest to disk as reference
            common.save_file_def('manifest.json',
                                 json.dumps(manifest).encode('utf-8'))
        # Save the manifest to the cache to retrieve it during its validity
        expiration = int(manifest['expiration'] / 1000)
        G.CACHE.add(CACHE_MANIFESTS,
                    cache_identifier,
                    manifest,
                    expires=expiration)
        return manifest
Ejemplo n.º 8
0
    def _get_manifest(self, viewable_id, esn, challenge, sid):
        if common.get_system_platform() != 'android' and (not challenge or not sid):
            LOG.error('DRM session data not valid (Session ID: {}, Challenge: {})', challenge, sid)

        from pprint import pformat
        cache_identifier = f'{esn}_{viewable_id}'
        try:
            # The manifest must be requested once and maintained for its entire duration
            manifest = G.CACHE.get(CACHE_MANIFESTS, cache_identifier)
            expiration = int(manifest['expiration'] / 1000)
            if (expiration - time.time()) < 14400:
                # Some devices remain active even longer than 48 hours, if the manifest is at the limit of the deadline
                # when requested by am_stream_continuity.py / events_handler.py will cause problems
                # if it is already expired, so we guarantee a minimum of safety ttl of 4h (14400s = 4 hours)
                raise CacheMiss()
            if LOG.is_enabled:
                LOG.debug('Manifest for {} obtained from the cache', viewable_id)
                # Save the manifest to disk as reference
                common.save_file_def('manifest.json', json.dumps(manifest).encode('utf-8'))
            return manifest
        except CacheMiss:
            pass

        isa_addon = xbmcaddon.Addon('inputstream.adaptive')
        hdcp_override = isa_addon.getSettingBool('HDCPOVERRIDE')
        hdcp_4k_capable = common.is_device_4k_capable() or G.ADDON.getSettingBool('enable_force_hdcp')

        hdcp_version = []
        if not hdcp_4k_capable and hdcp_override:
            hdcp_version = ['1.4']
        if hdcp_4k_capable and hdcp_override:
            hdcp_version = ['2.2']

        manifest_ver = G.ADDON.getSettingString('msl_manifest_version')
        profiles = enabled_profiles()

        LOG.info('Requesting manifest (version {}) for\nVIDEO ID: {}\nESN: {}\nHDCP: {}\nPROFILES:\n{}',
                 manifest_ver,
                 viewable_id,
                 common.censure(esn) if len(esn) > 50 else esn,
                 hdcp_version,
                 pformat(profiles, indent=2))
        xid = None
        # On non-Android systems, we pre-initialize the DRM with default PSSH/KID, this allows to obtain Challenge/SID
        # to achieve 1080p resolution.
        # On Android, pre-initialize DRM is possible but cannot keep the same DRM session, will result in an error
        # because the manifest license data do not match the current DRM session, then we do not use it and
        # we still make the license requests.
        if manifest_ver == 'v1':
            endpoint_url, request_data = self._build_manifest_v1(viewable_id=viewable_id, hdcp_version=hdcp_version,
                                                                 hdcp_override=hdcp_override, profiles=profiles,
                                                                 challenge=challenge)
        else:  # Default - most recent version
            endpoint_url, request_data, xid = self._build_manifest_v2(viewable_id=viewable_id,
                                                                      hdcp_version=hdcp_version,
                                                                      hdcp_override=hdcp_override,
                                                                      profiles=profiles,
                                                                      challenge=challenge, sid=sid)
        manifest = self.msl_requests.chunked_request(endpoint_url, request_data, esn, disable_msl_switch=False)
        if manifest_ver == 'default' and 'license' in manifest['video_tracks'][0]:
            self.needs_license_request = False
            # This xid must be used also for each future Event request, until playback stops
            G.LOCAL_DB.set_value('xid', xid, TABLE_SESSION)
            self.licenses_xid.insert(0, xid)
            self.licenses_session_id.insert(0, manifest['video_tracks'][0]['license']['drmSessionId'])
            self.licenses_release_url.insert(0,
                                             manifest['video_tracks'][0]['license']['links']['releaseLicense']['href'])
            self.licenses_response = manifest['video_tracks'][0]['license']['licenseResponseBase64']
        else:
            self.needs_license_request = True
        if LOG.is_enabled:
            # Save the manifest to disk as reference
            common.save_file_def('manifest.json', json.dumps(manifest).encode('utf-8'))
        # Save the manifest to the cache to retrieve it during its validity
        expiration = int(manifest['expiration'] / 1000)
        G.CACHE.add(CACHE_MANIFESTS, cache_identifier, manifest, expires=expiration)
        return manifest