VideoHelp Forum
+ Reply to Thread
Results 1 to 19 of 19
Thread
  1. Good evening, my friends, I'd like to discuss with you a decryption procedure with site la7.it. As an example, let's consider the following video url
    Code:
    https://www.la7.it/film-e-fiction/rivedila7/il-processo-di-norimberga-21-06-2024-548773
    Inspecting I can get the data:
    PSSH
    Code:
    AAAAYXBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAAEEIARIQ01pp2bOiS8CAQtaiuFqJPRoFQ29uYXgiJDk4MWM2ZmY4LWM5NGItNGZhNS04NjYyLTNiYzY2YWQ1ZGM5OA==
    License server
    Code:
    https://la7.prod.conax.cloud/widevine/license?d=1719420040062
    mpd
    Code:
    https://d3iki3eydrtvsa.cloudfront.net/IlProcessoDiNorimberga_20240620211948/DASH/IlProcessoDiNorimberga_20240620211948.mpd
    When I put license server and PSSH into keysdb.net or some similar site, it returns an error. Should I use a specific header? What do you think? Thank you very, very much in advance for all help and advices. Have a great evening
    Quote Quote  
  2. The license request can only be used once, presumably because the JWT (submitted with the "preAuthorization" header) contains a JTI to counter replay attacks.

    If you block the license request and use it with your own scripts/tools, it'll return the key:

    Code:
    --key d35a69d9b3a24bc08042d6a2b85a893d:cc97cb1d9d7cc66da2b11b2502d56a3d
    Quote Quote  
  3. Member aqzs's Avatar
    Join Date
    Mar 2024
    Location
    Paris
    Search Comp PM
    It worked well for me without blocking the request, I could even get 5 times the key without changing the header.
    Quote Quote  
  4. Thank you very much for prompt answer and key. I'd like to understand which header to use, for example in site keysdb.net or https://old.cdrm-project.com/ in order to get the key, as, if I leave the header pane empty, I get an error. Thank you very much for all your help
    Quote Quote  
  5. Member aqzs's Avatar
    Join Date
    Mar 2024
    Location
    Paris
    Search Comp PM
    Here is a script that will get the job done for you :
    HTML Code:
    from pywidevine.cdm import Cdm
    from pywidevine.device import Device
    from pywidevine.pssh import PSSH
    import requests
    import re
    from bs4 import BeautifulSoup
    
    def getkey(preTokenUrl, pssh):
        pretoken = requests.get(preTokenUrl).json()['preAuthToken']
        headers = {'preauthorization': pretoken,}
    
        pssh = PSSH(pssh)
        lic_url = 'https://la7.prod.conax.cloud/widevine/license'
    
        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,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':
                keys.append(f"{key.kid.hex}:{key.key.hex()}")
        cdm.close(session_id)
        return keys
    
    def getcommand(url):
        response = requests.get(url)
    
        preTokenUrl = re.search(r'preTokenUrl = "(.*?)"', response.text).group(1)
    
        videoParams = re.search(r'var videoParams\s*=\s*({.*?});', response.text, re.DOTALL).group(1)
        videoParams_clean = re.sub(r'([a-zA-Z_]+)\s*:', r'"\1":', videoParams).replace('"https"', 'https')
    
        mpd_url = re.search(r'"dash"\s*:\s*"([^"]+)"', videoParams_clean).group(1)
        title = re.search(r'"title"\s*:\s*"([^"]+)"', videoParams_clean).group(1)
    
        pssh = BeautifulSoup(requests.get(mpd_url).text, 'lxml-xml').findAll('cenc:pssh')[1].text
    
        keys = getkey(preTokenUrl, pssh)
        print(f"""N_m3u8DL-RE "{mpd_url}" --save-name "{title}" --select-video best --select-audio all --select-subtitle all -mt -M format=mkv  --log-level OFF --key """ + ' --key '.join(keys))
    
    # vidurl = 'https://www.la7.it/film-e-fiction/rivedila7/il-processo-di-norimberga-21-06-2024-548773'
    DEVICEPATH = "device.wvd"
    vidurl = input('Enter the URL of the video: ')
    getcommand(vidurl)
    You have to edit the path of DEVICEPATH. If you don't have a CDM grab one from here : https://forum.videohelp.com/threads/413719-Ready-to-use-CDMs-available-here%21 or make on from here : https://forum.videohelp.com/threads/408031-Dumping-Your-own-L3-CDM-with-Android-Studio

    For exemple for : https://www.la7.it/film-e-fiction/rivedila7/il-processo-di-norimberga-21-06-2024-548773 the script return :
    Code:
    N_m3u8DL-RE "https://d3iki3eydrtvsa.cloudfront.net/IlProcessoDiNorimberga_20240620211948/DASH/IlProcessoDiNorimberga_20240620211948.mpd" --save-name "Il processo di Norimberga" --select-video best --select-audio all --select-subtitle all -mt -M format=mkv  --log-level OFF --key d35a69d9b3a24bc08042d6a2b85a893d:cc97cb1d9d7cc66da2b11b2502d56a3d
    Last edited by aqzs; 26th Jun 2024 at 14:12.
    Quote Quote  
  6. @aqzs, then I might have been really unlucky that the JWT expired below my fingers. The JWT has a lifetime of a couple minutes.
    @misek1963: I guess you'll need at least the preAuthorization header. But haven't tested it.
    Quote Quote  
  7. Member aqzs's Avatar
    Join Date
    Mar 2024
    Location
    Paris
    Search Comp PM
    Originally Posted by Obo View Post
    @aqzs, then I might have been really unlucky that the JWT expired below my fingers. The JWT has a lifetime of a couple minutes.
    @misek1963: I guess you'll need at least the preAuthorization header. But haven't tested it.
    I don't know the expiration time of that token, but for every license request with a token in it, it's just easier to make a python script that retrieve the new token for you!
    Quote Quote  
  8. Yeah, but before writing a script you'll have to analyze the network requests first and manually try what has to be done...

    For expire times and generally payload of JWTs simply paste them at https://jwt.io/
    Quote Quote  
  9. Member aqzs's Avatar
    Join Date
    Mar 2024
    Location
    Paris
    Search Comp PM
    I usually copy raw curl from network tab, if I see an JWT token I search for it in network tab too and copy the request a curl too. I try removing every part from header that can be removed and automate everything. That's how I mad this script : https://forum.videohelp.com/threads/415062-Help-with-la7-it#post2740871 and many others I shared here ^^
    Quote Quote  
  10. Member aqzs's Avatar
    Join Date
    Mar 2024
    Location
    Paris
    Search Comp PM
    Originally Posted by Obo View Post
    Yeah, but before writing a script you'll have to analyze the network requests first and manually try what has to be done...

    For expire times and generally payload of JWTs simply paste them at https://jwt.io/
    JWT token are json base64 encoded. You can read them with that script :
    HTML Code:
    import json
    import base64
    from pprint import pprint
    
    def jd64(input):
        try:
            return json.loads(base64.b64decode(input))
        except base64.binascii.Error :
            if '====' in input:
                return 'Invalid base64-encoded string'
            else:
                return jd64(input+'=')
    
    tokenpart = 'eyJhbGciOiJFUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ'
    pprint(jd64(tokenpart))
    Quote Quote  
  11. Yes, I know. I have my own script to decode them. Which I usually do if I see one.
    Quote Quote  
  12. Originally Posted by aqzs View Post
    Here is a script that will get the job done for you :
    HTML Code:
    from pywidevine.cdm import Cdm
    from pywidevine.device import Device
    from pywidevine.pssh import PSSH
    import requests
    import re
    from bs4 import BeautifulSoup
    
    def getkey(preTokenUrl, pssh):
        pretoken = requests.get(preTokenUrl).json()['preAuthToken']
        headers = {'preauthorization': pretoken,}
    
        pssh = PSSH(pssh)
        lic_url = 'https://la7.prod.conax.cloud/widevine/license'
    
        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,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':
                keys.append(f"{key.kid.hex}:{key.key.hex()}")
        cdm.close(session_id)
        return keys
    
    def getcommand(url):
        response = requests.get(url)
    
        preTokenUrl = re.search(r'preTokenUrl = "(.*?)"', response.text).group(1)
    
        videoParams = re.search(r'var videoParams\s*=\s*({.*?});', response.text, re.DOTALL).group(1)
        videoParams_clean = re.sub(r'([a-zA-Z_]+)\s*:', r'"\1":', videoParams).replace('"https"', 'https')
    
        mpd_url = re.search(r'"dash"\s*:\s*"([^"]+)"', videoParams_clean).group(1)
        title = re.search(r'"title"\s*:\s*"([^"]+)"', videoParams_clean).group(1)
    
        pssh = BeautifulSoup(requests.get(mpd_url).text, 'lxml-xml').findAll('cenc:pssh')[1].text
    
        keys = getkey(preTokenUrl, pssh)
        print(f"""N_m3u8DL-RE "{mpd_url}" --save-name "{title}" --select-video best --select-audio all --select-subtitle all -mt -M format=mkv  --log-level OFF --key """ + ' --key '.join(keys))
    
    # vidurl = 'https://www.la7.it/film-e-fiction/rivedila7/il-processo-di-norimberga-21-06-2024-548773'
    DEVICEPATH = "device.wvd"
    vidurl = input('Enter the URL of the video: ')
    getcommand(vidurl)
    You have to edit the path of DEVICEPATH. If you don't have a CDM grab one from here : https://forum.videohelp.com/threads/413719-Ready-to-use-CDMs-available-here%21 or make on from here : https://forum.videohelp.com/threads/408031-Dumping-Your-own-L3-CDM-with-Android-Studio

    For exemple for : https://www.la7.it/film-e-fiction/rivedila7/il-processo-di-norimberga-21-06-2024-548773 the script return :
    Code:
    N_m3u8DL-RE "https://d3iki3eydrtvsa.cloudfront.net/IlProcessoDiNorimberga_20240620211948/DASH/IlProcessoDiNorimberga_20240620211948.mpd" --save-name "Il processo di Norimberga" --select-video best --select-audio all --select-subtitle all -mt -M format=mkv  --log-level OFF --key d35a69d9b3a24bc08042d6a2b85a893d:cc97cb1d9d7cc66da2b11b2502d56a3d
    Thank you very, very much, for the script tailored for my need. I've grabbed the CDM and now I've got the two files
    Code:
    device_client_id_blob.bin
    and
    Code:
    device_private_key.pem
    which are located in
    Code:
    T:\Pythonult\
    directory. I've edited DEVICEPATH in several ways but I see all the time an invalid sintax error. Maybe my question is a stupid one, surely it is, but may you kindly advice me in detail how to perform the editing? Thank you very, very much, again, for all your help and efforts. I apologize for the trivial question
    Quote Quote  
  13. Member aqzs's Avatar
    Join Date
    Mar 2024
    Location
    Paris
    Search Comp PM
    With those 2 files you need to make a CDM using that command :
    Code:
    pywidevine create-device -k device_private_key  -c device_client_id_blob -t ANDROID -l3 -o WVD
    Or in you case based on name you have :
    Code:
    pywidevine create-device -k device_private_key.pem -c device_client_id_blob.bin -t ANDROID -l3 -o WVD
    Then you should have a file that end with .wvd, copy the path of that file and put it in the script
    Quote Quote  
  14. Originally Posted by aqzs View Post
    With those 2 files you need to make a CDM using that command :
    Code:
    pywidevine create-device -k device_private_key  -c device_client_id_blob -t ANDROID -l3 -o WVD
    Or in you case based on name you have :
    Code:
    pywidevine create-device -k device_private_key.pem -c device_client_id_blob.bin -t ANDROID -l3 -o WVD
    Then you should have a file that end with .wvd, copy the path of that file and put it in the script
    Thank you so much for the code. I've followed your advices and now I've got the following file
    Code:
    unknown_android_sdk_built_for_x86_16.0.0_9d1ea0a0_8162_l3.wvd
    Should I leave this name or change it? When I edit the script should I write only the path to the file (that is
    Code:
    t:\Puthonult\WVD
    ) or the file, too (that is
    Code:
    t:\Puthonult\WVD\unknown_android_sdk_built_for_x86_16.0.0_9d1ea0a0_8162_l3.wvd
    ) ? Thank you very much for your pacience and precious help. Have a great evening
    Quote Quote  
  15. Member aqzs's Avatar
    Join Date
    Mar 2024
    Location
    Paris
    Search Comp PM
    in the script write :
    Code:
    DEVICEPATH = 't:/Puthonult/WVD/unknown_android_sdk_built_for_x86_16.0.0_9d1ea0a0_8162_l3.wvd'
    Quote Quote  
  16. Originally Posted by aqzs View Post
    in the script write :
    Code:
    DEVICEPATH = 't:/Puthonult/WVD/unknown_android_sdk_built_for_x86_16.0.0_9d1ea0a0_8162_l3.wvd'
    Thank you very much, really, for all your so kind help and efforts in explaining in detail all steps. After modifying the script I've been able to launch it. Anyway after inserting the video url (video plays in browser) I get the following error messages
    Code:
    la7.py
    Enter the URL of the video: https://www.la7.it/film-e-fiction/rivedila7/il-processo-di-norimberga-21-06-2024-548773
    Traceback (most recent call last):
      File "T:\Pythonult\la7.py", line 49, in <module>
        getcommand(vidurl)
      File "T:\Pythonult\la7.py", line 41, in getcommand
        pssh = BeautifulSoup(requests.get(mpd_url).text, 'lxml-xml').findAll('cenc:pssh')[1].text
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "T:\Python39\Lib\site-packages\bs4\__init__.py", line 250, in __init__
        raise FeatureNotFound(
    bs4.FeatureNotFound: Couldn't find a tree builder with the features you requested: lxml-xml. Do you need to install a parser library?
    What do you think about this? Thanks, again, for all help. Have a nice day
    Quote Quote  
  17. Member aqzs's Avatar
    Join Date
    Mar 2024
    Location
    Paris
    Search Comp PM
    Code:
    pip install lxml
    Quote Quote  
  18. Originally Posted by aqzs View Post
    Code:
    pip install lxml
    pip is installed but I get the following
    Code:
    t:\Pythonult\pip install lxml
    't:\Pythonult\pip' is not recognized as an internal or external command,
    operable program or batch file.
    Quote Quote  
  19. Originally Posted by aqzs View Post
    Code:
    pip install lxml
    After some trials and errors I get this message
    Code:
    Requirement already satisfied: lxml in t:\pythonult\lib\site-packages (5.2.1)
    , so lxml should be in place, anyway the error is still present. Any help? Thanks, again, for all your patience, I'm annoying you all the time
    Last edited by misiek1963; 28th Jun 2024 at 14:07.
    Quote Quote  
Visit our sponsor! Try DVDFab and backup Blu-rays!