From c2d113597bfee4ca1a4c4e4ec4cfca3ed158ad5d Mon Sep 17 00:00:00 2001 From: libdev Date: Sun, 2 Dec 2018 20:36:15 +0100 Subject: [PATCH] v2 license request / Add h264 profileLevel40 streams --- resources/lib/MSLv2.py | 82 ++++++++++++++---------------------------- 1 file changed, 27 insertions(+), 55 deletions(-) diff --git a/resources/lib/MSLv2.py b/resources/lib/MSLv2.py index 20fcbf6ee..cdd2f7f9c 100644 --- a/resources/lib/MSLv2.py +++ b/resources/lib/MSLv2.py @@ -8,7 +8,6 @@ import re import sys import zlib -import gzip import json import time import base64 @@ -48,8 +47,7 @@ class MSL(object): base_url = 'https://www.netflix.com/nq/msl_v1/cadmium/' endpoints = { 'manifest': base_url + 'pbo_manifests/%5E1.0.0/router', - #'license': base_url + 'pbo_licenses/%5E1.0.0/router' - 'license': 'http://www.netflix.com/api/msl/NFCDCH-LX/cadmium/license' + 'license': base_url + 'pbo_licenses/%5E1.0.0/router' } def __init__(self, nx_common): @@ -80,14 +78,19 @@ def load_manifest(self, viewable_id, dolby, hevc, hdr, dolbyvision, vp9): :param viewable_id: The id of of the viewable :return: MPD XML Manifest or False if no success """ + + ia_addon = xbmcaddon.Addon('inputstream.adaptive') + hdcp = ia_addon is not None and ia_addon.getSetting('HDCPOVERRIDE') == 'true' + esn = self.nx_common.get_esn() + id = int(time.time() * 10000) manifest_request_data = { 'version': 2, 'url': '/manifest', - 'id': 15423166626396, + 'id': id, 'esn': esn, 'languages': self.locale_id, - 'uiVersion': 'shakti-vb45817f4', + 'uiVersion': 'shakti-v25d2fa21', 'clientVersion': '6.0011.474.011', 'params': { 'type': 'standard', @@ -97,10 +100,10 @@ def load_manifest(self, viewable_id, dolby, hevc, hdr, dolbyvision, vp9): 'drmVersion': 25, 'usePsshBox': True, 'isBranching': False, - 'useHttpsStreams': True, + 'useHttpsStreams': False, 'imageSubtitleHeight': 1080, 'uiVersion': 'shakti-vb45817f4', - 'clientVersion': '6.0011.474.011', + 'clientVersion': '6.0011.511.011', 'supportsPreReleasePin': True, 'supportsWatermark': True, 'showAllSubDubTracks': False, @@ -109,7 +112,7 @@ def load_manifest(self, viewable_id, dolby, hevc, hdr, dolbyvision, vp9): 'type': 'DigitalVideoOutputDescriptor', 'outputType': 'unknown', 'supportedHdcpVersions': [], - 'isHdcpEngaged': False + 'isHdcpEngaged': hdcp }], 'preferAssistiveAudio': False, 'isNonMember': False @@ -117,11 +120,10 @@ def load_manifest(self, viewable_id, dolby, hevc, hdr, dolbyvision, vp9): } manifest_request_data['params']['titleSpecificData'][viewable_id] = { 'unletterboxed': False } - profiles = ['playready-h264mpl30-dash', 'playready-h264mpl31-dash', 'playready-h264hpl30-dash', 'playready-h264hpl31-dash', 'heaac-2-dash', 'BIF240', 'BIF320'] + profiles = ['playready-h264mpl30-dash', 'playready-h264mpl31-dash', 'playready-h264mpl40-dash', 'playready-h264hpl30-dash', 'playready-h264hpl31-dash', 'playready-h264hpl40-dash', 'heaac-2-dash', 'BIF240', 'BIF320'] # subtitles - addon = xbmcaddon.Addon('inputstream.adaptive') - if addon and self.nx_common.compare_versions(map(int, addon.getAddonInfo('version').split('.')), [2, 3, 8]) >= 0: + if ia_addon and self.nx_common.compare_versions(map(int, ia_addon.getAddonInfo('version').split('.')), [2, 3, 8]) >= 0: profiles.append('webvtt-lssdh-ios8') else: profiles.append('simplesdh') @@ -212,7 +214,7 @@ def load_manifest(self, viewable_id, dolby, hevc, hdr, dolbyvision, vp9): profiles.append('ddplus-5.1-dash') manifest_request_data["params"]["profiles"] = profiles - print manifest_request_data + #print manifest_request_data request_data = self.__generate_msl_request_data(manifest_request_data) @@ -252,7 +254,7 @@ def get_license(self, challenge, sid): """ esn = self.nx_common.get_esn() id = int(time.time() * 10000) - '''license_request_data = { + license_request_data = { 'version': 2, 'url': self.last_license_url, 'id': id, @@ -267,24 +269,7 @@ def get_license(self, challenge, sid): 'xid': str(id + 1610) }], 'echo': 'sessionId' - }''' - - license_request_data = { - 'method': 'license', - 'licenseType': 'STANDARD', - 'clientVersion': '4.0004.899.011', - 'uiVersion': 'akira', - 'languages': self.locale_id, - 'playbackContextId': self.last_playback_context, - 'drmContextIds': [self.last_drm_context], - 'challenges': [{ - 'dataBase64': challenge, - 'sessionId': sid - }], - 'clientTime': int(id / 10000), - 'xid': id + 1610 } - #print license_request_data request_data = self.__generate_msl_request_data(license_request_data) @@ -309,8 +294,8 @@ def get_license(self, challenge, sid): # json() failed so we have a chunked json response resp = self.__parse_chunked_msl_response(resp.text) data = self.__decrypt_payload_chunks(resp['payloads']) - if data['success'] is True: - return data['result']['licenses'][0]['data'] + if 'licenseResponseBase64' in data[0]: + return data[0]['licenseResponseBase64'] else: self.nx_common.log( msg='Error getting license: ' + json.dumps(data)) @@ -339,7 +324,12 @@ def __decrypt_payload_chunks(self, payloadchunks): data = base64.standard_b64decode(data) decrypted_payload += data - decrypted_payload = json.JSONDecoder().decode(decrypted_payload)[1]['payload'] + decrypted_payload = json.JSONDecoder().decode(decrypted_payload) + + if 'result' in decrypted_payload: + return decrypted_payload['result'] + + decrypted_payload = decrypted_payload[1]['payload'] if 'json' in decrypted_payload: return decrypted_payload['json']['result'] else: @@ -354,7 +344,7 @@ def __tranform_to_dash(self, manifest): filename='manifest.json', content=json.dumps(manifest)) - self.last_license_url = manifest['links']['ldl']['href'] + self.last_license_url = manifest['links']['license']['href'] self.last_playback_context = manifest['playbackContextId'] self.last_drm_context = manifest['drmContextId'] @@ -562,21 +552,10 @@ def __generate_msl_request_data(self, data): 'mastertoken': self.mastertoken, } - # Serialize the given Data - raw_marshalled_data = json.dumps(data) - marshalled_data = raw_marshalled_data.replace('"', '\\"') - serialized_data = '[{},{"headers":{},"path":"/cbp/cadmium-13"' - serialized_data += ',"payload":{"data":"' - serialized_data += marshalled_data - serialized_data += '"},"query":""}]\n' - - compressed_data = self.__compress_data(serialized_data) - # Create FIRST Payload Chunks first_payload = { 'messageid': self.current_message_id, - 'data': compressed_data, - 'compressionalgo': 'GZIP', + 'data': base64.standard_b64encode(json.dumps(data)), 'sequencenumber': 1, 'endofmsg': True } @@ -590,13 +569,6 @@ def __generate_msl_request_data(self, data): request_data = json.dumps(header) + json.dumps(first_payload_chunk) return request_data - def __compress_data(self, data): - # GZIP THE DATA - out = StringIO() - with gzip.GzipFile(fileobj=out, mode="w") as f: - f.write(data) - return base64.standard_b64encode(out.getvalue()) - def __generate_msl_header( self, is_handshake=False, @@ -618,13 +590,13 @@ def __generate_msl_header( 'handshake': is_handshake, 'nonreplayable': False, 'capabilities': { - 'languages': ['en-US'], + 'languages': self.locale_id, 'compressionalgos': compression_algos }, 'recipient': 'Netflix', 'renewable': True, 'messageid': self.current_message_id, - 'timestamp': 1467733923 + 'timestamp': time.time() } # If this is a keyrequest act diffrent then other requests