Hey
I've dumped my own L3 CDM using the sticky and would like to use WKS-KEYS to download an mpd-stream.
I got the mpd by using The Stream Detector:
In there, I see a PSSH that starts with 'AAA' instead of what I usually see ('AAAA').Code:https://video.dpgmedia.net/out/v1/24feb673b0094cc4b52f5198611d4bff/eb76c933215848938246f10a6161dc4b/75aa63df75e944e0aeafbdbcbf096fc1/index.mpd?aws.manifestfilter=subtitle_language%3Azzz%3Btrickplay_type%3Aiframe
[Attachment 78154 - Click to enlarge]
I threw it in Axinom's PSSH Box Decoder and found the following:
The license seems to correspond with what I found, so that's good, but how do I get to a PSSH-key l3.py can use?Code:<WRMHEADER xmlns="http://schemas.microsoft.com/DRM/2007/03/PlayReadyHeader" version="4.0.0.0"> <DATA> <PROTECTINFO> <KEYLEN>16</KEYLEN> <ALGID>AESCTR</ALGID> </PROTECTINFO> <KID>QFtmU21Z/DGcI2q54BVAEQ==</KID> <LA_URL>https://lic.drmtoday.com/license-proxy-headerauth/drmtoday/RightsManager.asmx</LA_URL> <LUI_URL>https://playready-ui.example.com</LUI_URL> <CHECKSUM>MqlqXxjeHqQ=</CHECKSUM> </DATA> </WRMHEADER>
Thanks!
+ Reply to Thread
Results 1 to 21 of 21
-
-
widevine pssh in mpd too, it is short one, and found 4 cached key
pssh:
Code:AAAAenBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAAFoIARIQJPbPlaOSMOGxqDSVInr4fiJEZXlKaGMzTmxkRWxrSWpvaU0yVTNOV1psT0RrdE5qQmxZUzAwTldReExUaGtZVFl0TWpBM01tWmxPRGsyT1dReEluMD0=
Code:24f6cf95a39230e1b1a83495227af87e:3da21a12603fdd5fa28cfa29305595d1 b2705e5a25c0384b97b3301cd3743b9c:119285e7c033ea0f7012e624880a6135 53665b40596d31fc9c236ab9e0154011:51aa899b9b5dc13058bec17b1b3ae734 e48393f7b84036c0bec386437ad2854c:76ac2cc1142f4d88aab8ed0f406aa265
Last edited by shellcmd; 5th Apr 2024 at 05:30.
-
You selected the wrong pssh it's the one 3 lines above :
Code:AAAAenBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAAFoIARIQJPbPlaOSMOGxqDSVInr4fiJEZXlKaGMzTmxkRWxrSWpvaU0yVTNOV1psT0RrdE5qQmxZUzAwTldReExUaGtZVFl0TWpBM01tWmxPRGsyT1dReEluMD0=
Code:from pywidevine.cdm import Cdm from pywidevine.device import Device from pywidevine.pssh import PSSH import requests headers = { 'Accept': '*/*', # 'x-dt-auth-token': 'XXXXX' } pssh = input("PSSH? ") pssh = PSSH(pssh) lic_url = input("License URL? ") device = Device.load(r"device.wvd") cdm = Cdm.from_device(device) session_id = cdm.open() challenge = cdm.get_license_challenge(session_id, pssh) licence = requests.post(lic_url, headers = headers, data=challenge) licence.raise_for_status() cdm.parse_license(session_id, licence.content) for key in cdm.get_keys(session_id): if key.type=='CONTENT': print(f"\n--key {key.kid.hex}:{key.key.hex()}") cdm.close(session_id)
-
If you only have a PR pssh you can convert it using pywidevine:
Code:from pywidevine.pssh import PSSH wv = PSSH("<PR PSSH HERE>") wv.to_widevine()
-
Thanks a lot for the help already!
I'm working with the following code now:
Code:from pywidevine.cdm import Cdm from pywidevine.device import Device from pywidevine.pssh import PSSH import requests headers = { 'Accept': '*/*', 'x-dt-auth-token':'<snip>', 'Connection': 'keep-alive', 'Sec-Fetch-Dest': 'empty', 'Sec-Fetch-Mode': 'cors', 'Sec-Fetch-Site': 'cross-site', 'Content-Type': 'application/x-www-form-urlencoded', } pssh = input("PSSH? ") pssh = PSSH(pssh) lic_url = input("License URL? ") device = Device.load(r"google_aosp_on_ia_emulator_14.0.0_<snip>_l3.wvd") cdm = Cdm.from_device(device) session_id = cdm.open() challenge = cdm.get_license_challenge(session_id, pssh) licence = requests.post(lic_url, headers = headers, data=challenge) licence.raise_for_status() cdm.parse_license(session_id, licence.content) for key in cdm.get_keys(session_id): if key.type=='CONTENT': print(f"\n--key {key.kid.hex}:{key.key.hex()}") cdm.close(session_id)
But I keep getting back a 400 from the license server.
Code:Traceback (most recent call last): File "/Users/<snip>/VideoStuff/streamz.py", line 30, in <module> licence.raise_for_status() File "/usr/local/lib/python3.9/site-packages/requests/models.py", line 1021, in raise_for_status raise HTTPError(http_error_msg, response=self) requests.exceptions.HTTPError: 400 Client Error: for url: https://lic.drmtoday.com/license-proxy-widevine/cenc/?specConform=true
Thanks! -
Are you able to print license.text before 'raise_for_status'?
I'd remove that anyway because it's only meant for debugging -
I can, but it's also giving me a 400.
Code:<!doctype html><html lang="en"><head><title>HTTP Status 400 – Bad Request</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 400 – Bad Request</h1></body></html>
-
Only use the 'X-Dt-Auth-Token' header in your headers dict (although it's probably just the 'Content-Type' header added by curlconverter that breaks it). Also, to correctly parse the license, you'll probably need:
Code:cdm.parse_license(session_id, json.loads(licence.content)['license'])
-
Thanks white_snake.
Removing everything but the x-dt-auth-token changes the 400 to a 403 (Unauthorised).
Code:<Response [403]> <!doctype html><html lang="en"><head><title>HTTP Status 403 – Forbidden</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 403 – Forbidden</h1></body></html> Traceback (most recent call last): File "/Users/<snip>/VideoStuff/streamz.py", line 23, in <module> licence.raise_for_status() File "/usr/local/lib/python3.9/site-packages/requests/models.py", line 1021, in raise_for_status raise HTTPError(http_error_msg, response=self) requests.exceptions.HTTPError: 403 Client Error: for url: https://lic.drmtoday.com/license-proxy-widevine/cenc/?specConform=true
-
-
Indeed, had to be faster
Got them:
Code:--key 75e30eb062c84f81ab92e287ace41e3f:86196121dc0bbc42d6d0b1d6b8b52cdf --key b2705e5a25c0384b97b3301cd3743b9c:119285e7c033ea0f7012e624880a6135 --key 24f6cf95a39230e1b1a83495227af87e:3da21a12603fdd5fa28cfa29305595d1 --key e48393f7b84036c0bec386437ad2854c:76ac2cc1142f4d88aab8ed0f406aa265 --key b00ac2f926293d45b2a99c96707df630:e66234c713daf860204f49f24031ad38 --key 9556639230893d6bb48ea967ce3ad2c9:90874234820a7e3c5584ed0778005fbe --key 53665b40596d31fc9c236ab9e0154011:51aa899b9b5dc13058bec17b1b3ae734
-
hello, to simplify things you can do this:
Code:from pywidevine.cdm import Cdm from pywidevine.device import Device from pywidevine.pssh import PSSH import requests pssh = input("PSSH : ") pssh = PSSH(pssh) Token = input("x-dt-auth-token : ") lic_url = "https://lic.drmtoday.com/license-proxy-widevine/cenc/?specConform=true" headers = {'x-dt-auth-token': '{0}'.format(Token)} device = Device.load(r"google_aosp_on_ia_emulator_14.0.0_<snip>_l3.wvd") cdm = Cdm.from_device(device) session_id = cdm.open() challenge = cdm.get_license_challenge(session_id, pssh) licence = requests.post(lic_url, headers = headers, data=challenge) licence.raise_for_status() cdm.parse_license(session_id, licence.content) for key in cdm.get_keys(session_id): if key.type=='CONTENT': print(f"\n--key {key.kid.hex}:{key.key.hex()}") cdm.close(session_id)
-
Thanks for the code mate It's Perfectly working
Can you please add a download function in this script to download with N_m3u8DL-RE?
As I am not good with python so couldn't do it myself. I tried to take help of chatGPT but it didn't worked
Here's sample command
Code:N_m3u8DL-RE -M format=mkv -sa best -sv best --key key goes here "URL of manifest " --save-name filename
-
Code:
from pywidevine.cdm import Cdm from pywidevine.device import Device from pywidevine.pssh import PSSH import requests import subprocess headers = { 'Accept': '*/*', # 'x-dt-auth-token': 'XXXXX' } def getkey(pssh, lic_url): device = Device.load(r"device.wvd") cdm = Cdm.from_device(device) session_id = cdm.open() challenge = cdm.get_license_challenge(session_id, pssh) licence = requests.post(lic_url, headers = headers, data=challenge) licence.raise_for_status() cdm.parse_license(session_id, licence.content) keys = '' for key in cdm.get_keys(session_id): if key.type=='CONTENT': print(f"\n--key {key.kid.hex}:{key.key.hex()}") keys += f"\n--key {key.kid.hex}:{key.key.hex()}" cdm.close(session_id) return keys def dlN_m3u8DLRE(path, url_mpd, save_name, keys, debug): command = [path, '-M', 'format=mkv', '-sa', 'best', '-sv', 'best', url_mpd, '--save-name', f'{save_name}', keys] if debug: subprocess.run(command, check=True) else: subprocess.run(command, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) pssh = input("PSSH? ") pssh = PSSH(pssh) lic_url = input("License URL? ") path = "N_m3u8DL-RE" # path to N_m3u8DL-RE url_mpd = input("Url of the manifest (.mpd) ") save_name = input("Save name? ") dlN_m3u8DLRE(path, url_mpd, save_name, getkey(pssh, lic_url), debug=False)
You can turn debug=True if you want to get some prompt informations when running the script -
-
[QUOTE=aqzs;2730533]
Code:from pywidevine.cdm import Cdm from pywidevine.device import Device from pywidevine.pssh import PSSH import requests import subprocess headers = { 'Accept': '*/*', # 'x-dt-auth-token': 'XXXXX' } def getkey(pssh, lic_url): device = Device.load(r"device.wvd") cdm = Cdm.from_device(device) session_id = cdm.open() challenge = cdm.get_license_challenge(session_id, pssh) licence = requests.post(lic_url, headers = headers, data=challenge) licence.raise_for_status() cdm.parse_license(session_id, licence.content) keys = '' for key in cdm.get_keys(session_id): if key.type=='CONTENT': print(f"\n--key {key.kid.hex}:{key.key.hex()}") keys += f"\n--key {key.kid.hex}:{key.key.hex()}" cdm.close(session_id) return keys def dlN_m3u8DLRE(path, url_mpd, save_name, keys, debug): command = [path, '-M', 'format=mkv', '-sa', 'best', '-sv', 'best', url_mpd, '--save-name', f'{save_name}', keys] if debug: subprocess.run(command, check=True) else: subprocess.run(command, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) pssh = input("PSSH? ") pssh = PSSH(pssh) lic_url = input("License URL? ") path = "N_m3u8DL-RE" # path to N_m3u8DL-RE url_mpd = input("Url of the manifest (.mpd) ") save_name = input("Save name? ") dlN_m3u8DLRE(path, url_mpd, save_name, getkey(pssh, lic_url), debug=False)
You have to set the path of N_m3u8DL-RE (like C://Download/N_m3u8DL-RE.exe or /home/user/dl/N_m3u8DL-RE)
You can turn debug=True if you want to get some prompt informations when running the script
I really appreciate your help
Thanks -
Indeed - thanks all
.
First episodes are in. Working on subtitle automation now.
I'm trying to make a comprehensive script to do it all now - start from browser URL, get auth tokens for drmtoday, get mpd, calculate keys, download, decrypt and subtitles. -
-
Quite easy to do, I did that for https://www.tf1.fr and every night it's getting new vids for a show. Keep up !
Similar Threads
-
get key in playready pssh
By Zabon in forum Video Streaming DownloadingReplies: 5Last Post: 12th Nov 2024, 19:41 -
playready key?
By iamghost in forum Video Streaming DownloadingReplies: 7Last Post: 5th Jul 2024, 09:15 -
Different PSSH in mpd, EME logger and PSSH Box Generator
By Kevste in forum Video Streaming DownloadingReplies: 4Last Post: 31st May 2023, 22:24 -
Playready
By hencha in forum Video Streaming DownloadingReplies: 11Last Post: 30th Jan 2023, 06:01 -
Playready DRM
By farafero21 in forum Video Streaming DownloadingReplies: 7Last Post: 17th Dec 2021, 15:45