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 1 of 2
1 2 LastLast
Results 1 to 30 of 41
  1. Hello there,

    Before i decided to write here i tried to follow some instructions i had found here and there but it seems that my mind's capacity is limited. i'm sorry if i sound stupid, but i really would like to know how a drm video can be downloaded.

    Having said this, i've been trying to download this video: https://www.tf1.fr/tmc/quotidien-avec-yann-barthes/videos/invites-fanny-ardant-et-thie...-85679919.html To watch it you need to set a vpn to france.
    i'm not sure if i found the correct .mpd file, but here it is what i found:

    https://vod-das.cdn-0.diff.tf1.fr/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjaXAiOiI5MC4...m/14084295.mpd

    also i've tried to find the key from this mpd and i found this: "42558A25-B0B6-5975-AEC8-4C8D39B607F6"

    is it correct? if so, how can i continue from here, please? if not, would you be so kind to tell me the right ones, please?

    thank you in advance for you time and help!
    Quote Quote  
  2. Feels Good Man 2nHxWW6GkN1l916N3ayz8HQoi's Avatar
    Join Date
    Jan 2024
    Location
    Pepe Island
    Search Comp PM
    Code:
    14a80493a42f564a94de6f774dbb396b:0fbf6719952e5d8ce492f607eb178381
    93829e13b18c53cbb906d61b9f1d1140:66f12939e4c26f49ce645aa46f9959e2
    41349d3d2dde582bbb1cf8d03bcb4b1d:9800df594adc46a5db7189456e3f063b
    Edit: I think this can be automated with a proper vpn. Might be wrong though.
    --[----->+<]>.++++++++++++.---.--------.
    [*drm mass downloader: widefrog*]~~~~~~~~~~~[*how to make your own mass downloader: guide*]
    Quote Quote  
  3. Originally Posted by grizabella View Post
    but it seems that my mind's capacity is limited.
    Before pepe makes it even easier for you, here, watch the video in this. You also have the tools needed inside.
    https://we.tl/t-EeLmYVR5hj
    click click2
    If I/my posts ever helped you, and you want to give back, send me a private message!
    Quote Quote  
  4. thank you so much, both of you! I'm super grateful!

    Originally Posted by [ss]vegeta View Post
    Originally Posted by grizabella View Post
    but it seems that my mind's capacity is limited.
    Before pepe makes it even easier for you, here, watch the video in this. You also have the tools needed inside.
    https://we.tl/t-EeLmYVR5hj
    you have now idea how much you've helped me! i use macos, but i will try my best to download the video based on your help. actually, i already have 2 of the 3 tools installed, i need to find the last one (n) for mac. thank you again and have a lovely day!
    Quote Quote  
  5. i've done it, THANK YOU so much for your time and help. BOTH of you! extremely grateful. <3
    Quote Quote  
  6. Search, Learn, Download! Karoolus's Avatar
    Join Date
    Oct 2022
    Location
    Belgium
    Search Comp PM
    And now we're all part of Vegeta's botnet!

    Jokes aside though, good video! Shows clearly how to do it
    Quote Quote  
  7. Originally Posted by Karoolus View Post
    And now we're all part of Vegeta's botnet!

    Image
    [Attachment 77621 - Click to enlarge]


    Originally Posted by grizabella View Post
    i use macos!
    Slightly less cancerous than DRM.

    Originally Posted by grizabella View Post
    i've done it
    I knew you aren't that limited.
    click click2
    If I/my posts ever helped you, and you want to give back, send me a private message!
    Quote Quote  
  8. Feels Good Man 2nHxWW6GkN1l916N3ayz8HQoi's Avatar
    Join Date
    Jan 2024
    Location
    Pepe Island
    Search Comp PM
    Here is a downloader for tf1. You need to write your email and password (no Google/Apple/Facebook, just the classic way). I have no idea if there are free videos available for someone without an account.

    Code:
    import json
    import re
    import sys
    import urllib.parse
    
    import requests
    from pywidevine.cdm import Cdm
    from pywidevine.device import Device
    from pywidevine.pssh import PSSH
    
    LICENSE_URL = "https://widevine-proxy-m.prod.p.tf1.fr/proxy"
    WVD_FILE = "./device_wvd_file.wvd"
    
    TF1_LOGIN = 'https://compte.tf1.fr/accounts.login'
    TF1_TOKEN = 'https://www.tf1.fr/token/gigya/web'
    TF1_MEDIA = 'https://mediainfo.tf1.fr/mediainfocombo/{media_id}'
    
    TF1_PLAYER = 'https://prod-player.tf1.fr'
    TF1_URL = 'https://www.tf1.fr'
    
    EMAIL = "YOUR_EMAIL"
    PASSWORD = "YOUR_PASSWORD"
    
    
    def format_version(version):
        major, minor, patch = map(int, version.split('.'))
        return str(major * 1000000 + minor * 1000 + patch)
    
    
    def get_tf1_info():
        response = requests.get(TF1_URL).content.decode()
        api_key = re.findall(r'"apiKey":"([^"]+)"', response)[0]
        consent_ids = re.findall(r'neededConsentIds":\[(.*?)]', response)[0].replace("\"", "").split(",")
    
        player_version = re.findall(rf'"playerEndpoint":"{TF1_PLAYER}/","version":"([^"]+)"', response)[0]
        return api_key, consent_ids, format_version(player_version)
    
    
    API_KEY, CONSENT_IDS, PLAYER_VERSION = get_tf1_info()
    
    
    def get_bearer_token():
        response = json.loads(requests.post(
            TF1_LOGIN, headers={'Content-Type': 'application/x-www-form-urlencoded'},
            data=f'loginID={urllib.parse.quote(EMAIL)}&password={PASSWORD}&APIKey={API_KEY}'
        ).content.decode())
    
        login_status = response.get('statusCode')
        if login_status == 200:
            print(f'\nTF1 login OK')
        else:
            sys.exit(f'\nTF1 login error: {login_status}')
    
        return json.loads(requests.post(
            TF1_TOKEN,
            json={
                'uid': response["UID"], 'signature': response["UIDSignature"],
                'timestamp': int(response["signatureTimestamp"]),
                'consent_ids': CONSENT_IDS
    
            }
        ).content.decode())["token"]
    
    
    BEARER_TOKEN = get_bearer_token()
    
    
    def get_video_data(source_url):
        matches = re.findall(r'"embedUrl":"([^"]+)"', requests.get(source_url).content.decode())
        match = [m for m in matches if "/player/" in m][0]
        media_id = re.findall(r'/player/([^/]+)', match)[0]
        response = requests.get(
            TF1_MEDIA.format(media_id=media_id),
            params={'pver': PLAYER_VERSION, 'context': 'context'},
            headers={'authorization': f'Bearer {BEARER_TOKEN}'}
        )
    
        response = response.content.decode()
        if "GEOBLOCKED" in response:
            print("VPN FAILURE")
            exit(0)
        manifest = json.loads(response)["delivery"]["url"]
        pssh_value = str(min(
            re.findall(r'<cenc:pssh>(.+?)</cenc:pssh>', requests.get(manifest).content.decode()), key=len
        ))
        return manifest, pssh_value
    
    
    def get_keys(pssh_value):
        if pssh_value 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)
        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 = get_video_data(source_url)
        keys = get_keys(pssh_value)
    
        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 = [
        "https://www.tf1.fr/tf1/esprits-criminels/videos/esprits-criminels-s08-e17-le-poids-des-mots-91857726.html",
        "https://www.tf1.fr/tf1/lycee-toulouse-lautrec/videos/lycee-toulouse-lautrec-s02-e01-la-peur-58024474.html",
        "https://www.tf1.fr/tmc/quotidien-avec-yann-barthes/videos/invites-fanny-ardant-et-thierry-klifa-rois-de-la-piste-et-de-larnaque-85679919.html",
        "https://www.tf1.fr/tmc/la-mode-by-loic-prigent/videos/5-minutes-de-mode-by-loic-prigent-du-8-mars-2024-73931950.html",
        "https://www.tf1.fr/tfx/chroniques-criminelles/videos/chroniques-criminelles-laffaire-emile-louis-les-disparues-de-lyonne-laffaire-grenier-le-pompier-aux-deux-visages-29670038.html",
        "https://www.tf1.fr/tfx/incroyables-mariages-gitans/videos/incroyables-mariages-gitans-paillettes-et-strass-pour-la-fete-de-leur-vie-20645319.html"
    ]
    for s in SOURCE_URLS:
        print(get_download_command(s))
    Output (I have shortened the manifest URLs because they will expire anyway since they have a token included in them):
    Code:
    N_m3u8DL-RE.exe https://vod-das.cdn-0.diff.tf1.fr/ey.../2/USP-0x0/15/04/14081504/ssm/662fc22a79004b5a5c3ab5b923253963047a3a4aaadcc0b700b8845caf8c10da.ism/14081504.mpd --key 04b4d96a711f510387637cc0ff487794:d74c6316a1a314d0e47a1e5e96b43493 --key 8adfbb94d8be5a719768a49116cc1d6a:3179e9f0a921be5ef5dc1325bb519bf9 --key 5d88b40a02dd5a4da857136bc93b4405:12cd44e9f3114df891d04e6f7138aca9 -M format=mkv
    N_m3u8DL-RE.exe https://vod-das.cdn-0.diff.tf1.fr/ey.../2/USP-0x0/25/52/14082552/ssm/ad98e512b44b31a01b874a4a11b94b75d6774a640a4cf5099427f95bfea18eb2.ism/14082552.mpd --key 8b02db604c37512e8f991b24949776ce:9b4f680fc264ca10ec79cdc4042a5559 --key b58b3e54e81d5cb0af74c78ae16a7776:d79d449cb4396e989e5ac2d55c9ede73 --key 4cc2b714030d56aaa3446752ebe7a5e6:e0dca060a2227df0f188574fcad7089a -M format=mkv
    N_m3u8DL-RE.exe https://vod-das.cdn-0.diff.tf1.fr/ey.../2/USP-0x0/42/95/14084295/ssm/aa9ab7d3530c8cf8c613a65657e332adc3e77979a7cd54b7cb62b6af28756de9.ism/14084295.mpd --key 14a80493a42f564a94de6f774dbb396b:0fbf6719952e5d8ce492f607eb178381 --key 93829e13b18c53cbb906d61b9f1d1140:66f12939e4c26f49ce645aa46f9959e2 --key 41349d3d2dde582bbb1cf8d03bcb4b1d:9800df594adc46a5db7189456e3f063b -M format=mkv
    N_m3u8DL-RE.exe https://vod-das.cdn-0.diff.tf1.fr/ey.../2/USP-0x0/19/91/14081991/ssm/207d14ed0d94635cc7a427c0e94e9e5e0b5b7ba9c5bbd7e790145eb38062d35c.ism/14081991.mpd --key 18685f497668574ba14971676f24ae09:ec3e4d42cd4dd4f2e07aea37a399a247 --key 8d9cd0ed61c4563985243ddfe45b2167:e37d4db29a055309343078077957958c --key b1d7f3716aba5f2f9fc7cfdba0e5e6c9:17c86742f496788e20050686c6e2f987 -M format=mkv
    N_m3u8DL-RE.exe https://vod-das.cdn-0.diff.tf1.fr/ey.../2/USP-0x0/28/76/14082876/ssm/2b2d91414e9720585451a381db75c83653ef65b4564c1ae31c4223f8fb228c01.ism/14082876.mpd --key e4abd497132b57cc9dc10166da27e319:baeb9de780ee40648c39dec7c686f772 --key 2a78be13fe595fc8851c5b1aa3227f19:937cc21c52c32aa2749c5b53c1127b6a --key ba4bd56eafb3593dbf019351356db347:2ae595dd961f3bd0e060b23b388a3b7b -M format=mkv
    N_m3u8DL-RE.exe https://vod-das.cdn-0.diff.tf1.fr/ey.../2/USP-0x0/32/48/14083248/ssm/dfd102d475b72dc3835d0d1fe023801597673f071704576a792740b648eb43d4.ism/14083248.mpd --key 2b79d01756925188a680cab4c89e0009:03951a2b75cb295065c79b2531e08cd9 --key 692f01b998b45338a673afe0b97ff43d:7c83ed49ad64b54f541d200a83fbf1ba --key ed1bb4550a535522bdd182cfd7239219:f38d177d85425036f403a808a9681584 -M format=mkv
    Have only checked for a few random videos. You need a French IP. If you use a VPN, sometimes the site may reject your IP, so refresh and get a new one. The script only works for free videos. If it crashes for a specific video, leave a message here.

    Also, I have no idea if there is a mpd link hidden for higher resolutions than 720p. If you know how to get one (if it even exists), I may modify the script.

    Edit: Only the script needs a French IP. Once you have the download command, you can turn it off to avoid wasting data on the actual N_m3u8 video download.

    Edit2: Login checking added by @ice. Thanks!

    Edit3: This script has been added and extended in the widefrog tool so I won't maintain it here. Any relevant updates will take place in the support thread.
    Last edited by 2nHxWW6GkN1l916N3ayz8HQoi; 21st May 2024 at 04:27.
    --[----->+<]>.++++++++++++.---.--------.
    [*drm mass downloader: widefrog*]~~~~~~~~~~~[*how to make your own mass downloader: guide*]
    Quote Quote  
  9. Originally Posted by 2nHxWW6GkN1l916N3ayz8HQoi View Post
    Here is a downloader for tf1. You need to write your email and password (no Google/Apple/Facebook, just the classic way). I have no idea if there are free videos available for someone without an account.

    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
    
    LICENSE_URL = "https://widevine-proxy-m.prod.p.tf1.fr/proxy"
    WVD_FILE = "./device_wvd_file.wvd"
    
    TF1_LOGIN = 'https://compte.tf1.fr/accounts.login'
    TF1_TOKEN = 'https://www.tf1.fr/token/gigya/web'
    TF1_MEDIA = 'https://mediainfo.tf1.fr/mediainfocombo/{media_id}'
    
    TF1_PLAYER = 'https://prod-player.tf1.fr'
    TF1_URL = 'https://www.tf1.fr'
    
    EMAIL = "YOUR_EMAIL"
    PASSWORD = "YOUR_PASSWORD"
    
    
    def format_version(version):
        major, minor, patch = map(int, version.split('.'))
        return str(major * 1000000 + minor * 1000 + patch)
    
    
    def get_tf1_info():
        response = requests.get(TF1_URL).content.decode()
        api_key = re.findall(r'"apiKey":"([^"]+)"', response)[0]
        consent_ids = re.findall(r'neededConsentIds":\[(.*?)]', response)[0].replace("\"", "").split(",")
    
        player_version = re.findall(rf'"playerEndpoint":"{TF1_PLAYER}/","version":"([^"]+)"', response)[0]
        return api_key, consent_ids, format_version(player_version)
    
    
    API_KEY, CONSENT_IDS, PLAYER_VERSION = get_tf1_info()
    
    
    def get_bearer_token():
        response = json.loads(requests.post(
            TF1_LOGIN, headers={'Content-Type': 'application/x-www-form-urlencoded'},
            data=f'loginID={urllib.parse.quote(EMAIL)}&password={PASSWORD}&APIKey={API_KEY}'
        ).content.decode())
    
        return json.loads(requests.post(
            TF1_TOKEN,
            json={
                'uid': response["UID"], 'signature': response["UIDSignature"],
                'timestamp': int(response["signatureTimestamp"]),
                'consent_ids': CONSENT_IDS
    
            }
        ).content.decode())["token"]
    
    
    BEARER_TOKEN = get_bearer_token()
    
    
    def get_video_data(source_url):
        matches = re.findall(r'"embedUrl":"([^"]+)"', requests.get(source_url).content.decode())
        match = [m for m in matches if "/player/" in m][0]
        media_id = re.findall(r'/player/([^/]+)', match)[0]
        response = requests.get(
            TF1_MEDIA.format(media_id=media_id),
            params={'pver': PLAYER_VERSION, 'context': 'context'},
            headers={'authorization': f'Bearer {BEARER_TOKEN}'}
        )
    
        response = response.content.decode()
        if "GEOBLOCKED" in response:
            print("VPN FAILURE")
            exit(0)
        manifest = json.loads(response)["delivery"]["url"]
        pssh_value = str(min(
            re.findall(r'<cenc:pssh>(.+?)</cenc:pssh>', requests.get(manifest).content.decode()), key=len
        ))
        return manifest, pssh_value
    
    
    def get_keys(pssh_value):
        if pssh_value 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)
        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 = get_video_data(source_url)
        keys = get_keys(pssh_value)
    
        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 = [
        "https://www.tf1.fr/tf1/esprits-criminels/videos/esprits-criminels-s08-e17-le-poids-des-mots-91857726.html",
        "https://www.tf1.fr/tf1/lycee-toulouse-lautrec/videos/lycee-toulouse-lautrec-s02-e01-la-peur-58024474.html",
        "https://www.tf1.fr/tmc/quotidien-avec-yann-barthes/videos/invites-fanny-ardant-et-thierry-klifa-rois-de-la-piste-et-de-larnaque-85679919.html",
        "https://www.tf1.fr/tmc/la-mode-by-loic-prigent/videos/5-minutes-de-mode-by-loic-prigent-du-8-mars-2024-73931950.html",
        "https://www.tf1.fr/tfx/chroniques-criminelles/videos/chroniques-criminelles-laffaire-emile-louis-les-disparues-de-lyonne-laffaire-grenier-le-pompier-aux-deux-visages-29670038.html",
        "https://www.tf1.fr/tfx/incroyables-mariages-gitans/videos/incroyables-mariages-gitans-paillettes-et-strass-pour-la-fete-de-leur-vie-20645319.html"
    ]
    for s in SOURCE_URLS:
        print(get_download_command(s))
    Output (I have shortened the manifest URLs because they will expire anyway since they have a token included in them):
    Code:
    N_m3u8DL-RE.exe https://vod-das.cdn-0.diff.tf1.fr/ey.../2/USP-0x0/15/04/14081504/ssm/662fc22a79004b5a5c3ab5b923253963047a3a4aaadcc0b700b8845caf8c10da.ism/14081504.mpd --key 04b4d96a711f510387637cc0ff487794:d74c6316a1a314d0e47a1e5e96b43493 --key 8adfbb94d8be5a719768a49116cc1d6a:3179e9f0a921be5ef5dc1325bb519bf9 --key 5d88b40a02dd5a4da857136bc93b4405:12cd44e9f3114df891d04e6f7138aca9 -M format=mkv
    N_m3u8DL-RE.exe https://vod-das.cdn-0.diff.tf1.fr/ey.../2/USP-0x0/25/52/14082552/ssm/ad98e512b44b31a01b874a4a11b94b75d6774a640a4cf5099427f95bfea18eb2.ism/14082552.mpd --key 8b02db604c37512e8f991b24949776ce:9b4f680fc264ca10ec79cdc4042a5559 --key b58b3e54e81d5cb0af74c78ae16a7776:d79d449cb4396e989e5ac2d55c9ede73 --key 4cc2b714030d56aaa3446752ebe7a5e6:e0dca060a2227df0f188574fcad7089a -M format=mkv
    N_m3u8DL-RE.exe https://vod-das.cdn-0.diff.tf1.fr/ey.../2/USP-0x0/42/95/14084295/ssm/aa9ab7d3530c8cf8c613a65657e332adc3e77979a7cd54b7cb62b6af28756de9.ism/14084295.mpd --key 14a80493a42f564a94de6f774dbb396b:0fbf6719952e5d8ce492f607eb178381 --key 93829e13b18c53cbb906d61b9f1d1140:66f12939e4c26f49ce645aa46f9959e2 --key 41349d3d2dde582bbb1cf8d03bcb4b1d:9800df594adc46a5db7189456e3f063b -M format=mkv
    N_m3u8DL-RE.exe https://vod-das.cdn-0.diff.tf1.fr/ey.../2/USP-0x0/19/91/14081991/ssm/207d14ed0d94635cc7a427c0e94e9e5e0b5b7ba9c5bbd7e790145eb38062d35c.ism/14081991.mpd --key 18685f497668574ba14971676f24ae09:ec3e4d42cd4dd4f2e07aea37a399a247 --key 8d9cd0ed61c4563985243ddfe45b2167:e37d4db29a055309343078077957958c --key b1d7f3716aba5f2f9fc7cfdba0e5e6c9:17c86742f496788e20050686c6e2f987 -M format=mkv
    N_m3u8DL-RE.exe https://vod-das.cdn-0.diff.tf1.fr/ey.../2/USP-0x0/28/76/14082876/ssm/2b2d91414e9720585451a381db75c83653ef65b4564c1ae31c4223f8fb228c01.ism/14082876.mpd --key e4abd497132b57cc9dc10166da27e319:baeb9de780ee40648c39dec7c686f772 --key 2a78be13fe595fc8851c5b1aa3227f19:937cc21c52c32aa2749c5b53c1127b6a --key ba4bd56eafb3593dbf019351356db347:2ae595dd961f3bd0e060b23b388a3b7b -M format=mkv
    N_m3u8DL-RE.exe https://vod-das.cdn-0.diff.tf1.fr/ey.../2/USP-0x0/32/48/14083248/ssm/dfd102d475b72dc3835d0d1fe023801597673f071704576a792740b648eb43d4.ism/14083248.mpd --key 2b79d01756925188a680cab4c89e0009:03951a2b75cb295065c79b2531e08cd9 --key 692f01b998b45338a673afe0b97ff43d:7c83ed49ad64b54f541d200a83fbf1ba --key ed1bb4550a535522bdd182cfd7239219:f38d177d85425036f403a808a9681584 -M format=mkv
    Have only checked for a few random videos. You need a French IP. If you use a VPN, sometimes the site may reject your IP, so refresh and get a new one. The script only works for free videos. If it crashes for a specific video, leave a message here.

    Also, I have no idea if there is a mpd link hidden for higher resolutions than 720p. If you know how to get one (if it even exists), I may modify the script.
    OMG, THANK YOU SO MUCH! i'm so grateful for your time and help. have a lovely day! <3
    Quote Quote  
  10. Originally Posted by 2nHxWW6GkN1l916N3ayz8HQoi View Post
    Here is a downloader for tf1. You need to write your email and password (no Google/Apple/Facebook, just the classic way). I have no idea if there are free videos available for someone without an account.
    Thank you, script master

    I had started to collect information to try to make a script, but I was very far from this
    Now, with yours, I'll be able to understand its construction better.
    Quote Quote  
  11. Feels Good Man 2nHxWW6GkN1l916N3ayz8HQoi's Avatar
    Join Date
    Jan 2024
    Location
    Pepe Island
    Search Comp PM
    Originally Posted by IceM View Post
    Thank you, script master
    I'm afraid that's too big a word for me, but regardless, you're welcome.

    Originally Posted by IceM View Post
    I had started to collect information to try to make a script, but I was very far from this
    Now, with yours, I'll be able to understand its construction better.
    Here's a neat little trick you can do to speed up the coding process. Start by simply writing a script to get the keys as if you were doing it manually. That means you need the license, pssh, and headers. In this case, there are absolutely no headers needed and the fun thing is that even though the manually captured license request seems to have some additional query parameters those are not even needed at all if you remove them

    So all you need is the PSSH. You can then create a function for the cdm request, and another one that deals with the pssh. The CDM request is already solved and you can continue by starting to implement the pssh function that deals with obtaining it. And from pssh, you get to mpd and you just drop the curl request that returns an mpd link. If it needs additional parameters you then deal with obtaining them, etc etc.

    In a way, you're writing the script from end to beginning. The beginning will receive only the source URL as a parameter. This way is easier since you focus only on the relevant requests and you also write Python code at the same time. It's faster to do them both simultaneously.

    Originally Posted by grizabella View Post
    OMG, THANK YOU SO MUCH! i'm so grateful for your time and help. have a lovely day! <3
    No problem
    Last edited by 2nHxWW6GkN1l916N3ayz8HQoi; 12th Mar 2024 at 12:29.
    --[----->+<]>.++++++++++++.---.--------.
    [*drm mass downloader: widefrog*]~~~~~~~~~~~[*how to make your own mass downloader: guide*]
    Quote Quote  
  12. I already have a working script, using all the information available on the forum, to get the keys, it's the part to automatically retrieve the mpd, headers and license I started investigating
    Last edited by IceM; 12th Mar 2024 at 13:20.
    Quote Quote  
  13. Hello, I'm trying to download from TF1+ too. Thank you for the script. I'm not well versed in scripting nor programming therefore what I'm asking might be silly, please bear with me.

    1. I'm on Windows. I managed to install pywidevine. (in command prompt)
    2. I ran the script (in Visual Studio Code) and everything seems to be working up to the point where it hits this part:

    Originally Posted by 2nHxWW6GkN1l916N3ayz8HQoi View Post
    Code:
    LICENSE_URL = "https://widevine-proxy-m.prod.p.tf1.fr/proxy"
    WVD_FILE = "./device_wvd_file.wvd"
    I get this error: [Errno 2] No such file or directory: 'device_wvd_file.wvd'

    I feel like i should create a .wvd file or i should have it already by some means. There was a We transfer link above in a post with some tools that would be needed but the link expired. Can someone enlighten me on how to proceed? I tried looking around on the forum, there are a lot of posts that just ended up confusing me.

    Originally Posted by 2nHxWW6GkN1l916N3ayz8HQoi View Post
    Also, I have no idea if there is a mpd link hidden for higher resolutions than 720p. If you know how to get one (if it even exists), I may modify the script.
    I think with your free account you cannot get 1080p streams, you need to subscribe to a premium account then you can get 1080p streams. For now I'm only interested in getting the 720p streams at the very least. Then later on I'll see if the 1080p can be done.
    Quote Quote  
  14. Good morning,

    If you need to convert CDM format between WVD <==> Blobs, consider using this tool.
    https://emarsden.github.io/pssh-box-wasm/convert/

    I watched the video on MYTF1 for a free account is 720P.
    Image
    [Attachment 78671 - Click to enlarge]


    you will have to copy the same name into the script which will give the file device_wvd_file.wvd.
    Quote Quote  
  15. Originally Posted by sesamap159 View Post
    Good morning,

    If you need to convert CDM format between WVD <==> Blobs, consider using this tool.
    https://emarsden.github.io/pssh-box-wasm/convert/

    I watched the video on MYTF1 for a free account is 720P.
    Image
    [Attachment 78671 - Click to enlarge]


    you will have to copy the same name into the script which will give the file device_wvd_file.wvd.
    Thank you for your help.

    While waiting for a response, i found this thread https://forum.videohelp.com/threads/410429-Widevine-cdm-in-wvd-format and based on your answer and that thread, i now know that i need to find my Client ID and Private Key. If i understand correctly i need a rooted non-samsung android phone to get those. Right?

    The combination of Client ID and Private Key is called a CDM and i can use those to generate my widevine WVD file. Somebody can correct me if I'm wrong. I'm thinking out loud such that if another newbie comes across this thread, he can get most information here.
    Quote Quote  
  16. here is a link with valid CDMs: https://forum.videohelp.com/threads/413719-Ready-to-use-CDMs-available-here!

    to convert device_private_key and device_client_id_blob files to WVD.wvd with cmd:
    Code:
    pip install pywidevine
    pywidevine create-device -t ANDROID -l 3 -k device_private_key -c device_client_id_blob
    pip uninstall pywidevine
    Quote Quote  
  17. Originally Posted by 2nHxWW6GkN1l916N3ayz8HQoi View Post
    Here is a downloader for tf1. You need to write your email and password (no Google/Apple/Facebook, just the classic way). I have no idea if there are free videos available for someone without an account.

    Code:
    import json
    import re
    import sys
    import urllib.parse
    
    import requests
    from pywidevine.cdm import Cdm
    from pywidevine.device import Device
    from pywidevine.pssh import PSSH
    
    LICENSE_URL = "https://widevine-proxy-m.prod.p.tf1.fr/proxy"
    WVD_FILE = "./device_wvd_file.wvd"
    
    TF1_LOGIN = 'https://compte.tf1.fr/accounts.login'
    TF1_TOKEN = 'https://www.tf1.fr/token/gigya/web'
    TF1_MEDIA = 'https://mediainfo.tf1.fr/mediainfocombo/{media_id}'
    
    TF1_PLAYER = 'https://prod-player.tf1.fr'
    TF1_URL = 'https://www.tf1.fr'
    
    EMAIL = "YOUR_EMAIL"
    PASSWORD = "YOUR_PASSWORD"
    
    
    def format_version(version):
        major, minor, patch = map(int, version.split('.'))
        return str(major * 1000000 + minor * 1000 + patch)
    
    
    def get_tf1_info():
        response = requests.get(TF1_URL).content.decode()
        api_key = re.findall(r'"apiKey":"([^"]+)"', response)[0]
        consent_ids = re.findall(r'neededConsentIds":\[(.*?)]', response)[0].replace("\"", "").split(",")
    
        player_version = re.findall(rf'"playerEndpoint":"{TF1_PLAYER}/","version":"([^"]+)"', response)[0]
        return api_key, consent_ids, format_version(player_version)
    
    
    API_KEY, CONSENT_IDS, PLAYER_VERSION = get_tf1_info()
    
    
    def get_bearer_token():
        response = json.loads(requests.post(
            TF1_LOGIN, headers={'Content-Type': 'application/x-www-form-urlencoded'},
            data=f'loginID={urllib.parse.quote(EMAIL)}&password={PASSWORD}&APIKey={API_KEY}'
        ).content.decode())
    
        login_status = response.get('statusCode')
        if login_status == 200:
            print(f'\nTF1 login OK')
        else:
            sys.exit(f'\nTF1 login error: {login_status}')
    
        return json.loads(requests.post(
            TF1_TOKEN,
            json={
                'uid': response["UID"], 'signature': response["UIDSignature"],
                'timestamp': int(response["signatureTimestamp"]),
                'consent_ids': CONSENT_IDS
    
            }
        ).content.decode())["token"]
    
    
    BEARER_TOKEN = get_bearer_token()
    
    
    def get_video_data(source_url):
        matches = re.findall(r'"embedUrl":"([^"]+)"', requests.get(source_url).content.decode())
        match = [m for m in matches if "/player/" in m][0]
        media_id = re.findall(r'/player/([^/]+)', match)[0]
        response = requests.get(
            TF1_MEDIA.format(media_id=media_id),
            params={'pver': PLAYER_VERSION, 'context': 'context'},
            headers={'authorization': f'Bearer {BEARER_TOKEN}'}
        )
    
        response = response.content.decode()
        if "GEOBLOCKED" in response:
            print("VPN FAILURE")
            exit(0)
        manifest = json.loads(response)["delivery"]["url"]
        pssh_value = str(min(
            re.findall(r'<cenc:pssh>(.+?)</cenc:pssh>', requests.get(manifest).content.decode()), key=len
        ))
        return manifest, pssh_value
    
    
    def get_keys(pssh_value):
        if pssh_value 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)
        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 = get_video_data(source_url)
        keys = get_keys(pssh_value)
    
        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 = [
        "https://www.tf1.fr/tf1/esprits-criminels/videos/esprits-criminels-s08-e17-le-poids-des-mots-91857726.html",
        "https://www.tf1.fr/tf1/lycee-toulouse-lautrec/videos/lycee-toulouse-lautrec-s02-e01-la-peur-58024474.html",
        "https://www.tf1.fr/tmc/quotidien-avec-yann-barthes/videos/invites-fanny-ardant-et-thierry-klifa-rois-de-la-piste-et-de-larnaque-85679919.html",
        "https://www.tf1.fr/tmc/la-mode-by-loic-prigent/videos/5-minutes-de-mode-by-loic-prigent-du-8-mars-2024-73931950.html",
        "https://www.tf1.fr/tfx/chroniques-criminelles/videos/chroniques-criminelles-laffaire-emile-louis-les-disparues-de-lyonne-laffaire-grenier-le-pompier-aux-deux-visages-29670038.html",
        "https://www.tf1.fr/tfx/incroyables-mariages-gitans/videos/incroyables-mariages-gitans-paillettes-et-strass-pour-la-fete-de-leur-vie-20645319.html"
    ]
    for s in SOURCE_URLS:
        print(get_download_command(s))

    Thank you
    Great code.

    - I have path for the WVD file set
    - I have entered e-mail/password (when registering E-Mail is sent in HTML Only format ..
    - Amended program URL's

    Also:
    - Widevine L3 Guessor 2024 work on the tf1+ website as well.
    - Many keys can be found on CDRM GetWVKeys (Is there Devine plugin for TF1+ available ?)


    #PSSH/MPD/LicenseServer:
    -Browser F12 Dev tools:
    Code:
    /AcquireLicense|mpd/
    -Browser F12 Dev tools + "Esc" Console (with EME Logger/Tampermonkey installed):
    Code:
    MediaKeySession::generateRequest

    #Power Shell
    Code:
    N_m3u8DL-RE -M mp4 -mt `
    --save-name .FR.2024.720p.TF1P.WEB-DL.AAC.LC.2.0.H.264`
    --key `
    "MPD-URL"
    If I was in politics I make sure you drink plenty of beer
    and watch plenty of TV to keep you busy. | Data is the new oil.
    Quote Quote  
  18. Feels Good Man 2nHxWW6GkN1l916N3ayz8HQoi's Avatar
    Join Date
    Jan 2024
    Location
    Pepe Island
    Search Comp PM
    Originally Posted by pssh View Post
    (Is there Devine plugin for TF1+ available ?)
    I dunno. There's a thread for devine somewhere. You can ask/check there.
    --[----->+<]>.++++++++++++.---.--------.
    [*drm mass downloader: widefrog*]~~~~~~~~~~~[*how to make your own mass downloader: guide*]
    Quote Quote  
  19. Originally Posted by pssh View Post

    #Power Shell
    Code:
    N_m3u8DL-RE -M mp4 -mt `
    --save-name .FR.2024.720p.TF1P.WEB-DL.AAC.LC.2.0.H.264`
    --key `
    "MPD-URL"
    This one works well too but muxes in MP4, therefore just put it in mkv for an mkv video.

    By the way mt means concurrent downloads, meaning it will download all 3 files (video, audio and subs) all together instead of 1 after the other.

    Thank you everyone the script is great and I got it to work.

    Is there a way to make it download several links one after the other without having to select which quality? Like putting default the highest quality? Then after all downloads, it would mux each files one by one?
    Last edited by Matt27; 28th Apr 2024 at 15:18.
    Quote Quote  
  20. # This would give you all the options
    Code:
    N_m3u8DL-RE --help
    # My favorite one is:
    (Assuming FFMpeg is in your "PATH" for MP4 container).
    Code:
    N_m3u8DL-RE -M mp4 -mt -sv best -sa all -ss all \
    --save-name xxxx.FR+EN.yyyy.720p.TF1P.WEB-DL.AAC.LC.2.0.H.264 \
    "MPD-URL" \
    - MP4 Container
    - Concurrently download the selected audio, video and subtitles
    - Select "best" video stream
    - Select "all" audio streams
    - Select "all" subtitle streams

    But I see that TF1P offers on some programs:
    Code:
    INFO : Aud *CENC audio_eng=128000 | 128 Kbps | mp4a.40.2 | en | 2CH | 878 Segments | Main | ~01h57m02s
    INFO : Aud *CENC audio_fra=128000 | 128 Kbps | mp4a.40.2 | fr | 2CH | 878 Segments | Main | ~01h57m02s
    INFO : Aud *CENC audio_eng=64000 | 64 Kbps | mp4a.40.2 | en | 2CH | 878 Segments | Main | ~01h57m02s
    INFO : Aud *CENC audio_fra=64000 | 64 Kbps | mp4a.40.2 | fr | 2CH | 878 Segments | Main | ~01h57m02s
    INFO : Sub textstream_fra=1000 | fr | wvtt | 878 Segments | Subtitle | ~01h57m02s
    INFO : Sub textstream_fra_1=1000 | fr | wvtt | 878 Segments | ~01h57m02s
    INFO : Sub 4150766452 | fr | 1 Segment | Subtitle | ~01h57m02s
    INFO : Sub 3370552004 | fr | 1 Segment | ~01h57m02s
    # Select TWO best audio streams ONLY: (audio_eng=128000|audio_fra=128000)
    Code:
    -sa role="main":for:best
    #or
    Code:
    -sa lang="fr|en":for=best2
    # Select "best" "fra" audio ONLY:
    Code:
    -sa lang=fr:for=best

    DD|DD+|DTS etc. audio is available mainly on Premium streaming sites ...
    (There are some exceptions - CBS, Roku, CBC, CTV, SVTPlay)
    https://github.com/stabbedbybrick/freevine/blob/main/supportedsites.md


    # Notes:
    # End of line for (Inside the quotes):
    Code:
    Linux = "\"
    Code:
    Windows CMD "^"
    Code:
    Windows PowerShell "`"

    # You could explore the HELP options further:
    Code:
    N_m3u8DL-RE --morehelp select-video
    Code:
    N_m3u8DL-RE --morehelp select-audio
    Code:
    N_m3u8DL-RE --morehelp select-subtitle
    Code:
    N_m3u8DL-RE --morehelp mux-after-done
    Code:
    N_m3u8DL-RE --morehelp mux-import
    Code:
    N_m3u8DL-RE --morehelp custom-range
    Freevine uses "N_m3u8DL-RE" and there is a plenty of examples there as well
    "How-to" achieve ........


    Automating is all nice, providing the "Streaming" website does not change or keeps
    omitting languages/resolutions etc. so you want to really check what is happening
    rather than later find that all audio tracks are "audio descriptive".

    @stabbedbybrick done a great job, supporting "Freevine" ....... that automated
    download from some sites .......

    There could be a SERVICES created for "Devine" to use with TF1P
    (I am only making assumptions here based on fact that all the keys
    I looked for ware found on CDRM GetWVKeys) .....
    Last edited by pssh; 29th Apr 2024 at 04:14.
    If I was in politics I make sure you drink plenty of beer
    and watch plenty of TV to keep you busy. | Data is the new oil.
    Quote Quote  
  21. Originally Posted by pssh View Post
    # This would give you all the options
    Code:
    N_m3u8DL-RE --help
    # My favorite one is:
    (Assuming FFMpeg is in your "PATH" for MP4 container).
    Code:
    N_m3u8DL-RE -M mp4 -mt -sv best -sa all -ss all \
    --save-name xxxx.FR+EN.yyyy.720p.TF1P.WEB-DL.AAC.LC.2.0.H.264 \
    "MPD-URL" \
    - MP4 Container
    - Concurrently download the selected audio, video and subtitles
    - Select "best" video stream
    - Select "all" audio streams
    - Select "all" subtitle streams

    But I see that TF1P offers on some programs:
    Code:
    INFO : Aud *CENC audio_eng=128000 | 128 Kbps | mp4a.40.2 | en | 2CH | 878 Segments | Main | ~01h57m02s
    INFO : Aud *CENC audio_fra=128000 | 128 Kbps | mp4a.40.2 | fr | 2CH | 878 Segments | Main | ~01h57m02s
    INFO : Aud *CENC audio_eng=64000 | 64 Kbps | mp4a.40.2 | en | 2CH | 878 Segments | Main | ~01h57m02s
    INFO : Aud *CENC audio_fra=64000 | 64 Kbps | mp4a.40.2 | fr | 2CH | 878 Segments | Main | ~01h57m02s
    INFO : Sub textstream_fra=1000 | fr | wvtt | 878 Segments | Subtitle | ~01h57m02s
    INFO : Sub textstream_fra_1=1000 | fr | wvtt | 878 Segments | ~01h57m02s
    INFO : Sub 4150766452 | fr | 1 Segment | Subtitle | ~01h57m02s
    INFO : Sub 3370552004 | fr | 1 Segment | ~01h57m02s
    # Select TWO best audio streams ONLY: (audio_eng=128000|audio_fra=128000)
    Code:
    -sa role="main":for:best
    #or
    Code:
    -sa lang="fr|en":for=best2
    # Select "best" "fra" audio ONLY:
    Code:
    -sa lang=fr:for=best

    DD|DD+|DTS etc. audio is available mainly on Premium streaming sites ...
    (There are some exceptions - CBS, Roku, CBC, CTV, SVTPlay)
    https://github.com/stabbedbybrick/freevine/blob/main/supportedsites.md


    # Notes:
    # End of line for (Inside the quotes):
    Code:
    Linux = "\"
    Code:
    Windows CMD "^"
    Code:
    Windows PowerShell "`"

    # You could explore the HELP options further:
    Code:
    N_m3u8DL-RE --morehelp select-video
    Code:
    N_m3u8DL-RE --morehelp select-audio
    Code:
    N_m3u8DL-RE --morehelp select-subtitle
    Code:
    N_m3u8DL-RE --morehelp mux-after-done
    Code:
    N_m3u8DL-RE --morehelp mux-import
    Code:
    N_m3u8DL-RE --morehelp custom-range
    Freevine uses "N_m3u8DL-RE" and there is a plenty of examples there as well
    "How-to" achieve ........


    Automating is all nice, providing the "Streaming" website does not change or keeps
    omitting languages/resolutions etc. so you want to really check what is happening
    rather than later find that all audio tracks are "audio descriptive".

    @stabbedbybrick done a great job, supporting "Freevine" ....... that automated
    download from some sites .......

    There could be a SERVICES created for "Devine" to use with TF1P
    (I am only making assumptions here based on fact that all the keys
    I looked for ware found on CDRM GetWVKeys) .....
    Awesome! Thanks for the great tips.

    For the stuff i want from TF1+ there will be only French language so no worries.

    Freevine and Devine looks great to do all this. Looks like Freevine was just archived on the 28th March 2024.

    I'll look into Devine.
    Quote Quote  
  22. Originally Posted by 2nHxWW6GkN1l916N3ayz8HQoi View Post
    Here is a downloader for tf1. You need to write your email and password (no Google/Apple/Facebook, just the classic way). I have no idea if there are free videos available for someone without an account.

    Code:
    import json
    import re
    import sys
    import urllib.parse
    
    import requests
    from pywidevine.cdm import Cdm
    from pywidevine.device import Device
    from pywidevine.pssh import PSSH
    
    LICENSE_URL = "https://widevine-proxy-m.prod.p.tf1.fr/proxy"
    WVD_FILE = "./device_wvd_file.wvd"
    
    TF1_LOGIN = 'https://compte.tf1.fr/accounts.login'
    TF1_TOKEN = 'https://www.tf1.fr/token/gigya/web'
    TF1_MEDIA = 'https://mediainfo.tf1.fr/mediainfocombo/{media_id}'
    
    TF1_PLAYER = 'https://prod-player.tf1.fr'
    TF1_URL = 'https://www.tf1.fr'
    
    EMAIL = "YOUR_EMAIL"
    PASSWORD = "YOUR_PASSWORD"
    
    
    def format_version(version):
        major, minor, patch = map(int, version.split('.'))
        return str(major * 1000000 + minor * 1000 + patch)
    
    
    def get_tf1_info():
        response = requests.get(TF1_URL).content.decode()
        api_key = re.findall(r'"apiKey":"([^"]+)"', response)[0]
        consent_ids = re.findall(r'neededConsentIds":\[(.*?)]', response)[0].replace("\"", "").split(",")
    
        player_version = re.findall(rf'"playerEndpoint":"{TF1_PLAYER}/","version":"([^"]+)"', response)[0]
        return api_key, consent_ids, format_version(player_version)
    
    
    API_KEY, CONSENT_IDS, PLAYER_VERSION = get_tf1_info()
    
    
    def get_bearer_token():
        response = json.loads(requests.post(
            TF1_LOGIN, headers={'Content-Type': 'application/x-www-form-urlencoded'},
            data=f'loginID={urllib.parse.quote(EMAIL)}&password={PASSWORD}&APIKey={API_KEY}'
        ).content.decode())
    
        login_status = response.get('statusCode')
        if login_status == 200:
            print(f'\nTF1 login OK')
        else:
            sys.exit(f'\nTF1 login error: {login_status}')
    
        return json.loads(requests.post(
            TF1_TOKEN,
            json={
                'uid': response["UID"], 'signature': response["UIDSignature"],
                'timestamp': int(response["signatureTimestamp"]),
                'consent_ids': CONSENT_IDS
    
            }
        ).content.decode())["token"]
    
    
    BEARER_TOKEN = get_bearer_token()
    
    
    def get_video_data(source_url):
        matches = re.findall(r'"embedUrl":"([^"]+)"', requests.get(source_url).content.decode())
        match = [m for m in matches if "/player/" in m][0]
        media_id = re.findall(r'/player/([^/]+)', match)[0]
        response = requests.get(
            TF1_MEDIA.format(media_id=media_id),
            params={'pver': PLAYER_VERSION, 'context': 'context'},
            headers={'authorization': f'Bearer {BEARER_TOKEN}'}
        )
    
        response = response.content.decode()
        if "GEOBLOCKED" in response:
            print("VPN FAILURE")
            exit(0)
        manifest = json.loads(response)["delivery"]["url"]
        pssh_value = str(min(
            re.findall(r'<cenc:pssh>(.+?)</cenc:pssh>', requests.get(manifest).content.decode()), key=len
        ))
        return manifest, pssh_value
    
    
    def get_keys(pssh_value):
        if pssh_value 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)
        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 = get_video_data(source_url)
        keys = get_keys(pssh_value)
    
        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 = [
        "https://www.tf1.fr/tf1/esprits-criminels/videos/esprits-criminels-s08-e17-le-poids-des-mots-91857726.html",
        "https://www.tf1.fr/tf1/lycee-toulouse-lautrec/videos/lycee-toulouse-lautrec-s02-e01-la-peur-58024474.html",
        "https://www.tf1.fr/tmc/quotidien-avec-yann-barthes/videos/invites-fanny-ardant-et-thierry-klifa-rois-de-la-piste-et-de-larnaque-85679919.html",
        "https://www.tf1.fr/tmc/la-mode-by-loic-prigent/videos/5-minutes-de-mode-by-loic-prigent-du-8-mars-2024-73931950.html",
        "https://www.tf1.fr/tfx/chroniques-criminelles/videos/chroniques-criminelles-laffaire-emile-louis-les-disparues-de-lyonne-laffaire-grenier-le-pompier-aux-deux-visages-29670038.html",
        "https://www.tf1.fr/tfx/incroyables-mariages-gitans/videos/incroyables-mariages-gitans-paillettes-et-strass-pour-la-fete-de-leur-vie-20645319.html"
    ]
    for s in SOURCE_URLS:
        print(get_download_command(s))
    Output (I have shortened the manifest URLs because they will expire anyway since they have a token included in them):
    Code:
    N_m3u8DL-RE.exe https://vod-das.cdn-0.diff.tf1.fr/ey.../2/USP-0x0/15/04/14081504/ssm/662fc22a79004b5a5c3ab5b923253963047a3a4aaadcc0b700b8845caf8c10da.ism/14081504.mpd --key 04b4d96a711f510387637cc0ff487794:d74c6316a1a314d0e47a1e5e96b43493 --key 8adfbb94d8be5a719768a49116cc1d6a:3179e9f0a921be5ef5dc1325bb519bf9 --key 5d88b40a02dd5a4da857136bc93b4405:12cd44e9f3114df891d04e6f7138aca9 -M format=mkv
    N_m3u8DL-RE.exe https://vod-das.cdn-0.diff.tf1.fr/ey.../2/USP-0x0/25/52/14082552/ssm/ad98e512b44b31a01b874a4a11b94b75d6774a640a4cf5099427f95bfea18eb2.ism/14082552.mpd --key 8b02db604c37512e8f991b24949776ce:9b4f680fc264ca10ec79cdc4042a5559 --key b58b3e54e81d5cb0af74c78ae16a7776:d79d449cb4396e989e5ac2d55c9ede73 --key 4cc2b714030d56aaa3446752ebe7a5e6:e0dca060a2227df0f188574fcad7089a -M format=mkv
    N_m3u8DL-RE.exe https://vod-das.cdn-0.diff.tf1.fr/ey.../2/USP-0x0/42/95/14084295/ssm/aa9ab7d3530c8cf8c613a65657e332adc3e77979a7cd54b7cb62b6af28756de9.ism/14084295.mpd --key 14a80493a42f564a94de6f774dbb396b:0fbf6719952e5d8ce492f607eb178381 --key 93829e13b18c53cbb906d61b9f1d1140:66f12939e4c26f49ce645aa46f9959e2 --key 41349d3d2dde582bbb1cf8d03bcb4b1d:9800df594adc46a5db7189456e3f063b -M format=mkv
    N_m3u8DL-RE.exe https://vod-das.cdn-0.diff.tf1.fr/ey.../2/USP-0x0/19/91/14081991/ssm/207d14ed0d94635cc7a427c0e94e9e5e0b5b7ba9c5bbd7e790145eb38062d35c.ism/14081991.mpd --key 18685f497668574ba14971676f24ae09:ec3e4d42cd4dd4f2e07aea37a399a247 --key 8d9cd0ed61c4563985243ddfe45b2167:e37d4db29a055309343078077957958c --key b1d7f3716aba5f2f9fc7cfdba0e5e6c9:17c86742f496788e20050686c6e2f987 -M format=mkv
    N_m3u8DL-RE.exe https://vod-das.cdn-0.diff.tf1.fr/ey.../2/USP-0x0/28/76/14082876/ssm/2b2d91414e9720585451a381db75c83653ef65b4564c1ae31c4223f8fb228c01.ism/14082876.mpd --key e4abd497132b57cc9dc10166da27e319:baeb9de780ee40648c39dec7c686f772 --key 2a78be13fe595fc8851c5b1aa3227f19:937cc21c52c32aa2749c5b53c1127b6a --key ba4bd56eafb3593dbf019351356db347:2ae595dd961f3bd0e060b23b388a3b7b -M format=mkv
    N_m3u8DL-RE.exe https://vod-das.cdn-0.diff.tf1.fr/ey.../2/USP-0x0/32/48/14083248/ssm/dfd102d475b72dc3835d0d1fe023801597673f071704576a792740b648eb43d4.ism/14083248.mpd --key 2b79d01756925188a680cab4c89e0009:03951a2b75cb295065c79b2531e08cd9 --key 692f01b998b45338a673afe0b97ff43d:7c83ed49ad64b54f541d200a83fbf1ba --key ed1bb4550a535522bdd182cfd7239219:f38d177d85425036f403a808a9681584 -M format=mkv
    Have only checked for a few random videos. You need a French IP. If you use a VPN, sometimes the site may reject your IP, so refresh and get a new one. The script only works for free videos. If it crashes for a specific video, leave a message here.

    Also, I have no idea if there is a mpd link hidden for higher resolutions than 720p. If you know how to get one (if it even exists), I may modify the script.

    Edit: Only the script needs a French IP. Once you have the download command, you can turn it off to avoid wasting data on the actual N_m3u8 video download.

    Edit2: Login checking added by @ice. Thanks!

    Edit3: This script has been added and extended in the widefrog tool so I won't maintain it here. Any relevant updates will take place in the support thread.
    Hi 2nHxWW6GkN1l916N3ayz8HQoi 2nHxWW6GkN1l916N3ayz8HQoi,

    like a lot of people in this forum I guess, I want to learn as much as possible from your guide (awesome post!!), which I am trying out with Tf1.
    So as you suggest, I work backwards, from key generation up to URL input.
    It basically went fine until the step where
    Code:
    requests.post('https://compte.tf1.fr/accounts.login')
    needs to be called.

    For this, as usual in your guide: in Firefox right click on the request > Copy as POSIX,
    Image
    [Attachment 83928 - Click to enlarge]



    here is what I get in python:
    Image
    [Attachment 83929 - Click to enlarge]


    I can reduce the cookies to follwing minimum:
    Code:
    cookies = {
         #'TCPID': '124124637421602981861',
         #'_pprv': 'eyJjb25zZW50Ijp7IjAiOnsibW9kZSI6ImVzc2VudGlhbCJ9LCI3Ijp7Im1vZGUiOiJvcHQtaW4ifX0sInB1cnBvc2VzIjp7IjAiOiJBTSIsIjciOiJETCJ9LCJfdCI6Im1qemF2bW4xfG00YXZ5NWIxIn0%3D',
         #'tc_unique_id': 'V12024125637420.7448890676645805',
         #'ID_SESSION': 'V12024125637420.07466451719454725',
         #'tc_pp_test': 'c',
        'gmid': 'gmid.ver4.AtLtMBZDdA.FVsGQpPgIA8doSBKFaY0qr7aJgSYPR_Y5RB-zPHrw2picjogTeLil-BzOnWCWoTA.K-vzRJcevFO9VKFO2nivJdRPgVIJMD-awesryd27S3ZoNtr916wKg8ZxiA3JfRtO1WOlo2r697CfFTy0l8k-ig.sc3',
         #'ucid': 'FKOOe9Lmxi34fffjEPPPNw',
         #'hasGmid': 'ver4',
         #'gig_bootstrap_3_hWgJdARhz_7l1oOp3a8BDLoR9cuWZpUaKG4aqF7gum9_iK3uTZ2VlDBl8ANf8FVk': 'compte_ver4',
    }
    because if I comment out the entry 'gmid', the server returns an error:
    Code:
    {
      "callId": "b71b02179fb3407e9311c4c3dc2b80b1",
      "errorCode": 400006,
      "errorDetails": "Your request is blocked because of security issues.",
      "errorMessage": "Invalid parameter value",
      "apiVersion": 2,
      "statusCode": 400,
      "statusReason": "Bad Request",
      "time": "2024-12-05T10:54:03.316Z",
      "errorFlags": "missingKey"
    }
    So as you see, I have a new unknown variable 'gmid' and I am stuck here.

    I look in your code and obviously, you don’t have this unknown variable.
    You just pass the following requests
    Code:
    response = json.loads(requests.post(
            TF1_LOGIN, headers={'Content-Type': 'application/x-www-form-urlencoded'},
            data=f'loginID={urllib.parse.quote(EMAIL)}&password={PASSWORD}&APIKey={API_KEY}'
    which works.

    Now my question is:
    How did you come to this request? Obviously not over curlconverter, right?
    I would really appreciate a feedback, this drives me crazy ^^
    Merci d’avance
    Quote Quote  
  23. Feels Good Man 2nHxWW6GkN1l916N3ayz8HQoi's Avatar
    Join Date
    Jan 2024
    Location
    Pepe Island
    Search Comp PM
    Hello. Thanks for the nice words. There's an updated tf1 script in the widefrog tool. You can look in its source code and compare it with what you got since the script from this specific page is outdated and not meant to be used.

    I got the same request call for login, only I removed completely the cookies dictionary. First remove it fully and see if it works, if not, trim it. In my case gmid wasn't needed at all
    --[----->+<]>.++++++++++++.---.--------.
    [*drm mass downloader: widefrog*]~~~~~~~~~~~[*how to make your own mass downloader: guide*]
    Quote Quote  
  24. Originally Posted by 2nHxWW6GkN1l916N3ayz8HQoi View Post
    Hello. Thanks for the nice words. There's an updated tf1 script in the widefrog tool. You can look in its source code and compare it with what you got since the script from this specific page is outdated and not meant to be used.

    I got the same request call for login, only I removed completely the cookies dictionary. First remove it fully and see if it works, if not, trim it. In my case gmid wasn't needed at all
    Thx for your quick feedback =)
    The code in widefrog is the same as the old implementation:
    Code:
            response = json.loads(requests.post(
                tf1_fr.LOGIN_URL,
                data={
                    "loginID": credentials["EMAIL"],
                    "password": credentials["PASSWORD"],
                    "APIKey": tf1_fr.API_KEY
                }
            ).content.decode())
    But still I continue thinking the gmid has an influence;
    with gmid:
    Image
    [Attachment 83935 - Click to enlarge]


    without gmid:
    Image
    [Attachment 83936 - Click to enlarge]


    Possible that they changed sthg?
    Quote Quote  
  25. Feels Good Man 2nHxWW6GkN1l916N3ayz8HQoi's Avatar
    Join Date
    Jan 2024
    Location
    Pepe Island
    Search Comp PM
    You gotta trim the data dictionary as well. You can remove a lot of things, and you'll reach the 3 minimum necessary pieces of information like I did. If you remove "sdk": "js_latest", from data you can remove completely the cookies dictionary.
    --[----->+<]>.++++++++++++.---.--------.
    [*drm mass downloader: widefrog*]~~~~~~~~~~~[*how to make your own mass downloader: guide*]
    Quote Quote  
  26. Originally Posted by 2nHxWW6GkN1l916N3ayz8HQoi View Post
    You gotta trim the data dictionary as well. You can remove a lot of things, and you'll reach the 3 minimum necessary pieces of information like I did. If you remove "sdk": "js_latest", from data you can remove completely the cookies dictionary.
    ok it was worth having a try : I could get rid of 'gmid'
    But somehow the minimum in the data dictionary seems to contain 4 values for me: i cannot delete 'targetEnv': 'jssdk', otherwise the token is not generate any more and i get only dump:
    Image
    [Attachment 83950 - Click to enlarge]


    If i leave it in the dict:
    Image
    [Attachment 83951 - Click to enlarge]


    I would be very interested to know if it is the same for you =)
    Quote Quote  
  27. Feels Good Man 2nHxWW6GkN1l916N3ayz8HQoi's Avatar
    Join Date
    Jan 2024
    Location
    Pepe Island
    Search Comp PM
    If it works with 4 for what you want, then use 4. For me three is enough. Look how I generate it. 2 requests, login endpoint + token endpoint
    --[----->+<]>.++++++++++++.---.--------.
    [*drm mass downloader: widefrog*]~~~~~~~~~~~[*how to make your own mass downloader: guide*]
    Quote Quote  
  28. Originally Posted by 2nHxWW6GkN1l916N3ayz8HQoi View Post
    If it works with 4 for what you want, then use 4. For me three is enough. Look how I generate it. 2 requests, login endpoint + token endpoint
    Still fighting with TF1... sorry for the 2 following more questions

    1) I see in your code in Widefrog that in the function
    Code:
    def get_bearer_token()
    you call the following URL to get the token:
    Code:
    TOKEN_URL = 'https://www.tf1.fr/token/gigya/web'
    In my case, even though i apply strictly your guide (new In-Private Window, even deleted all the data before), I read in my HAR-File that the bearer is obtained from the following URL:
    Code:
            "request": {
              "bodySize": 261,
              "method": "POST",
              "url": "https://www.tf1.fr/token/refresh",
    which suggests to me that i am not receiving a new one but using an old one... (also in the cookie of my request, a bearer is given as an input).
    How did you "force" your setup to be blank?

    2) In the same matter and although i open an In-Private blank page, i have never seen any request to
    Code:
    BASE_URL = 'https://www.tf1.fr'
    which should deliver me "API_KEY, CONSENT_IDS, PLAYER_VERSION", still according to your code:
    Code:
        def get_tf1_info():
            response = requests.get(tf1_fr.BASE_URL).content.decode()
            api_key = re.findall(r'"apiKey":"([^"]+)"', response)[0]
            consent_ids = re.findall(r'neededConsentIds":\[(.*?)]', response)[0].replace("\"", "").split(",")
    
            player_version = re.findall(
                rf'"playerEndpoint":"{tf1_fr.PLAYER_URL}/","version":"([^"]+)"', response
            )[0]
    Any idea why my browser does not request it?

    Thanks again for the time you spend helping others
    Quote Quote  
  29. Feels Good Man 2nHxWW6GkN1l916N3ayz8HQoi's Avatar
    Join Date
    Jan 2024
    Location
    Pepe Island
    Search Comp PM
    1) I played video in incognito with persist logs enabled and I logged in. You'll get account login -> web token -> refresh token. I skipped the refresh part since there's no use in refreshing the token if you get a new one anyway. It's just a guide after all, you can take some liberties.

    2) There are some variables that could be considered global for a site and aren't tied to any specific video. So there's no point in obtaining them each time you download a video. I prefer creating them once with a fixed neutral URL, like the site itself.

    If you have further questions, ask them on the guide post. Let's keep this post on topic for tf1 only. Not scripting.
    --[----->+<]>.++++++++++++.---.--------.
    [*drm mass downloader: widefrog*]~~~~~~~~~~~[*how to make your own mass downloader: guide*]
    Quote Quote  
  30. Ambassador Of Atmosphere
    Join Date
    Nov 2024
    Location
    Brooklyn, NY
    Search Comp PM
    Please help me grab this https://www.tf1.fr/tmc/stromae-multitude-le-film-le-concert-evenement/videos/stromae-m...-00503567.html in its best quality while removing the DRM. I'll be using N_m3u8DL-RE for this. Thank you.
    We all bleed blue from the inside....
    Quote Quote  



Similar Threads

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