VideoHelp Forum


Try StreamFab Downloader and download from Netflix, Amazon, Youtube! Or Try DVDFab and copy Blu-rays!


Try StreamFab Downloader and download streaming video from Youtube, Netflix, Amazon! Download free trial.


+ Reply to Thread
Results 1 to 15 of 15
Thread
  1. hi there,
    i have a lot of script ready for wks-keys tool but now not compatible with new pywidevine. so the only way is make a venv environment

    but venv apart, we have a solution to simply adapt old scripts to the new pywidevine ?
    Last edited by whs912km; 27th Aug 2024 at 07:07.
    Quote Quote  
  2. it's easy to convert wks-keys scripts to pywidevine scripts
    Quote Quote  
  3. nice. how to ?
    Quote Quote  
  4. Angela has posted a thread on this subject: https://forum.videohelp.com/threads/411862-Beyond-WKS-KEYS

    But if you don't understand how your scripts work now with the old wks-tools don't even try to port them to pywidevine.

    It's easy, yes - but needs a basic understanding of writing python scripts. There no such thing as a convert-my-script-from-wks-to-pywidevine tool (albeit: maybe an AI can do it for you?)
    Quote Quote  
  5. yes, i know venv and at the moment it's my easiest solution

    about AI help: I don't think AI help me on similar conversion, even if in fact it is a machine and unlike a human who would make a distinction ...

    maybe it could help solve the problem. I will try. thanks
    Quote Quote  
  6. Easiest way to rename pywidevine ==> pywidevine_old
    from pywidevine_old *import *
    discord chuliaohaibaipiao
    Quote Quote  
  7. Novia, thanks for your reply, but not sure to understood well, it seems too simple ...

    one my script start with:
    Code:
    import base64, requests, json, xmltodict
    from base64 import b64encode
    from pywidevine.decrypt.wvdecrypt import WvDecrypt
    from pywidevine.cdm import cdm, deviceconfig
    so i need simply to edit like this?
    Code:
    import base64, requests, json, xmltodict
    from base64 import b64encode
    from pywidevine_old.decrypt.wvdecrypt import WvDecrypt
    from pywidevine_old.cdm import cdm, deviceconfig
    Quote Quote  
  8. really don't think a simple edit pywidevine ==> pywidevine_old is enough to make a script work on the new pywidevine, so i can share here one my script (obviously edited) as a test to convert into new pywidevine standard, just to see what are the differences
    Code:
    import base64, requests, json, xmltodict
    from base64 import b64encode
    from pywidevine.decrypt.wvdecrypt import WvDecrypt
    from pywidevine.cdm import cdm, deviceconfig
    
    title_url = input('\nURL: ')
    lic_url = 'https://lic/'
    token = input('Token: ')
    
    clip_id = title_url.split('-c_')[1]
    
    def get_pssh(mpd_url):
        r = requests.get(url=mpd_url)
        r.raise_for_status()
        xml = xmltodict.parse(r.text)
        mpd = json.loads(json.dumps(xml))
        tracks = mpd['MPD']['Period']['AdaptationSet']
        for video_tracks in tracks:
            if video_tracks['@contentType'] == 'video':
                for rep in video_tracks['Representation']:
                    for t in rep['ContentProtection']:
                        if t['@schemeIdUri'].lower() == 'urn:uuid:edef8ba9-79d6-4ahg-a3c8-99dcd51d21ed':
                            pssh = t['cenc:pssh']
                            return pssh
    
    def get_mpd(token, clip_id):
        res = requests.get(
            url=f"https://layout/video/clip_{clip_id}/layout",
            headers={
                "authorization": token,
                "referer": "https://uu.com/",
                "origin": "https://uu.com"
            }
        ).json()
        for block in res["blocks"]:
            if block["featureId"] == "videos_for_player":
                for item in block["content"]["items"]:
                    if item["itemType"] == "video":
                        for asset in item["itemContent"]["video"]["assets"]:
                            if asset["quality"] == "sd" and asset["format"] == "dashcenc":
                                sd_mpd_url = asset["path"]
                                uid = asset["drm"]["config"]["uid"]
                                content_id = asset["drm"]["config"]["contentId"]
                            elif asset["quality"] == "hd" and asset["format"] == "dashcenc":
                                hd_mpd_url = asset["path"]
    
        if not sd_mpd_url or not uid or not content_id:
            print(' - Error: mpd not found')
            exit()
        print(f"\nMPD URL: {hd_mpd_url}")
    
        pssh = get_pssh(sd_mpd_url)
    
        upfront = requests.get(
            url=f"https://dcc.asa.com/v1/customers/rtlnl/platforms/m6group_web/services/users/{uid}/videos/{content_id}/upfront-token",
            headers={
                "authorization": token,
                "referer": "https://ee.com/",
                "origin": "https://ee.com"
            }
        ).json()
            
        if upfront.get("token") is not None:
            upfront_token =  upfront["token"]
        else:
            print(f' - Error {upfront["message"]}')
            exit()
        return upfront_token, pssh
    
    
    def WV_Function(pssh, lic_url, cert_b64=None):
        headers = {
            "x-dt-auth-token": upfront_token,
        }
        wvdecrypt = WvDecrypt(init_data_b64=pssh, cert_data_b64=cert_b64, device=deviceconfig.device_android_generic)                   
        widevine_license = requests.post(url=lic_url, data=wvdecrypt.get_challenge(), headers=headers)
        if not widevine_license.ok:
            print("license request failed: \n", widevine_license.content)
            exit()
        license_b64 = json.loads(widevine_license.text)["license"]
        wvdecrypt.update_license(license_b64)
        Correct, keyswvdecrypt = wvdecrypt.start_process()
        if Correct:
            return Correct, keyswvdecrypt   
    
    upfront_token, pssh = get_mpd(token, clip_id)
    correct, keys = WV_Function(pssh, lic_url)
    
    print()
    for key in keys:
        print('--key ' + key)
    Quote Quote  
  9. Member aqzs's Avatar
    Join Date
    Mar 2024
    Location
    Paris
    Search Comp PM
    This version should work :
    HTML Code:
    import base64, requests, json, xmltodict
    from pywidevine.cdm import Cdm
    from pywidevine.device import Device
    from pywidevine.pssh import PSSH
    
    DEVICEPATH = r"device.wvd"
    
    title_url = input('\nURL: ')
    lic_url = 'https://lic/'
    token = input('Token: ')
    
    clip_id = title_url.split('-c_')[1]
    
    def get_pssh(mpd_url):
        r = requests.get(url=mpd_url)
        r.raise_for_status()
        xml = xmltodict.parse(r.text)
        mpd = json.loads(json.dumps(xml))
        tracks = mpd['MPD']['Period']['AdaptationSet']
        for video_tracks in tracks:
            if video_tracks['@contentType'] == 'video':
                for rep in video_tracks['Representation']:
                    for t in rep['ContentProtection']:
                        if t['@schemeIdUri'].lower() == 'urn:uuid:edef8ba9-79d6-4ahg-a3c8-99dcd51d21ed':
                            pssh = t['cenc:pssh']
                            return pssh
    
    def get_mpd(token, clip_id):
        res = requests.get(
            url=f"https://layout/video/clip_{clip_id}/layout",
            headers={
                "authorization": token,
                "referer": "https://uu.com/",
                "origin": "https://uu.com"
            }
        ).json()
        for block in res["blocks"]:
            if block["featureId"] == "videos_for_player":
                for item in block["content"]["items"]:
                    if item["itemType"] == "video":
                        for asset in item["itemContent"]["video"]["assets"]:
                            if asset["quality"] == "sd" and asset["format"] == "dashcenc":
                                sd_mpd_url = asset["path"]
                                uid = asset["drm"]["config"]["uid"]
                                content_id = asset["drm"]["config"]["contentId"]
                            elif asset["quality"] == "hd" and asset["format"] == "dashcenc":
                                hd_mpd_url = asset["path"]
    
        if not sd_mpd_url or not uid or not content_id:
            print(' - Error: mpd not found')
            exit()
        print(f"\nMPD URL: {hd_mpd_url}")
    
        pssh = get_pssh(sd_mpd_url)
    
        upfront = requests.get(
            url=f"https://dcc.asa.com/v1/customers/rtlnl/platforms/m6group_web/services/users/{uid}/videos/{content_id}/upfront-token",
            headers={
                "authorization": token,
                "referer": "https://ee.com/",
                "origin": "https://ee.com"
            }
        ).json()
            
        if upfront.get("token") is not None:
            upfront_token =  upfront["token"]
        else:
            print(f' - Error {upfront["message"]}')
            exit()
        return upfront_token, pssh
    
    
    def get_keys(pssh, lic_url):
        headers = {"x-dt-auth-token": upfront_token,}
        pssh = PSSH(pssh)
        device = Device.load(DEVICEPATH)
        cdm = Cdm.from_device(device)
        session_id = cdm.open()
        challenge = cdm.get_license_challenge(session_id, pssh)
        licence = requests.post(lic_url, data=challenge, headers=headers)
        licence.raise_for_status()
        cdm.parse_license(session_id, licence.json()['license'])
        keys = [f"{key.kid.hex}:{key.key.hex()}" for key in cdm.get_keys(session_id) if key.type != 'SIGNING']
        cdm.close(session_id)
        return keys
    
    upfront_token, pssh = get_mpd(token, clip_id)
    correct, keys = get_keys(pssh, lic_url)
    
    print()
    for key in keys:
        print('--key ' + key)
    Just look at get_keys() function.
    Quote Quote  
  10. thank you @aqzs for your editing. script work fine

    also i've tried with duckAI but the new script does not work
    Quote Quote  
  11. hi guys,

    here another script that i need to convert: https://files.videohelp.com/u/301058/92766.py

    already read Angela thread Beyond WKS-KEYS, but no luck with this script
    also tried with AI help, but without share the full script the help isn't full and clear. and i don't want share my script with AI ...
    hope someone here can help me. thanks guys
    Quote Quote  
  12. little note for those who downloaded my script (6 downloads at the moment): this script is intended as technical purpose only.

    The script in question does not work, it was edited on purpose, mine is just a technical question of how to switch from wks to new pywidevne
    Quote Quote  
  13. Member
    Join Date
    Feb 2022
    Location
    Search the forum first!
    Search PM
    Originally Posted by whs912km View Post
    mine is just a technical question of how to switch from wks to new pywidevne
    Mine is just a technical answer. You were given an exemplar in Beyond WKS-KEYS

    Is is up to you to extrapolate from the example to whatever script you are trying to use. Or are you just free-loading and anticipating someone will do the work you cannot? (Again)

    Take it slowly and consider the calls your WKS script makes and see if there is a replacement line in the example code for L3.py in Beyond WKS-KEYS.
    Noob Starter Pack. Just download every Widevine mpd! Not kidding!.
    https://files.videohelp.com/u/301890/hellyes6.zip
    Quote Quote  
  14. Feels Good Man 2nHxWW6GkN1l916N3ayz8HQoi's Avatar
    Join Date
    Jan 2024
    Location
    Pepe Island
    Search Comp PM
    Originally Posted by whs912km View Post
    here another script that i need to convert: https://files.videohelp.com/u/301058/92766.py
    This what you want?

    Code:
    import json
    import re
    
    from base64 import b64encode
    import requests
    import xmltodict
    
    # from pywidevine.cdm import cdm, deviceconfig
    # from pywidevine.decrypt.wvdecrypt import WvDecrypt
    from pywidevine import Cdm, Device
    
    
    def get_pssh(mpd_url):
        pssh = ''
        try:
            r = requests.get(url=mpd_url)
            r.raise_for_status()
            xml = xmltodict.parse(r.text)
            mpd = json.loads(json.dumps(xml))
            periods = mpd['MPD']['Period']
        except Exception as e:
            pssh = input(
                f"\nUnable to find PSSH or 'skip': ")
            return pssh
    
        try:
            regex_dscp = re.search('2013">(.{132})<cenc:pssh', mpd)
            pssh = regex_dscp.group(1)
        except:
            try:
                if isinstance(periods, list):
                    for idx, period in enumerate(periods):
                        if isinstance(period['AdaptationSet'], list):
                            for ad_set in period['AdaptationSet']:
                                if ad_set['@mimeType'] == 'video/mp4':
                                    try:
                                        for t in ad_set['ContentProtection']:
                                            if t['@schemeIdUri'].lower() == "urn:uuid:edeeeeeeeee4ace-a3c8-129acd51d21ed":
                                                pssh = t["cenc:pssh"]
                                    except Exception:
                                        pass
                        else:
                            if period['AdaptationSet']['@mimeType'] == 'video/mp4':
                                try:
                                    for t in period['AdaptationSet']['ContentProtection']:
                                        if t['@schemeIdUri'].lower() == "urn:uuid:edefhhhhhhhe-a3c8-129acd51d21ed":
                                            pssh = t["cenc:pssh"]
                                except Exception:
                                    pass
                else:
                    for ad_set in periods['AdaptationSet']:
                        if ad_set['@mimeType'] == 'video/mp4':
                            try:
                                for t in ad_set['ContentProtection']:
                                    if t['@schemeIdUri'].lower() == "urn:uuid:edef8ggggg-4ace-a3c8-129acd51d21ed":
                                        pssh = t["cenc:pssh"]
                            except Exception:
                                pass
            except Exception:
                try:  # for IW
                    for ad_set in periods['AdaptationSet']:
                        if ad_set['@contentType'] == 'video':
                            try:
                                for t in ad_set['ContentProtection']:
                                    if t['@schemeIdUri'].lower() == "urn:uuid:xxxx-79d6-4ace-a3c8-129acd51d21ed":
                                        pssh = t["cenc:pssh"]
                            except Exception:
                                pass
                except Exception:
                    pass
    
        if pssh == '':
            pssh = input(
                f"\nUnable to find PSSH ")
            return pssh
        else:
            return pssh
    
    
    def main():
        video_id = input('\nEnter video_id: ')
    
        mpd_resp = requests.get(f'https://ot33333ttv/contents/{video_id}/details')
        cas_id = mpd_resp.json()['VodItems'][0]['CasId']
        mpd_link = mpd_resp.json()['VodItems'][0]['UrlVideo']
        print(f'\nMPD_LINK: {mpd_link}')
        pssh = get_pssh(mpd_link)
        print(f'\nPSSH: {pssh}')
    
        # wvdecrypt = WvDecrypt(pssh, cert_data_b64=None)
        # raw_challenge = wvdecrypt.get_challenge()
        device = Device.load("device_wvd_file.wvd")
        cdm = Cdm.from_device(device)
        session_id = cdm.open()
        raw_challenge = cdm.get_license_challenge(session_id, pssh)
    
        challenge_b64 = b64encode(raw_challenge).decode()
    
        headers = {
            'User-Agent': 'Mozilla/5.0 (Linux; Android 9; sdk_google_atv_x86 Build/PSR1.180720.121; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.158 Mobile Safari/537.36',
            'x-movi-app': '2.9.1',
            'x-mov-deviceid': 'xxxxxxxxxxx',
            'x-mov-ui': '4.0.99',
            'X-Requested-With': 'com.mov.elpepe'
        }
        payload = {
            "accountNumber": "89549xxxx-TEF",
            "UserProfile": "0",
            "deviceType": "REDTTV_OTT",
            "streamFormat": "DASH",
            "streamDRM": "Widevine",
            "streamMiscellanea": "HTTPS",
            "deviceManufacturerProduct": "unknown"
        }
        resp = requests.post('https://clientservices/=ssp&version=8&status=default', headers=headers, json=payload)
        token = resp.json()['token']
        ssp_token = resp.json()['sspToken']
        access_token = resp.json()['accessToken']
    
        headers1 = {
            'User-Agent': 'Mozilla/5.0 (Linux; Android 9; sdk_google_atv_x86 Build/PSR1.180720.121; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.158 Mobile Safari/537.36',
            'X-HZId': token,
            'X-Requested-With': 'com.moeeeetv'
        }
        payload1 = {
            'contentID': video_id,
            'streamType': 'QAST',
            'drmMediaID': cas_id
        }
        resp = requests.post('https://hzmoiajshhd/02542b4af5b60/Session', headers=headers1, json=payload1)
        ctoken = resp.json()['resultData']['cToken']
    
        headers2 = {
    
            'nv-authorizations': ctoken,
            'User-Agent': 'MI5_TV/2.3.1 (Linux;Android 9) ExoPlayerLib/2.11.8'
        }
        resp2 = requests.post('https://wv-otuwhshhk/venseservice/v1/licenses', data=raw_challenge, headers=headers2)
        license_b64 = b64encode(resp2.content).decode()
        print(f'\n{license_b64}')
    
        # wvdecrypt.update_license(license_b64)
        # keys = wvdecrypt.start_process()
        # if keys:
        #     for key in keys:
        #         print(f'\n--key {key}')
        cdm.parse_license(session_id, license_b64)
        for key in cdm.get_keys(session_id):
            print(f"[{key.type}] {key.kid.hex}:{key.key.hex()}")
        cdm.close(session_id)
    
    
    if __name__ == '__main__':
        main()
    If yes, use https://text-compare.com/ to understand what was changed. I also commented in the code what was original, and what is new
    --[----->+<]>.++++++++++++.---.--------.
    [*drm mass downloader: widefrog*]~~~[*how to make your own mass downloader: guide*]
    Quote Quote  
  15. @ Angela

    i've tried to edit this script, but when it seems to work in the first part then in the second part i get an error which instead in the original wks script works fine
    the error is requests.exceptions.HTTPError: 400 Client Error: Bad Request for url: ht*ps://xxxxx/licenses


    @ 2nHxWW6GkN1l91

    thanks for your effort, really appreciated. I will try it soon and then i will let you know if it works. thanks
    about compare i use np++ with compare plug-in
    Last edited by whs912km; 6th Sep 2024 at 10:58.
    Quote Quote  



Similar Threads

Visit our sponsor! Try DVDFab and backup Blu-rays!