2025-03-18 00:23:51 +05:30

132 lines
5.0 KiB
Python

import argparse
import requests
import xmltodict
import base64
import uuid
WV_SYSTEM_ID = [237, 239, 139, 169, 121, 214, 74, 206, 163, 200, 39, 220, 213, 29, 33, 237]
def parse_manifest_ism(manifest_url):
r = requests.get(manifest_url, headers={'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
'AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/72.0.3626.121 Safari/537.36'})
if r.status_code != 200:
raise Exception(r.text)
# Write to file
#with open("manifest", "w") as file:
# file.write(r.text)
ism = xmltodict.parse(r.text)
pssh = ism['SmoothStreamingMedia']['Protection']['ProtectionHeader']['#text']
pr_pssh_dec = base64.b64decode(pssh).decode('utf16')
pr_pssh_dec = pr_pssh_dec[pr_pssh_dec.index('<'):]
pr_pssh_xml = xmltodict.parse(pr_pssh_dec)
kid_hex = base64.b64decode(pr_pssh_xml['WRMHEADER']['DATA']['KID']).hex()
kid = uuid.UUID(kid_hex).bytes_le
init_data = bytearray(b'\x12\x10')
init_data.extend(kid)
init_data.extend(b'H\xe3\xdc\x95\x9b\x06')
pssh = bytearray([0, 0, 0])
pssh.append(32 + len(init_data))
pssh[4:] = bytearray(b'pssh')
pssh[8:] = [0, 0, 0, 0]
pssh[13:] = WV_SYSTEM_ID
pssh[29:] = [0, 0, 0, 0]
pssh[31] = len(init_data)
pssh[32:] = init_data
print('\nPSSH (WV): ', base64.b64encode(pssh))
print('\nKID: {}'.format(kid.hex()))
stream_indices = ism['SmoothStreamingMedia']['StreamIndex']
# List to store information for each stream
stream_info_list = []
# Iterate over each StreamIndex (as it might be a list)
for stream_info in stream_indices if isinstance(stream_indices, list) else [stream_indices]:
type_info = stream_info['@Type']
if type_info in {'video', 'audio'}:
# Handle the case where there can be multiple QualityLevel elements
quality_levels = stream_info.get('QualityLevel', [])
if not isinstance(quality_levels, list):
quality_levels = [quality_levels]
for quality_level in quality_levels:
codec = quality_level.get('@FourCC', 'N/A')
bitrate = quality_level.get('@Bitrate', 'N/A')
# Additional attributes for video streams
if type_info == 'video':
max_width = quality_level.get('@MaxWidth', 'N/A')
max_height = quality_level.get('@MaxHeight', 'N/A')
resolution = f"{max_width}x{max_height}"
else:
resolution = 'N/A'
# Additional attributes for audio streams
language = stream_info.get('@Language', 'N/A')
track_id = stream_info.get('@AudioTrackId', 'N/A') if type_info == 'audio' else None
stream_info_list.append({
'type': type_info,
'codec': codec,
'bitrate': bitrate,
'resolution': resolution,
'language': language,
'track_id': track_id
})
# PSSH encoding logic in ism (Below generates WV PSSH)
array_of_bytes = bytearray(b'\x00\x00\x002pssh\x00\x00\x00\x00')
array_of_bytes.extend(bytes.fromhex("edef8ba979d64acea3c827dcd51d21ed"))
array_of_bytes.extend(b'\x00\x00\x00\x12\x12\x10')
array_of_bytes.extend(bytes.fromhex(str(kid.hex()).replace("-", "")))
encoded_string = base64.b64encode(bytes.fromhex(array_of_bytes.hex())).decode("utf-8")
return stream_info_list, encoded_string
def main():
# Create an ArgumentParser object and add the 'urls' argument
parser = argparse.ArgumentParser(description='Script for parsing Smooth Streaming manifest URLs.')
parser.add_argument('urls',
help='The URLs to parse. You may need to wrap the URLs in double quotes if you have issues.',
nargs='+')
# Parse the arguments
args = parser.parse_args()
# Iterate over the provided URLs
for manifest_link in args.urls:
stream_info_list, encoded_string = parse_manifest_ism(manifest_link)
# Print information for each stream
for stream_info in stream_info_list:
type_info = stream_info['type']
codec = stream_info['codec']
bitrate = stream_info['bitrate']
resolution = stream_info['resolution']
if type_info == 'video':
print(f'[INFO] VIDEO - Codec: {codec}, Resolution: {resolution}, Bitrate: {bitrate}')
elif type_info == 'audio':
language = stream_info['language']
track_id = stream_info['track_id']
print(f'[INFO] AUDIO - Codec: {codec}, Bitrate: {bitrate}, Language: {language}, Track ID: {track_id}')
# Print PSSH information
print('\n[INFO] PSSH:', encoded_string)
if __name__ == "__main__":
main()