Hi,
no error on my side (Windows 10)
Code:python widefrog.py https://www.tf1.fr/tfx/la-villa-des-coeurs-brises/videos/la-villa-saison-08-episode-01-14350616.html?playlist=la-saison-08-de-la-villa-09810740 [INFO] Current app version: 3.2.0 [INFO] Running on: windows/64bit/python 3.10.11 [INFO] Starting the service initialization stage. [INFO] Current progress: [1/1] [INFO] Starting the collections extraction stage. [INFO] Current progress: [1/1] [INFO] Starting the media data extraction stage. [INFO] Current progress: [1/1] [INFO] Finished generating the download commands. [INFO] Saved the generated download commands to app_files\cmds.txt [INFO] If you used a VPN, turn it off to avoid wasting data for the downloading stage. Also you may edit the app_files\cmds.txt file if necessary. Type yes when ready: yes [INFO] Starting the downloading stage.
Support our site by donate $5 directly to us Thanks!!!
Try StreamFab Downloader and download streaming video from Netflix, Amazon!
Try StreamFab Downloader and download streaming video from Netflix, Amazon!
+ Reply to Thread
Results 481 to 504 of 504
-
-
Hi guys!
First off, thanks for making this tool @2nHxWW6GkN1l916N3ayz8HQoi! It seems very comprehensive and capable of getting keys for practically any MPD, but I can't actually test that since I'm getting:Code:[USER_ERROR] Failed to download: https://cdn.bitmovin.com/content/assets/art-of-motion_drm/mpds/11331.mpd. Reason: No service available for cdn.bitmovin.com. Solution: Implement it.
Apologies if this has been posted about before, but I really don't want to spend hours reading all 11 pages of this massive thread if it can be avoided, especially when most of you are just talking about bugs and already-supported services, judging from what I have seen (I've read the first three pages and scanned the rest briefly).
So just a basic rundown of what's needed to implement a custom service would be awesome, and very much appreciated! Many thanks in advance
PS: I'm a pretty proficient Python dev as is, so no need to assume the usual n00b level of programming-based intelligence that is shown across this forum lolLast edited by CoopMeisterFresh; 24th Aug 2025 at 02:37.
-
-
-
-
It looks like TF1 ramped up their encryption since the beginning of the month, every media downloaded is now corrupted visually with no sound.
-
[Attachment 88490 - Click to enlarge]
cmds.txt
N_m3u8DL-RE "https://vod-das.cdn-0.diff.tf1.fr/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjaXAiOiI5M i4xMjguMTEzLjkwIiwiY21jZCI6IiIsImV4cCI6MTc1NjY5MDM 4NiwiZ2lkIjoiYzU2NjJkNGQ5ZmQzNGYxZGIwNjFjYmE1NGMyN TIzYjkiLCJpYXQiOjE3NTY2NzU5ODYsImlzcyI6ImRlbGl2ZXJ 5IiwibWF4YiI6NTc0OTAwMCwic3RlbSI6Ii8yL1VTUC0weDAvM DcvOTYvMTQzNTA3OTYvc3NtLzg0YzQzOGQzOWRmOWFjZTliNDQ zNWNhOGY1Zjc5YzA0Njg2NGViZTJkZTc3YzQ5NGY0OTBkOTMxZ WMwM2Q5ZTMuaXNtLzE0MzUwNzk2Lm1wZCIsInN1YiI6ImM1NjY yZDRkOWZkMzRmMWRiMDYxY2JhNTRjMjUyM2I5In0.VyeYHIR7W 7qisH3rqhQVTeI_vssg_tZyoDOhGRTTml0/2/USP-0x0/07/96/14350796/ssm/84c438d39df9ace9b4435ca8f5f79c046864ebe2de77c494f4 90d931ec03d9e3.ism/14350796.mpd" --tmp-dir "media\tmp\Snhyyur" --save-dir "media\tf1_fr\Avant-premiere_-_La_Villa_-_Saison_10_Episode_18_du_2_septembre_2025" --save-name "content" -sv res="1024x576":for=best -sa best -da id=audio_div -ss all -mt --check-segments-count false --no-log -M format=mkv & "terminal\mkvmerge.bat" "media\tf1_fr\Avant-premiere_-_La_Villa_-_Saison_10_Episode_18_du_2_septembre_2025.mkv" "media\tf1_fr\Avant-premiere_-_La_Villa_-_Saison_10_Episode_18_du_2_septembre_2025" true & rd /s /q "media\tmp\Snhyyur"
no detect keys -
It's a User agent problem. Replace your tf1_fr.py by this. In your config file use this user agent for instance.
"USER_AGENT": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:142.0) Gecko/20100101 Firefox/142.0",Last edited by pleg33; 2nd Sep 2025 at 22:24.
-
sa ne fonctonne pas ce que ta donnez sa met sa
mon tf1.py
import json
import re
from os.path import join
import requests
from utils.constants.macros import ERR_MSG, USER_ERROR, APP_ERROR
from utils.main_service import main_service
from utils.structs import BaseElement, CustomException, BaseService
from utils.tools.args import check_range
from utils.tools.common import get_valid_filename
class tf1_fr(BaseService):
DEMO_URLS = [
"https://www.tf1.fr/tf1/gladiators/videos/extract",
"https://www.tf1.fr/tf1/les-freres-scott/videos",
"https://www.tf1.fr/tf1/les-bracelets-rouges/videos/extract",
"https://www.tf1.fr/tmc/les-mysteres-de-l-amour/videos",
"https://www.tf1.fr/tf1/gladiators/videos",
"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/tf1/double-zero/videos/double-zero-70592950.html",
]
LOGIN_URL = 'https://compte.tf1.fr/accounts.login'
TOKEN_URL = 'https://www.tf1.fr/token/gigya/web'
MEDIAINFO_URL = 'https://mediainfo.tf1.fr/mediainfocombo/{media_id}'
PLAYER_URL = 'https://prod-player.tf1.fr'
BASE_URL = 'https://www.tf1.fr'
LICENSE_URL = "https://widevine-proxy-m.prod.p.tf1.fr/proxy"
EMAIL = "YOUR_EMAIL"
PASSWORD = "YOUR_PASSWORD"
BEARER_TOKEN = None
API_KEY, CONSENT_IDS, PLAYER_VERSION = None, None, None
USER_AGENT = builtins.CONFIG["USER_AGENT"]
@staticmethod
def test_service():
main_service.run_service(tf1_fr)
@staticmethod
def credentials_needed():
return {
"EMAIL": tf1_fr.EMAIL,
"PASSWORD": tf1_fr.PASSWORD
}
@staticmethod
def format_version(version):
major, minor, patch = map(int, version.split('.'))
return str(major * 1000000 + minor * 1000 + patch)
@staticmethod
def get_tf1_info():
response = requests.get(
tf1_fr.BASE_URL,
headers={'User-Agent': tf1_fr.USER_AGENT}).content.decode().replace('\\"' , '"')
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]
return api_key, consent_ids, tf1_fr.format_version(player_version)
@staticmethod
def get_bearer_token():
class_name = tf1_fr.__name__.replace("_", ".")
credentials = builtins.CONFIG["SERVICE_CREDENTIALS"][class_name]
try:
assert type(credentials["EMAIL"]) is str
assert type(credentials["PASSWORD"]) is str
except:
return None
response = json.loads(requests.post(
tf1_fr.LOGIN_URL,
data={
"loginID": credentials["EMAIL"],
"password": credentials["PASSWORD"],
"APIKey": tf1_fr.API_KEY
}, headers={'User-Agent': tf1_fr.USER_AGENT}
).content.decode())
status_code = response.get('statusCode', None)
if status_code != 200:
if status_code == 403:
return None
raise CustomException(ERR_MSG.format(
type=f'{APP_ERROR}',
url=f"from the {tf1_fr.__name__} service",
reason=f"Unknown error encountered: {str(response)}",
solution="Debug the service"
))
return json.loads(requests.post(
tf1_fr.TOKEN_URL,
json={
'uid': response["UID"], 'signature': response["UIDSignature"],
'timestamp': int(response["signatureTimestamp"]),
'consent_ids': tf1_fr.CONSENT_IDS
}
,
headers={'User-Agent': tf1_fr.USER_AGENT}).content.decode())["token"]
@staticmethod
def initialize_service():
if tf1_fr.API_KEY is None or tf1_fr.CONSENT_IDS is None or tf1_fr.PLAYER_VERSION is None:
tf1_fr.API_KEY, tf1_fr.CONSENT_IDS, tf1_fr.PLAYER_VERSION = tf1_fr.get_tf1_info()
if tf1_fr.BEARER_TOKEN is None:
tf1_fr.BEARER_TOKEN = tf1_fr.get_bearer_token()
if tf1_fr.BEARER_TOKEN is None:
return None
return tf1_fr
@staticmethod
def get_keys(challenge, additional):
licence = requests.post(
tf1_fr.LICENSE_URL, data=challenge, headers={'User-Agent': tf1_fr.USER_AGENT})
licence.raise_for_status()
return licence.content
@staticmethod
def get_video_data(source_element):
matches = re.findall(
r'"embedUrl":"([^"]+)"', requests.get(
source_element.url, headers={'User-Agent': tf1_fr.USER_AGENT}).content.decode()
)
match = [m for m in matches if "/player/" in m]
if len(match) == 0:
raise CustomException(ERR_MSG.format(
type=f'{USER_ERROR}',
url=source_element.url,
reason="Content isn't available anymore",
solution="Do not attempt to download it"
))
media_id = re.findall(r'/player/([^/]+)', match[0])[0]
response = requests.get(
tf1_fr.MEDIAINFO_URL.format(media_id=media_id),
params={'pver': tf1_fr.PLAYER_VERSION, 'context': 'context'},
headers={'authorization': f'Bearer {tf1_fr.BEARER_TOKEN}','User-Agent': tf1_fr.USER_AGENT}
)
response = response.content.decode()
if "geoblocked" in response.lower():
raise CustomException(ERR_MSG.format(
type=f'{USER_ERROR}',
url=source_element.url,
reason="Need French IP to access content",
solution="Use a VPN"
))
response = json.loads(response)
if (response.get("delivery", {}).get("code", None) == 4034 or
"permission_denied" in response.get("media", {}).get("error_code", "").lower()):
raise CustomException(ERR_MSG.format(
type=f'{USER_ERROR}',
url=source_element.url,
reason="Premium content can't be downloaded",
solution="Do not attempt to download it"
))
if source_element.element is None:
source_element.element = get_valid_filename(response.get("media", {}).get("title", media_id))
if source_element.collection is None:
source_element.collection = join(
str(builtins.CONFIG["DOWNLOAD_COMMANDS"]["OUTPUT_MEDIA_PATH"]),
tf1_fr.__name__
)
manifest = response["delivery"]["url"]
try:
pssh_value = str(min(re.findall(
r'<cencssh>(.+?)</cencssh>',
requests.get(
manifest, headers={'User-Agent': tf1_fr.USER_AGENT}).content.decode()
), key=len))
except:
pssh_value = None
if builtins.CONFIG.get("BASIC", False) is True:
manifest = re.sub(r'/eyJ[^/]*/', '/<TOKEN>/', manifest)
return manifest, pssh_value, {}
@staticmethod
def get_episode_index(label):
if label is None or len(label) == 0:
return None
label = label.lower()
label = re.sub(r'\s+', ' ', label)
label = label.replace(' ', "_")
for s in ["e", "pisode_", "pisodes_", "part_", "emission_"]:
try:
return int(re.findall(fr'{s}(\d+)', label)[0])
except:
if s == "emission_" and "emission_" in label:
return 1
return None
@staticmethod
def get_collection_elements(collection_url):
if "/playlist" in collection_url:
return None
if "/videos/" in collection_url and ".html" in collection_url:
return [BaseElement(url=collection_url)]
if "/videos" in collection_url:
collection = []
try:
section = re.search(r"/videos/([^/?]*)", collection_url).group(1)
except:
section = None
response = requests.get(
collection_url, headers={'User-Agent': tf1_fr.USER_AGENT}).content.decode()
collection_name = re.search(r"([^/]+)/videos", collection_url).group(1)
collection_name = get_valid_filename(collection_name)
re_href = 'href="'
re_href += re.search(fr"{tf1_fr.BASE_URL}(.*?)/videos", collection_url).group(1)
re_href += "/videos/"
if section is not None and len(section) > 0:
collection_name = collection_name + "_" + section
collection_name = get_valid_filename(collection_name)
matches = re.findall(fr'({re_href}.*?\.html)"[^>]*><div[^>]*>([^<]+)</div>', response)
if len(matches) == 0:
matches = re.findall(fr'({re_href}.*?\.html)"[^>]*>([^<]+)<', response)
matches = [(
tf1_fr.BASE_URL + m[0].split('"')[1], m[1]
) for m in matches]
matches = [(*value, index + 1) for index, value in enumerate(matches)]
for content_url, content_name, content_index in matches:
check = check_range(False, None, content_index)
if check is True:
continue
elif check is False:
return collection
collection.append(BaseElement(
url=content_url,
collection=join(join(
str(builtins.CONFIG["DOWNLOAD_COMMANDS"]["OUTPUT_MEDIA_PATH"]),
tf1_fr.__name__
), collection_name),
element=f'{content_index}'
f'_'
f'{get_valid_filename(content_name)}'
))
return collection
re_href += "saison-"
matches = re.findall(fr'{re_href}\d+"', response)
matches = [m.split('"')[1] for m in matches]
matches = [(
f'{tf1_fr.BASE_URL}{m}', int(m.split("/videos/saison-")[1])
) for m in matches]
matches = sorted(matches, key=lambda m: m[1])
re_href = re_href.replace("saison-", "")
for season_url, season_index in matches:
check = check_range(True, season_index, None)
if check is True:
continue
elif check is False:
return collection
page = 0
while True:
page += 1
if page > 1:
response = requests.get(
f'{season_url}/{page}', allow_redirects=False, headers={'User-Agent': tf1_fr.USER_AGENT})
try:
assert 300 <= response.status_code < 400
assert len(response.headers["Location"]) > 0
assert response.headers["Location"] in season_url
break
except:
pass
else:
response = requests.get(
f'{season_url}/{page}', headers={'User-Agent': tf1_fr.USER_AGENT})
response = response.content.decode()
matches = re.findall(fr'({re_href}.*?\.html)"[^>]*><div[^>]*>([^<]+)</div>', response)
if len(matches) == 0:
matches = re.findall(fr'({re_href}.*?\.html)"[^>]*>([^<]+)<', response)
matches = [(
tf1_fr.BASE_URL + m[0].split('"')[1],
m[1],
tf1_fr.get_episode_index(m[1])
) for m in matches]
matches = [m for m in matches if m[2] is not None]
for episode_url, episode_name, episode_index in matches:
check = check_range(False, season_index, episode_index)
if check in [True, False]:
continue
collection.append(BaseElement(
url=episode_url,
collection=join(
join(
str(builtins.CONFIG["DOWNLOAD_COMMANDS"]["OUTPUT_MEDIA_PATH"]),
tf1_fr.__name__
),
join(collection_name, f'Season_{season_index}')
),
element=f"Episode_{episode_index}"
f"_"
f"{get_valid_filename(episode_name)}"
))
if len(matches) == 0:
break
return collection
return None -
j'ai une question possible d'ajouter le service crunchyroll je ses pas comment ajouter des services
-
Hello, has anyone been able to use the tf1 service successfully?
I even tried this and updated the tf1 script but it did not work.
Code:[INFO] Current app version: 3.2.0 [INFO] Running on: windows/64bit/python 3.13.7 [INFO] Starting the service initialization stage. [APP_ERROR] Failed to download: https://www.tf1.fr/tmc/quotidien-avec-yann-barthes/videos/quotidien-premiere-partie-du-5-septembre-2025-02055522.html. Reason: Failed to initialize the tf1_fr service. Solution: Debug the service. [INFO] Current progress: [1/1] [INFO] Starting the collections extraction stage. [INFO] Starting the media data extraction stage. [INFO] Finished generating the download commands. [WARNING] Saved the failed URLs to app_files\cmds_failed.txt [WARNING] No download commands have been generated.
-
For Windows users download & replace widefrog.exe file
PLEASE BE CAREFUL WHEN DOWNLOADING FILES FROM NEW USERS!
Virustotal
https://www.virustotal.com/gui/file/90e1afac91ba60819435744cbc2cb1611f6d46fa722c502387...407c?nocache=1Last edited by Baldrick; 9th Sep 2025 at 11:51. Reason: add warning
-
-
OP hasn’t been active since March 26, 2025.
I hope everything is okay with him. -
-
Hi,
Since modifying the tf1_fr.py file, single downloads work again, but bulk downloads don't work via this type of URL:
https://www.tf1.fr/tf1/tout-pour-la-lumiere/videos/saison-1
[INFO] Current app version: 3.1.0
[INFO] Running on: windows/64bit/python 3.13.6
[INFO] Starting the service initialization stage.
[INFO] Current progress: [1/1]
[INFO] Starting the collections extraction stage.
[INFO] Current progress: [1/1]
[INFO] Starting the media data extraction stage.
[INFO] Finished generating the download commands.
[WARNING] No download commands have been generated. -
[INFO] Current app version: 3.2.0
[INFO] Running on: windows/64bit/python 3.11.0
[INFO] Starting the service initialization stage.
[INFO] Current progress: [1/1]
[INFO] Starting the collections extraction stage.
[INFO] Current progress: [1/1]
[INFO] Starting the media data extraction stage.
[USER_ERROR]/[APP_ERROR] Failed to download: https://www.tf1.fr/tf1/plus-belle-la-vie/videos/avant-premiere-plus-belle-la-vie-encor...-29975900.html. Reason: Something went wrong with the license call. Solution: Make sure you can play the content and/or change your VPN IP. Additionally, wait a few minutes and/or replace your CDM with a fresh one. If the issue persists, then debug the tf1_fr service.
[INFO] Finished generating the download commands.
[WARNING] Saved the failed URLs to app_files\cmds_failed.txt
[WARNING] No download commands have been generated.
Appuyez sur une touche pour continuer...
Terminer le programme de commandes (O/N)*?
TF1 on blacklisté mon CDM je fait comment -
I have the same problem. It's not your CDM that's blacklisted...
A few weeks ago, we already had a user-agent problem...
There have certainly been technical changes to the TF1 website.
The "tf1_fr.py" file in the "services" folder is therefore no longer valid without an update.
I hope someone will be able to do this, as I don't have the skills to create a new version of this file that works with the website updates. -
tf1 a changé leur syteme de login maintenant plus besoin ce login c tout dans un seul truc
👉 TF1 n’utilise plus __NEXT_DATA__.
Ils chargent les infos vidéo via un autre mécanisme (React + Apollo/GraphQL). Les pages contiennent des gros blocs comme window.__APOLLO_STATE__ ou des script inline avec des clés contentId, videoId, Product::TF1_xxx.
C’est pour ça que mon code échoue : il cherche encore le vieux bloc.
✅ Ce qu’il faut faire maintenant
Scanner tous les <script> à la recherche d’un JSON contenant "contentId" ou "Product::".
Extraire ce champ → ça nous donne l’ID vidéo.
Utiliser cet ID avec l’API https://player.tf1.fr/{contentId}?device=browser (ça, ça n’a pas changé). -
I managed to fix the latest tf1_fr service issue the following way:
1. Modify the get_video_data(source_element) method:
Replace the last line:
Code:return manifest, pssh_value, {}
Code:additional = {} if "drms" in response["delivery"]: drm = response["delivery"]["drms"] for k in drm: if "widevine" in k["name"].lower(): additional["license_url"] = k["url"] break return manifest, pssh_value, additional
Replace the first line:
Code:licence = requests.post(tf1_fr.LICENSE_URL, data=challenge, headers={"User-Agent": tf1_fr.USER_AGENT})
Code:licence = requests.post(additional["license_url"], data=challenge, headers={"User-Agent": tf1_fr.USER_AGENT})
Last edited by gibolin; 25th Sep 2025 at 03:39. Reason: tf1_fr.zip file attached
-
Thank you!
It's OK again for a video with a URL that points to the video itself.
However, we're still having an issue with bulk downloads for this type of URL:
https://www.tf1.fr/tf1/tout-pour-la-lumiere/videos/saison-1 -
@Sardine1508, the two issues are unrelated.
The collection download is handled in the get_collection_elements method, and the issue you pointed out came from the regex not matching the episode URLs anymore.
Parsing HTML with regex is tricky, and it would probably be more robust to rewrite this method using BeautifulSoup...
For now, I made a quick and dirty fix that should solve the problem, see attachment.
Similar Threads
-
Batch IMDB Image Downloader
By Jay123210599 in forum ComputerReplies: 3Last Post: 11th Jan 2024, 12:02 -
Help downloading DRM protected content
By edenshapira in forum Video Streaming DownloadingReplies: 0Last Post: 28th Oct 2023, 14:59 -
Downloading DRM protected content from mewatch.sg
By notred in forum Video Streaming DownloadingReplies: 4Last Post: 25th Nov 2022, 04:34 -
Weird Behaviour Of DRM Protected Content
By portalie in forum Video Streaming DownloadingReplies: 1Last Post: 6th Feb 2022, 15:10