@@ -279,6 +279,7 @@ def _get_playertoken(self, variant, roaming):
279279
280280 @staticmethod
281281 def _generate_playerinfo ():
282+ """Generate playerinfo json for playertoken request"""
282283 import time
283284 from json import dumps
284285 import base64
@@ -287,73 +288,96 @@ def _generate_playerinfo():
287288 import re
288289
289290 playerinfo = None
290- data = None
291-
292- # Get data from player javascript
293- player_url = 'https://player.vrt.be/vrtnu/js/main.js'
294- crypt_data = None
295- while not crypt_data :
296- response = open_url (player_url )
297- if response :
298- data = response .read ().decode ('utf-8' )
299-
300- if data :
301- # Extract JWT key id and secret
302- crypt_rx = re .compile (r'atob\(\"(==[A-Za-z0-9+/]*)\"' )
303- crypt_data = re .findall (crypt_rx , data )
304- if not crypt_data :
305- # Try redirect
306- redirect_rx = re .compile (r"'([a-z]+\.[a-z0-9]{20}\.js)';" )
307- redirect_path = re .search (redirect_rx , data )
308- if redirect_path :
309- player_url = '{}/{}' .format (player_url [:player_url .rfind ('/' )], redirect_path .group (1 ))
310- else :
311- return playerinfo
312-
313- kid_source = crypt_data [0 ]
314- secret_source = crypt_data [- 1 ]
315- kid = base64 .b64decode (kid_source [::- 1 ]).decode ('utf-8' )
316- secret = base64 .b64decode (secret_source [::- 1 ]).decode ('utf-8' )
317-
318- # Extract player version
319- player_version = '3.1.1'
320- pv_rx = re .compile (r'playerVersion:\"(\S*)\"' )
321- match = re .search (pv_rx , data )
322- if match :
323- player_version = match .group (1 )
324-
325- # Generate JWT
326- segments = []
327- header = {
328- 'alg' : 'HS256' ,
329- 'kid' : kid
330- }
331- payload = {
332- 'exp' : time .time () + 1000 ,
333- 'platform' : 'desktop' ,
334- 'app' : {
335- 'type' : 'browser' ,
336- 'name' : 'Firefox' ,
337- 'version' : '114.0'
338- },
339- 'device' : 'undefined (undefined)' ,
340- 'os' : {
341- 'name' : 'Linux' ,
342- 'version' : 'x86_64'
343- },
344- 'player' : {
345- 'name' : 'VRT web player' ,
346- 'version' : player_version
291+ seen_urls = set ()
292+ result_urls = []
293+ base_url = 'https://player.vrt.be/vrtmax/js/player-lib.js'
294+ folder = '/' .join (base_url .split ('/' )[:- 1 ])
295+ player_version = '5.2.2'
296+ atobs = None
297+ kid = None
298+ secret = None
299+
300+ main_script = open_url (base_url ).read ().decode ('utf-8' )
301+ first_level_pattern = r'"\.(/[a-z0-9-/]+[a-z0-9]{8}\.js)";'
302+ first_level_paths = re .findall (first_level_pattern , main_script )
303+
304+ for path in first_level_paths :
305+ script_url = folder + path
306+ if script_url in seen_urls :
307+ continue
308+ seen_urls .add (script_url )
309+
310+ if 'drm' in script_url or 'bootstrapper' in script_url :
311+ result_urls .append (script_url )
312+
313+ script_content = open_url (script_url ).read ().decode ('utf-8' )
314+ second_level_pattern = r'import\(\"\.(/[a-z0-9-]+\.js)\"\)'
315+ second_level_paths = re .findall (second_level_pattern , script_content )
316+
317+ for sub_path in second_level_paths :
318+ sub_script_url = folder + sub_path
319+ if sub_script_url in seen_urls :
320+ continue
321+ seen_urls .add (sub_script_url )
322+
323+ if 'drm' in sub_script_url or 'bootstrapper' in sub_script_url :
324+ result_urls .append (sub_script_url )
325+
326+ pattern_version = re .compile (r'\s"(\d{1}\.\d{1}\.\d{1}-[a-zA-Z0-9\-:]*)"' )
327+ pattern_atob = re .compile (r'atob\(\"(==[A-Za-z0-9+/]*)\"' )
328+
329+ for url in result_urls :
330+ content = open_url (url ).read ().decode ('utf-8' )
331+ version_match = pattern_version .search (content )
332+ if version_match :
333+ player_version = version_match .group (1 )
334+ atobs = pattern_atob .findall (content )
335+
336+ if atobs :
337+ # first atob reversed
338+ kid = base64 .b64decode (atobs [0 ][::- 1 ]).decode ('utf-8' )
339+ # last atob reversed
340+ secret = base64 .b64decode (atobs [- 1 ][::- 1 ]).decode ('utf-8' )
341+
342+ log (2 , kid )
343+ log (2 , secret )
344+
345+ # Generate JWT
346+ segments = []
347+ header = {
348+ 'alg' : 'HS256' ,
349+ 'kid' : kid
347350 }
348- }
349- json_header = dumps (header ).encode ()
350- json_payload = dumps (payload ).encode ()
351- segments .append (base64 .urlsafe_b64encode (json_header ).decode ('utf-8' ).replace ('=' , '' ))
352- segments .append (base64 .urlsafe_b64encode (json_payload ).decode ('utf-8' ).replace ('=' , '' ))
353- signing_input = '.' .join (segments ).encode ()
354- signature = hmac .new (secret .encode (), signing_input , hashlib .sha256 ).digest ()
355- segments .append (base64 .urlsafe_b64encode (signature ).decode ('utf-8' ).replace ('=' , '' ))
356- playerinfo = '.' .join (segments )
351+ payload = {
352+ 'drm' : {
353+ 'widevine' : 'L3'
354+ },
355+ 'exp' : round (time .time () + 3600 , 3 ),
356+ 'platform' : 'desktop' ,
357+ 'app' : {
358+ 'type' : 'browser' ,
359+ 'name' : 'Firefox' ,
360+ 'version' : '137.0' ,
361+ },
362+ 'device' : 'undefined (undefined)' ,
363+ 'os' : {
364+ 'name' : 'Linux' ,
365+ 'version' : 'x86_64' ,
366+ },
367+ 'player' : {
368+ 'name' : 'VRT web player' ,
369+ 'version' : player_version ,
370+ }
371+ }
372+ json_header = dumps (header ).encode ()
373+ json_payload = dumps (payload ).encode ()
374+ segments .append (base64 .urlsafe_b64encode (json_header ).rstrip (b'=' ).decode ('utf-8' ))
375+ segments .append (base64 .urlsafe_b64encode (json_payload ).rstrip (b'=' ).decode ('utf-8' ))
376+ signing_input = '.' .join (segments ).encode ()
377+ signature = hmac .new (secret .encode (), signing_input , hashlib .sha256 ).digest ()
378+ segments .append (base64 .urlsafe_b64encode (signature ).rstrip (b'=' ).decode ('utf-8' ))
379+ playerinfo = '.' .join (segments )
380+ log (2 , playerinfo )
357381 return playerinfo
358382
359383 def delete_tokens (self ):
0 commit comments