예제 #1
0
def run_addon_configuration(show_end_msg=False):
    """
    Add-on configuration wizard,
    automatically configures profiles and add-ons dependencies, based on user-supplied data and device characteristics
    """
    system = get_system_platform()
    debug('Running add-on configuration wizard ({})', system)
    G.settings_monitor_suspend(True, False)
    is_4k_capable = is_device_4k_capable()

    _set_profiles(system, is_4k_capable)
    _set_kodi_settings(system)
    _set_isa_addon_settings(is_4k_capable, system == 'android')

    # This settings for now used only with android devices and it should remain disabled (keep it for test),
    # in the future it may be useful for other platforms or it may be removed
    G.ADDON.setSettingBool('enable_force_hdcp', False)

    # Enable UpNext if it is installed and enabled
    G.ADDON.setSettingBool(
        'UpNextNotifier_enabled',
        getCondVisibility('System.AddonIsEnabled(service.upnext)'))

    G.settings_monitor_suspend(False)
    if show_end_msg:
        show_ok_dialog(get_local_string(30154), get_local_string(30157))
def _perform_service_changes(previous_ver, current_ver):
    """Perform actions for an version bump"""
    LOG.debug(
        'Initialize add-on service upgrade operations, from version {} to {}',
        previous_ver, current_ver)
    # Clear cache (prevents problems when netflix change data structures)
    G.CACHE.clear()
    if not previous_ver:
        return
    # Delete all stream continuity data - if user has upgraded from Kodi 18 to Kodi 19
    if CmpVersion(previous_ver) < '1.13':
        # There is no way to determine if the user has migrated from Kodi 18 to Kodi 19,
        #   then we assume that add-on versions prior to 1.13 was on Kodi 18
        # The am_stream_continuity.py on Kodi 18 works differently and the existing data can not be used on Kodi 19
        G.SHARED_DB.clear_stream_continuity()
        # Disable enable_hevc_profiles if has been wrongly enabled by the user and it is unsupported by the systems
        with G.SETTINGS_MONITOR.ignore_events(1):
            is_4k_capable = is_device_4k_capable()
            G.ADDON.setSettingBool('enable_hevc_profiles', is_4k_capable)
    if CmpVersion(previous_ver) < '1.9.0':
        # In the version 1.9.0 has been changed the COOKIE_ filename with a static filename
        from resources.lib.upgrade_actions import rename_cookie_file
        rename_cookie_file()
    if CmpVersion(previous_ver) < '1.12.0':
        # In the version 1.13.0:
        # - 'force_widevine' on setting.xml has been moved
        #   as 'widevine_force_seclev' in TABLE_SESSION with different values:
        # force_widevine = G.ADDON.getSettingString('force_widevine')
        # # Old values: Disabled|Widevine L3|Widevine L3 (ID-4445)
        # # New values: Disabled|L3|L3 (ID 4445)
        # if force_widevine == 'Widevine L3':
        #     G.LOCAL_DB.set_value('widevine_force_seclev', 'L3', table=TABLE_SESSION)
        # elif force_widevine == 'Widevine L3 (ID-4445)':
        #     G.LOCAL_DB.set_value('widevine_force_seclev', 'L3 (ID 4445)', table=TABLE_SESSION)
        # # - 'esn' on setting.xml is not more used but if was set the value need to be copied on 'esn' on TABLE_SESSION:
        # esn = G.ADDON.getSettingString('esn')
        # if esn:
        #     from resources.lib.utils.esn import set_esn
        #     set_esn(esn)
        # - 'suspend_settings_monitor' is not more used
        G.LOCAL_DB.delete_key('suspend_settings_monitor')
        # In the version 1.14.0 the new settings.xml format has been introduced
        # the migration of the settings (commented above) from this version is no more possible
        from resources.lib.kodi import ui
        ui.show_ok_dialog(
            'Netflix add-on upgrade',
            'This add-on upgrade has reset your ESN code, if you had set an ESN code manually '
            'you must re-enter it again in the Expert settings, otherwise simply ignore this message.'
        )
    if CmpVersion(previous_ver) < '1.16.0':
        # In the version 1.16.0 the watched status sync setting has been enabled by default,
        # therefore to be able to keep the user's setting even when it has never been changed,
        # we have done a new setting (ProgressManager_enabled >> to >> sync_watched_status)
        with G.SETTINGS_MONITOR.ignore_events(1):
            G.ADDON.setSettingBool(
                'sync_watched_status',
                G.ADDON.getSettingBool('ProgressManager_enabled'))
    if CmpVersion(previous_ver) < '1.18.3':
        # In the version 1.18.3 content_profiles_int has been replaced by manifest_settings_status
        G.LOCAL_DB.delete_key('content_profiles_int')
예제 #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
예제 #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
예제 #5
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
예제 #6
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
예제 #7
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