Add files via upload
Added ism pssh support and an ism parser just to get the information for video and audio codecs.
This commit is contained in:
parent
b138f223b4
commit
4b985d7d71
75
cdm/wks.py
75
cdm/wks.py
|
@ -14,7 +14,10 @@ import requests
|
||||||
from base64 import b64encode
|
from base64 import b64encode
|
||||||
from google.protobuf.message import DecodeError
|
from google.protobuf.message import DecodeError
|
||||||
from google.protobuf import text_format
|
from google.protobuf import text_format
|
||||||
|
import xmltodict
|
||||||
|
import base64
|
||||||
|
import uuid
|
||||||
|
import requests
|
||||||
from Cryptodome.Random import get_random_bytes
|
from Cryptodome.Random import get_random_bytes
|
||||||
from Cryptodome.Random import random
|
from Cryptodome.Random import random
|
||||||
from Cryptodome.Cipher import PKCS1_OAEP, AES
|
from Cryptodome.Cipher import PKCS1_OAEP, AES
|
||||||
|
@ -783,3 +786,73 @@ class DataExtractor_DSNP:
|
||||||
matches = [(match[0], re.search(r'base64,(.*)', match[1]).group(1)) for match in re.findall(r'KEYFORMAT="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed",KEYFORMATVERSIONS="[^"]+",CHARACTERISTICS="([^"]+)",URI="([^"]+)"', self.content)]
|
matches = [(match[0], re.search(r'base64,(.*)', match[1]).group(1)) for match in re.findall(r'KEYFORMAT="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed",KEYFORMATVERSIONS="[^"]+",CHARACTERISTICS="([^"]+)",URI="([^"]+)"', self.content)]
|
||||||
return matches
|
return matches
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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.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
|
||||||
|
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).replace("-", "")))
|
||||||
|
|
||||||
|
encoded_string = base64.b64encode(bytes.fromhex(array_of_bytes.hex())).decode("utf-8")
|
||||||
|
|
||||||
|
return kid, stream_info_list, encoded_string
|
|
@ -0,0 +1,36 @@
|
||||||
|
import argparse
|
||||||
|
from cdm.wks import parse_manifest_ism
|
||||||
|
|
||||||
|
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:
|
||||||
|
kid, 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()
|
Loading…
Reference in New Issue