From b375e1d85c8f32c1499bb6ea25f0f200fca25c33 Mon Sep 17 00:00:00 2001 From: bashonly <88596187+bashonly@users.noreply.github.com> Date: Tue, 18 Aug 2026 05:17:12 -0500 Subject: [PATCH] [ie/tiktok] Fix extractor (#17452) Closes #17403, Closes #17437 Authored by: bashonly --- yt_dlp/extractor/common.py | 13 +++++++++++++ yt_dlp/extractor/dailymotion.py | 14 -------------- yt_dlp/extractor/tiktok.py | 8 +++++--- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/yt_dlp/extractor/common.py b/yt_dlp/extractor/common.py index 732f8b140e..5278c07bde 100644 --- a/yt_dlp/extractor/common.py +++ b/yt_dlp/extractor/common.py @@ -3960,6 +3960,19 @@ class InfoExtractor: headers['Ytdl-request-proxy'] = geo_verification_proxy return headers + @staticmethod + def _generate_blockbuster_headers(*, min_headers=2, max_headers=8): + """Randomize our HTTP header fingerprint in an attempt to bust HTTP Error 403 blockage""" + + def random_letters(minimum, maximum): + # Omit vowels so we don't generate valid header names like 'authorization', etc + return ''.join(random.choices('bcdfghjklmnpqrstvwxz', k=random.randint(minimum, maximum))) + + return { + random_letters(8, 24): random_letters(16, 32) + for _ in range(random.randint(min_headers, max_headers)) + } + @staticmethod def _generic_id(url): return urllib.parse.unquote(os.path.splitext(url.rstrip('/').split('/')[-1])[0]) diff --git a/yt_dlp/extractor/dailymotion.py b/yt_dlp/extractor/dailymotion.py index e8c8ab8b28..de56e9e276 100644 --- a/yt_dlp/extractor/dailymotion.py +++ b/yt_dlp/extractor/dailymotion.py @@ -1,6 +1,5 @@ import functools import json -import random import re import urllib.parse @@ -364,19 +363,6 @@ class DailymotionIE(DailymotionBaseInfoExtractor): continue yield update_url(player_url, query=query_string) - @staticmethod - def _generate_blockbuster_headers(): - """Randomize our HTTP header fingerprint to bust the HTTP Error 403 block""" - - def random_letters(minimum, maximum): - # Omit vowels so we don't generate valid header names like 'authorization', etc - return ''.join(random.choices('bcdfghjklmnpqrstvwxz', k=random.randint(minimum, maximum))) - - return { - random_letters(8, 24): random_letters(16, 32) - for _ in range(random.randint(2, 8)) - } - def _extract_dailymotion_m3u8_formats_and_subtitles(self, media_url, video_id, live=False): """See https://github.com/yt-dlp/yt-dlp/issues/15526""" diff --git a/yt_dlp/extractor/tiktok.py b/yt_dlp/extractor/tiktok.py index 0660e6e693..f02515c494 100644 --- a/yt_dlp/extractor/tiktok.py +++ b/yt_dlp/extractor/tiktok.py @@ -274,9 +274,10 @@ class TikTokBaseIE(InfoExtractor): def _extract_web_data_and_status(self, url, video_id, fatal=True): video_data, status = {}, -1 + headers = self._generate_blockbuster_headers() def get_webpage(note='Downloading webpage'): - res = self._download_webpage_handle(url, video_id, note, fatal=fatal, impersonate=True) + res = self._download_webpage_handle(url, video_id, note, fatal=fatal, headers=headers) if res is False: return False @@ -1155,7 +1156,7 @@ class TikTokUserIE(TikTokBaseIE): webpage = self._download_webpage( self._UPLOADER_URL_FORMAT % user_name, user_name, 'Downloading user webpage', 'Unable to download user webpage', - fatal=False, impersonate=True) or '' + fatal=False, headers=self._generate_blockbuster_headers()) or '' detail = traverse_obj( self._get_universal_data(webpage, user_name), ('webapp.user-detail', {dict})) or {} video_count = traverse_obj(detail, ('userInfo', ('stats', 'statsV2'), 'videoCount', {int}, any)) @@ -1613,7 +1614,8 @@ class TikTokLiveIE(TikTokBaseIE): uploader, room_id = self._match_valid_url(url).group('uploader', 'id') if not room_id: webpage = self._download_webpage( - format_field(uploader, None, self._UPLOADER_URL_FORMAT), uploader, impersonate=True) + format_field(uploader, None, self._UPLOADER_URL_FORMAT), uploader, + headers=self._generate_blockbuster_headers()) room_id = traverse_obj( self._get_universal_data(webpage, uploader), ('webapp.user-detail', 'userInfo', 'user', 'roomId', {str}))