X-Git-Url: http://git.code-monkey.de/?a=blobdiff_plain;f=resources%2Flib%2FMSL.py;h=7210c71f31a5817729188e28708db4dec7498aee;hb=0c5653eedae999646dfa3e125cd7126021d927f9;hp=83ae2e7abfe2b34cfff46f67f3fc4bc0ff4ff196;hpb=26c305a4ecab0366fa05d1d4c3bc9ee24d170029;p=plugin.video.netflix.git diff --git a/resources/lib/MSL.py b/resources/lib/MSL.py index 83ae2e7..7210c71 100644 --- a/resources/lib/MSL.py +++ b/resources/lib/MSL.py @@ -18,8 +18,6 @@ from Crypto.Random import get_random_bytes # from Crypto.Hash import HMAC, SHA256 from Crypto.Util import Padding import xml.etree.ElementTree as ET -from common import log -from common import ADDONUSERDATA pp = pprint.PrettyPrinter(indent=4) @@ -49,27 +47,28 @@ class MSL: 'license': 'http://www.netflix.com/api/msl/NFCDCH-LX/cadmium/license' } - def __init__(self, email, password): + def __init__(self, email, password, kodi_helper): """ The Constructor checks for already existing crypto Keys. If they exist it will load the existing keys """ self.email = email self.password = password + self.kodi_helper = kodi_helper try: - os.mkdir(ADDONUSERDATA) + os.mkdir(self.kodi_helper.msl_data_path) except OSError: pass - if self.file_exists('msl_data.json'): + if self.file_exists(self.kodi_helper.msl_data_path, 'msl_data.json'): self.__load_msl_data() self.handshake_performed = True - elif self.file_exists('rsa_key.bin'): - log('RSA Keys do already exist load old ones') + elif self.file_exists(self.kodi_helper.msl_data_path, 'rsa_key.bin'): + self.kodi_helper.log(msg='RSA Keys do already exist load old ones') self.__load_rsa_keys() self.__perform_key_handshake() else: - log('Create new RSA Keys') + self.kodi_helper.log(msg='Create new RSA Keys') # Create new Key Pair and save self.rsa_key = RSA.generate(2048) self.__save_rsa_keys() @@ -113,7 +112,7 @@ class MSL: try: resp.json() - log('MANIFEST RESPONE JSON: '+resp.text) + self.kodi_helper.log(msg='MANIFEST RESPONE JSON: '+resp.text) except ValueError: # Maybe we have a CHUNKED response resp = self.__parse_chunked_msl_response(resp.text) @@ -161,7 +160,7 @@ class MSL: try: resp.json() - log('LICENSE RESPONE JSON: '+resp.text) + self.kodi_helper.log(msg='LICENSE RESPONE JSON: '+resp.text) except ValueError: # Maybe we have a CHUNKED response resp = self.__parse_chunked_msl_response(resp.text) @@ -196,7 +195,7 @@ class MSL: def __tranform_to_dash(self, manifest): - self.save_file('manifest.json', json.dumps(manifest)) + self.save_file(self.kodi_helper.msl_data_path, 'manifest.json', json.dumps(manifest)) manifest = manifest['result']['viewables'][0] self.last_playback_context = manifest['playbackContextId'] @@ -208,15 +207,13 @@ class MSL: if len(manifest['psshb64']) >= 1: pssh = manifest['psshb64'][0] - + seconds = manifest['runtime']/1000 + duration = "PT"+str(seconds)+".00S" root = ET.Element('MPD') root.attrib['xmlns'] = 'urn:mpeg:dash:schema:mpd:2011' root.attrib['xmlns:cenc'] = 'urn:mpeg:cenc:2013' - - - seconds = manifest['runtime']/1000 - duration = "PT"+str(seconds)+".00S" + root.attrib['mediaPresentationDuration'] = duration period = ET.SubElement(root, 'Period', start='PT0S', duration=duration) @@ -233,7 +230,7 @@ class MSL: rep = ET.SubElement(video_adaption_set, 'Representation', width=str(downloadable['width']), height=str(downloadable['height']), - bandwidth=str(downloadable['bitrate']*8*1024), + bandwidth=str(downloadable['bitrate']*1024), codecs='h264', mimeType='video/mp4') @@ -254,7 +251,7 @@ class MSL: for downloadable in audio_track['downloadables']: rep = ET.SubElement(audio_adaption_set, 'Representation', codecs='aac', - bandwidth=str(downloadable['bitrate'] * 8 * 1024), + bandwidth=str(downloadable['bitrate']*1024), mimeType='audio/mp4') #AudioChannel Config @@ -456,21 +453,21 @@ class MSL: 'headerdata': base64.standard_b64encode(header), 'signature': '', } - log('Key Handshake Request:') - log(json.dumps(request)) + self.kodi_helper.log(msg='Key Handshake Request:') + self.kodi_helper.log(msg=json.dumps(request)) resp = self.session.post(self.endpoints['manifest'], json.dumps(request, sort_keys=True)) if resp.status_code == 200: resp = resp.json() if 'errordata' in resp: - log('Key Exchange failed') - log(base64.standard_b64decode(resp['errordata'])) + self.kodi_helper.log(msg='Key Exchange failed') + self.kodi_helper.log(msg=base64.standard_b64decode(resp['errordata'])) return False self.__parse_crypto_keys(json.JSONDecoder().decode(base64.standard_b64decode(resp['headerdata']))) else: - log('Key Exchange failed') - log(resp.text) + self.kodi_helper.log(msg='Key Exchange failed') + self.kodi_helper.log(msg=resp.text) def __parse_crypto_keys(self, headerdata): self.__set_master_token(headerdata['keyresponsedata']['mastertoken']) @@ -491,7 +488,7 @@ class MSL: self.handshake_performed = True def __load_msl_data(self): - msl_data = json.JSONDecoder().decode(self.load_file('msl_data.json')) + msl_data = json.JSONDecoder().decode(self.load_file(self.kodi_helper.msl_data_path, 'msl_data.json')) self.__set_master_token(msl_data['tokens']['mastertoken']) self.encryption_key = base64.standard_b64decode(msl_data['encryption_key']) self.sign_key = base64.standard_b64decode(msl_data['sign_key']) @@ -509,7 +506,7 @@ class MSL: } } serialized_data = json.JSONEncoder().encode(data) - self.save_file('msl_data.json', serialized_data) + self.save_file(self.kodi_helper.msl_data_path, 'msl_data.json', serialized_data) def __set_master_token(self, master_token): self.mastertoken = master_token @@ -517,42 +514,42 @@ class MSL: 'sequencenumber'] def __load_rsa_keys(self): - loaded_key = self.load_file('rsa_key.bin') + loaded_key = self.load_file(self.kodi_helper.msl_data_path, 'rsa_key.bin') self.rsa_key = RSA.importKey(loaded_key) def __save_rsa_keys(self): - log('Save RSA Keys') + self.kodi_helper.log(msg='Save RSA Keys') # Get the DER Base64 of the keys encrypted_key = self.rsa_key.exportKey() - self.save_file('rsa_key.bin', encrypted_key) + self.save_file(self.kodi_helper.msl_data_path, 'rsa_key.bin', encrypted_key) @staticmethod - def file_exists(filename): + def file_exists(msl_data_path, filename): """ Checks if a given file exists :param filename: The filename :return: True if so """ - return os.path.isfile(ADDONUSERDATA + filename) + return os.path.isfile(msl_data_path + filename) @staticmethod - def save_file(filename, content): + def save_file(msl_data_path, filename, content): """ Saves the given content under given filename :param filename: The filename :param content: The content of the file """ - with open(ADDONUSERDATA + filename, 'w') as file_: + with open(msl_data_path + filename, 'w') as file_: file_.write(content) file_.flush() @staticmethod - def load_file(filename): + def load_file(msl_data_path, filename): """ Loads the content of a given filename :param filename: The file to load :return: The content of the file """ - with open(ADDONUSERDATA + filename) as file_: + with open(msl_data_path + filename) as file_: file_content = file_.read() return file_content