def get(self, bucket, identifier): """Get a item from cache bucket""" identifier = self._add_prefix(identifier) try: cache_entry = self._get_cache_bucket(bucket['name'])[identifier] if cache_entry['expires'] < int(time()): # Cache expired raise CacheMiss() return cache_entry['data'] except KeyError: if bucket['is_persistent']: return self._get_db(bucket['name'], identifier) raise CacheMiss()
def get(self, bucket, identifier): """Get a item from cache bucket""" try: identifier = self._add_prefix(identifier) cache_entry = self._get_cache_bucket(bucket['name'])[identifier] if cache_entry['expires'] < int(time()): # Cache expired raise CacheMiss() return cache_entry['data'] except KeyError: if bucket['is_persistent']: return self._get_db(bucket['name'], identifier) raise CacheMiss() except ProfilesMissing: # Raised by _add_prefix there is no active profile guid when add-on is installed from scratch raise CacheMiss()
def _get_db(self, bucket_name, identifier): try: cursor = self.conn.cursor() query = ('SELECT value FROM cache_data ' 'WHERE ' 'expires > ? AND ' 'bucket = ? AND identifier = ?') cursor.execute(query, (time(), bucket_name, identifier)) result = cursor.fetchone() if result is None: raise CacheMiss() return result[0] except sql.Error as exc: common.error('SQLite error {}:', exc.args[0]) raise SQLiteError
def deserialize_data(value): try: if G.PY_IS_VER2: # On python 2 pickle.loads wants str from base64 import standard_b64decode return pickle.loads(standard_b64decode(value)) # On python 3 pickle.loads wants byte return pickle.loads(value) except (pickle.UnpicklingError, TypeError, EOFError): # TypeError/EOFError happen when standard_b64decode fails # This should happen only if manually mixing the database data common.error( 'It was not possible to deserialize the cache data, try purge cache from expert settings menu' ) raise CacheMiss()
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 _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 common.is_debug_verbose(): common.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'] 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': 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 common.is_debug_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