VideoHelp Forum



Support our site by donate $5 directly to us Thanks!!!

Try StreamFab Downloader and download streaming video from Netflix, Amazon!



+ Reply to Thread
Page 3 of 3
FirstFirst 1 2 3
Results 61 to 62 of 62
  1. Originally Posted by totis View Post
    Originally Posted by 2nHxWW6GkN1l916N3ayz8HQoi View Post
    Here is the downloader for nba.com. It can only be used for free videos (with and without an account, and also both classic videos and game recaps).

    Code:
    import json
    import re
    import urllib.parse
    
    import requests
    from pywidevine.cdm import Cdm
    from pywidevine.device import Device
    from pywidevine.pssh import PSSH
    
    AZUKI_IMC = "IMC7.2.0_AN_D3.0.0_S0"
    DEVICE_PROFILE = "eyJtb2RlbCI6IkRlc2t0b3AiLCJvc1ZlcnNpb24iOiIxMCIsInZlbmRvck5hbWUiOiJNaWNyb3NvZnQiLCJvc05hbWUiOiJIVE1MNSIsInd2TGV2ZWwiOiJMMyIsImRldmljZVVVSUQiOiJkZXZpY2VVVUlEIn0="
    EMAIL = 'YOUR_EMAIL'
    PASSWORD = 'YOUR_PASSWORD'
    
    NBA_PLAY_OPTIONS = 'https://ottapp-appgw-client.nba.com/S1/subscriber/{path}/{program_id}/play-options'
    NBA_ROLL = 'https://ottapp-appgw-amp.nba.com/v1/client/roll'
    NBA_API_STS = 'https://identity.nba.com/api/v1/sts'
    NBA_API_AUTH = 'https://identity.nba.com/api/v1/auth'
    
    LICENSE_URL = "https://ottapp-appgw-amp.nba.com/v1/client/get-widevine-license"
    WVD_FILE = "./device_wvd_file.wvd"
    
    
    def get_pssh_info(base_uri, manifest_uri):
        manifest = f"{base_uri}/{manifest_uri}"
        v_m3u8 = re.findall(r'^v.*?\.m3u8\?.*?$', requests.get(manifest).content.decode(), re.MULTILINE)[-1]
        v_m3u8 = f"{base_uri}/{manifest_uri.replace('index.m3u8', v_m3u8)}"
        pssh = re.search(r'base64,([^"]+)"', requests.get(v_m3u8).content.decode()).group(1)
        return manifest, pssh
    
    
    def get_program_info(source_url):
        if "/video/" in source_url:
            return "v3/programs", re.findall(
                r'"mediakindExternalProgramId":"([^"]*)"', requests.get(source_url).content.decode()
            )[0]
        if "/game/" in source_url:
            return "v2/events", max(re.findall(r'\d+', source_url), key=len)
        return None, None
    
    
    def get_auth_token():
        for info in [EMAIL, PASSWORD]:
            if info is None or len(info) == 0:
                return json.loads(requests.get(NBA_API_STS).content.decode())["data"]["AccessToken"]
        response = json.loads(requests.post(
            NBA_API_AUTH, json={'email': EMAIL, 'password': PASSWORD}
        ).content.decode())["data"]
    
        nba_identity = urllib.parse.quote(json.dumps(
            {"jwt": response["jwt"], "refreshToken": response["refreshToken"]}
        ))
        return json.loads(requests.get(
            NBA_API_STS, cookies={'nbaidentity': nba_identity}
        ).content.decode())["data"]["AccessToken"]
    
    
    AUTH_TOKEN = get_auth_token()
    
    
    def get_video_data(source_url):
        path, program_id = get_program_info(source_url)
        response = requests.get(
            NBA_PLAY_OPTIONS.format(path=path, program_id=program_id),
            headers={'Authorization': f'OAUTH2 access_token="{AUTH_TOKEN}"'},
            params={'IsexternalId': 'true'}
        ).content.decode()
    
        matches = re.findall(r'"Id":"([^"]*)"', response)
        vod_id = sorted(
            [r for r in matches if "VIDEO" in r] if "VIDEO" in str(matches) else matches,
            key=len, reverse=True
        )[0]
    
        response = json.loads(requests.post(
            NBA_ROLL, data="{}",
            params={'ownerUid': 'azuki', 'mediaId': vod_id, 'sessionId': 'sessionId'},
            headers={'AuthorizationToken': AUTH_TOKEN, 'AzukiIMC': AZUKI_IMC, 'DeviceProfile': DEVICE_PROFILE},
        ).content.decode())
    
        manifest, pssh = get_pssh_info(
            response["response"]["cdns"]["cdn"][0]["base_uri"],
            response["response"]["manifest_uri"],
        )
        return manifest, pssh, vod_id
    
    
    def get_keys(pssh_value, vod_id):
        if pssh_value is None or vod_id is None:
            return []
    
        pssh = PSSH(pssh_value)
        device = Device.load(WVD_FILE)
        cdm = Cdm.from_device(device)
        cdm_session_id = cdm.open()
        challenge = cdm.get_license_challenge(cdm_session_id, pssh)
    
        licence = requests.post(
            LICENSE_URL, data=challenge,
            headers={"AuthorizationToken": AUTH_TOKEN, 'DeviceProfile': DEVICE_PROFILE, 'AzukiIMC': AZUKI_IMC},
            params={"ownerUid": "azuki", "mediaId": vod_id, "sessionId": "sessionId"}
        )
        licence.raise_for_status()
        cdm.parse_license(cdm_session_id, licence.content)
    
        keys = []
        for key in cdm.get_keys(cdm_session_id):
            if "CONTENT" in key.type:
                keys += [f"{key.kid.hex}:{key.key.hex()}"]
        cdm.close(cdm_session_id)
        return keys
    
    
    def get_download_command(source_url):
        manifest, pssh_value, vod_id = get_video_data(source_url)
        keys = get_keys(pssh_value, vod_id)
    
        if len(keys) == 0:
            return f"N_m3u8DL-RE.exe {manifest} -M format=mkv"
        return f"N_m3u8DL-RE.exe {manifest} {' '.join([f'--key {k}' for k in keys])} -M format=mkv"
    
    
    SOURCE_URLS = [
        # Without account
        "https://www.nba.com/watch/video/game-recap-lakers-120-timberwolves-109",
        "https://www.nba.com/watch/video/the-fast-break-mar-10-2",
        "https://www.nba.com/watch/video/game-recap-wizards-110-heat-108",
        "https://www.nba.com/game/hou-vs-ind-0022300719?watchRecap=true",
        "https://www.nba.com/game/mil-vs-lac-0022300924?watchRecap=true",
        "https://www.nba.com/game/ind-vs-orl-0022300928?watchRecap=true",
        # With (free) account
        "https://www.nba.com/watch/video/2016-dunk-contest-lavine-vs-gordon-best-ever",
        "https://www.nba.com/watch/video/2-27-2023-banchero-takes-over-in-clutch-vs-pels",
        "https://www.nba.com/watch/video/12-7-23-haliburton-shines-in-in-season-tournament",
    ]
    for s in SOURCE_URLS:
        print(get_download_command(s))
    Output:
    Code:
    N_m3u8DL-RE.exe https://nbalpng.akamaized.net/vod-pz/a/hls-wvpr/NBA_202403110042NBA_____VIDEOS__NBAE_2730310/index.m3u8?addUserInfo=1 --key 5ebee3c8d5e10606e92be5bb4fad2dab:80f3b559dedfcba9092fff0abf666aad -M format=mkv
    N_m3u8DL-RE.exe https://nbalpng.akamaized.net/vod-pz/a/hls-wvpr/NBA_436C1F6E-DF62-11EE-967D-3CFDFEE7DB71/index.m3u8?addUserInfo=1 --key 843e4a9d08bab7caa44ba37d33b08201:b02e50d244af1e211edc730b15f56b6a -M format=mkv
    N_m3u8DL-RE.exe https://nbalpng.akamaized.net/vod-pz/a/hls-wvpr/NBA_202403102202NBA_____VIDEOS__NBAE_2730223/index.m3u8?addUserInfo=1 --key 13701f9eefda552a1dcb4043a14e899c:8096b4a869983f8fa775787fbda20669 -M format=mkv
    N_m3u8DL-RE.exe https://nbalpng.akamaized.net/vod-pz/a/hls-wvpr/NBA_202402062235NBA_____VIDEOS__NBAE_2719841/index.m3u8?addUserInfo=1 --key b8ab402803beb8e7bbcdca2b8e68f587:ecc693579cf87b70651843297a52b29e -M format=mkv
    N_m3u8DL-RE.exe https://nbalpng.akamaized.net/vod-pz/a/hls-wvpr/NBA_202403101839NBA_____VIDEOS__NBAE_2730064/index.m3u8?addUserInfo=1 --key a28f78780df93c40c3537bef7423b38e:b6436ef1665c8474d48c943440ee5303 -M format=mkv
    N_m3u8DL-RE.exe https://nbalpng.akamaized.net/vod-pz/a/hls-wvpr/NBA_202403102200NBA_____VIDEOS__NBAE_2730212/index.m3u8?addUserInfo=1 --key f81eb304966d19d02bd90d27522ff25d:11cfe5b53309cfb500c47db09ad7dc82 -M format=mkv
    N_m3u8DL-RE.exe https://nbalpng.akamaized.net/vod-pz/a/hls-wvpr/NBA_202301290937NBA_____VIDEOS__NBAE_2630941/index.m3u8?addUserInfo=1 --key 964f42e46638932b33427461f136459f:06ee868164f7d156637fde3fa500a671 -M format=mkv
    N_m3u8DL-RE.exe https://nbalpng.akamaized.net/vod-pz/a/hls-wvpr/NBA_202401301756NBA_____VIDEOS__NBAE_2717587/index.m3u8?addUserInfo=1 --key 6eb0a05cb4fe3dd565df106a02d2419c:eb8f629dc8354f593ce2059a12a75a0b -M format=mkv
    N_m3u8DL-RE.exe https://nbalpng.akamaized.net/vod-pz/a/hls-wvpr/NBA_202402121203NBA_____VIDEOS__NBAE_2721586/index.m3u8?addUserInfo=1 --key b7866579c32ff24d52882fe194a2ac99:f9fe7b15d02deca76749b80963e68252 -M format=mkv
    Their login functionality was implemented in a good way, which was a nice thing to see. The easy solution was to simply use the browser cookies to skip that. So if you want free videos that need a free account to watch, make one and log in using the Firefox browser (and make sure the browser cookies don't get deleted).

    Also, their API was kinda inconsistent in some responses so the script may not work for the rare video once in a while. Regardless, if there are problems, just leave a message here and I may take a look.

    Edit: Seems I kinda praised their login functionality a bit too much. Now the script works fully programmatically without needing to check for existing browser cookies. If you have an account just fill in your email and password. If you just want videos without needing a free account, just make the EMAIL and PASSWORD variables empty strings (the script will obviously fail then if you attempt to download account-required videos).

    Edit2: I've seen that some users needed a US VPN. So if something fails, I guess try with that.
    Hello I have tried to get this script to work but it gives this error:
    in get_pssh_info
    v_m3u8 = re.findall(r'^v.*?\.m3u8\?.*?$', requests.get(manifest).content.decode(), re.MULTILINE)[-1]
    IndexError: list index out of range

    I manually retrieved the pssh and I was able to get the key and download but I want the script to not fail.
    I used this code with login and password and it also gave the same error, how did you manually correct the PSHHImage
    [Attachment 78473 - Click to enlarge]
    to get the keys?
    Quote Quote  
  2. Originally Posted by braquino21 View Post
    I would like to thank everyone here in the group again for their attention and all their help, after brainstorming I managed to generate my own device_client_id_blob and device_private_key files and then I just had to do the conversion to device_wvd_file.wvd format and put them inside the root of pywidevine-master on the first attempt I was rewarded with the my first two keys " but soon after the third attempt the next error occurredImage
    [Attachment 78472 - Click to enlarge]
    You need to update the authorization token. It expires after a while, so of course you start getting errors at some point.
    Quote Quote  



Similar Threads

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