yt-dlp 2026.1.19.233146.dev0__py3-none-any.whl → 2026.1.25.233128.dev0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- yt_dlp/extractor/dailymotion.py +17 -1
- yt_dlp/extractor/tiktok.py +83 -24
- yt_dlp/version.py +3 -3
- {yt_dlp-2026.1.19.233146.dev0.dist-info → yt_dlp-2026.1.25.233128.dev0.dist-info}/METADATA +1 -1
- {yt_dlp-2026.1.19.233146.dev0.dist-info → yt_dlp-2026.1.25.233128.dev0.dist-info}/RECORD +13 -13
- {yt_dlp-2026.1.19.233146.dev0.data → yt_dlp-2026.1.25.233128.dev0.data}/data/share/bash-completion/completions/yt-dlp +0 -0
- {yt_dlp-2026.1.19.233146.dev0.data → yt_dlp-2026.1.25.233128.dev0.data}/data/share/doc/yt_dlp/README.txt +0 -0
- {yt_dlp-2026.1.19.233146.dev0.data → yt_dlp-2026.1.25.233128.dev0.data}/data/share/fish/vendor_completions.d/yt-dlp.fish +0 -0
- {yt_dlp-2026.1.19.233146.dev0.data → yt_dlp-2026.1.25.233128.dev0.data}/data/share/man/man1/yt-dlp.1 +0 -0
- {yt_dlp-2026.1.19.233146.dev0.data → yt_dlp-2026.1.25.233128.dev0.data}/data/share/zsh/site-functions/_yt-dlp +0 -0
- {yt_dlp-2026.1.19.233146.dev0.dist-info → yt_dlp-2026.1.25.233128.dev0.dist-info}/WHEEL +0 -0
- {yt_dlp-2026.1.19.233146.dev0.dist-info → yt_dlp-2026.1.25.233128.dev0.dist-info}/entry_points.txt +0 -0
- {yt_dlp-2026.1.19.233146.dev0.dist-info → yt_dlp-2026.1.25.233128.dev0.dist-info}/licenses/LICENSE +0 -0
yt_dlp/extractor/dailymotion.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import functools
|
|
2
2
|
import json
|
|
3
|
+
import random
|
|
3
4
|
import re
|
|
4
5
|
import urllib.parse
|
|
5
6
|
|
|
@@ -363,6 +364,20 @@ class DailymotionIE(DailymotionBaseInfoExtractor):
|
|
|
363
364
|
continue
|
|
364
365
|
yield update_url(player_url, query=query_string)
|
|
365
366
|
|
|
367
|
+
@staticmethod
|
|
368
|
+
def _generate_blockbuster_headers():
|
|
369
|
+
# Randomize our HTTP header fingerprint to bust the HTTP Error 403 block
|
|
370
|
+
# See https://github.com/yt-dlp/yt-dlp/issues/15526
|
|
371
|
+
|
|
372
|
+
def random_letters(minimum, maximum):
|
|
373
|
+
# Omit vowels so we don't generate valid header names like 'authorization', etc
|
|
374
|
+
return ''.join(random.choices('bcdfghjklmnpqrstvwxz', k=random.randint(minimum, maximum)))
|
|
375
|
+
|
|
376
|
+
return {
|
|
377
|
+
random_letters(8, 24): random_letters(16, 32)
|
|
378
|
+
for _ in range(random.randint(2, 8))
|
|
379
|
+
}
|
|
380
|
+
|
|
366
381
|
def _real_extract(self, url):
|
|
367
382
|
url, smuggled_data = unsmuggle_url(url)
|
|
368
383
|
video_id, is_playlist, playlist_id = self._match_valid_url(url).group('id', 'is_playlist', 'playlist_id')
|
|
@@ -425,7 +440,8 @@ class DailymotionIE(DailymotionBaseInfoExtractor):
|
|
|
425
440
|
continue
|
|
426
441
|
if media_type == 'application/x-mpegURL':
|
|
427
442
|
fmt, subs = self._extract_m3u8_formats_and_subtitles(
|
|
428
|
-
media_url, video_id, 'mp4', live=is_live, m3u8_id='hls',
|
|
443
|
+
media_url, video_id, 'mp4', live=is_live, m3u8_id='hls',
|
|
444
|
+
fatal=False, headers=self._generate_blockbuster_headers())
|
|
429
445
|
formats.extend(fmt)
|
|
430
446
|
self._merge_subtitles(subs, target=subtitles)
|
|
431
447
|
else:
|
yt_dlp/extractor/tiktok.py
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import base64
|
|
1
2
|
import functools
|
|
3
|
+
import hashlib
|
|
2
4
|
import itertools
|
|
3
5
|
import json
|
|
4
6
|
import random
|
|
@@ -15,6 +17,7 @@ from ..utils import (
|
|
|
15
17
|
UnsupportedError,
|
|
16
18
|
UserNotLive,
|
|
17
19
|
determine_ext,
|
|
20
|
+
extract_attributes,
|
|
18
21
|
filter_dict,
|
|
19
22
|
format_field,
|
|
20
23
|
int_or_none,
|
|
@@ -25,13 +28,13 @@ from ..utils import (
|
|
|
25
28
|
qualities,
|
|
26
29
|
srt_subtitles_timecode,
|
|
27
30
|
str_or_none,
|
|
28
|
-
traverse_obj,
|
|
29
31
|
truncate_string,
|
|
30
32
|
try_call,
|
|
31
33
|
try_get,
|
|
32
34
|
url_or_none,
|
|
33
35
|
urlencode_postdata,
|
|
34
36
|
)
|
|
37
|
+
from ..utils.traversal import find_element, require, traverse_obj
|
|
35
38
|
|
|
36
39
|
|
|
37
40
|
class TikTokBaseIE(InfoExtractor):
|
|
@@ -217,38 +220,94 @@ class TikTokBaseIE(InfoExtractor):
|
|
|
217
220
|
raise ExtractorError('Unable to extract aweme detail info', video_id=aweme_id)
|
|
218
221
|
return self._parse_aweme_video_app(aweme_detail)
|
|
219
222
|
|
|
223
|
+
def _solve_challenge_and_set_cookie(self, webpage):
|
|
224
|
+
challenge_data = traverse_obj(webpage, (
|
|
225
|
+
{find_element(id='cs', html=True)}, {extract_attributes}, 'class',
|
|
226
|
+
filter, {lambda x: f'{x}==='}, {base64.b64decode}, {json.loads}))
|
|
227
|
+
|
|
228
|
+
if not challenge_data:
|
|
229
|
+
if 'Please wait...' in webpage:
|
|
230
|
+
raise ExtractorError('Unable to extract challenge data')
|
|
231
|
+
raise ExtractorError('Unexpected response from webpage request')
|
|
232
|
+
|
|
233
|
+
self.to_screen('Solving JS challenge using native Python implementation')
|
|
234
|
+
|
|
235
|
+
expected_digest = traverse_obj(challenge_data, (
|
|
236
|
+
'v', 'c', {str}, {base64.b64decode},
|
|
237
|
+
{require('challenge expected digest')}))
|
|
238
|
+
|
|
239
|
+
base_hash = traverse_obj(challenge_data, (
|
|
240
|
+
'v', 'a', {str}, {base64.b64decode},
|
|
241
|
+
{hashlib.sha256}, {require('challenge base hash')}))
|
|
242
|
+
|
|
243
|
+
for i in range(1_000_001):
|
|
244
|
+
number = str(i).encode()
|
|
245
|
+
test_hash = base_hash.copy()
|
|
246
|
+
test_hash.update(number)
|
|
247
|
+
if test_hash.digest() == expected_digest:
|
|
248
|
+
challenge_data['d'] = base64.b64encode(number).decode()
|
|
249
|
+
break
|
|
250
|
+
else:
|
|
251
|
+
raise ExtractorError('Unable to solve JS challenge')
|
|
252
|
+
|
|
253
|
+
cookie_value = base64.b64encode(
|
|
254
|
+
json.dumps(challenge_data, separators=(',', ':')).encode()).decode()
|
|
255
|
+
|
|
256
|
+
# At time of writing, the cookie name was _wafchallengeid
|
|
257
|
+
cookie_name = traverse_obj(webpage, (
|
|
258
|
+
{find_element(id='wci', html=True)}, {extract_attributes},
|
|
259
|
+
'class', {require('challenge cookie name')}))
|
|
260
|
+
|
|
261
|
+
# Actual JS sets Max-Age=1, but we need to adjust for --sleep-requests and Python slowness
|
|
262
|
+
expire_time = int(time.time()) + (self.get_param('sleep_interval_requests') or 0) + 2
|
|
263
|
+
self._set_cookie('.tiktok.com', cookie_name, cookie_value, expire_time=expire_time)
|
|
264
|
+
|
|
220
265
|
def _extract_web_data_and_status(self, url, video_id, fatal=True):
|
|
221
266
|
video_data, status = {}, -1
|
|
222
267
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
268
|
+
def get_webpage(note='Downloading webpage'):
|
|
269
|
+
res = self._download_webpage_handle(url, video_id, note, fatal=fatal, impersonate=True)
|
|
270
|
+
if res is False:
|
|
271
|
+
return False
|
|
226
272
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
273
|
+
webpage, urlh = res
|
|
274
|
+
if urllib.parse.urlparse(urlh.url).path == '/login':
|
|
275
|
+
message = 'TikTok is requiring login for access to this content'
|
|
276
|
+
if fatal:
|
|
277
|
+
self.raise_login_required(message)
|
|
278
|
+
self.report_warning(f'{message}. {self._login_hint()}', video_id=video_id)
|
|
279
|
+
return False
|
|
280
|
+
|
|
281
|
+
return webpage
|
|
282
|
+
|
|
283
|
+
webpage = get_webpage()
|
|
284
|
+
if webpage is False:
|
|
233
285
|
return video_data, status
|
|
234
286
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
287
|
+
universal_data = self._get_universal_data(webpage, video_id)
|
|
288
|
+
if not universal_data:
|
|
289
|
+
try:
|
|
290
|
+
self._solve_challenge_and_set_cookie(webpage)
|
|
291
|
+
except ExtractorError as e:
|
|
292
|
+
if fatal:
|
|
293
|
+
raise
|
|
294
|
+
self.report_warning(e.orig_msg, video_id=video_id)
|
|
295
|
+
return video_data, status
|
|
239
296
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
297
|
+
webpage = get_webpage(note='Downloading webpage with challenge cookie')
|
|
298
|
+
if webpage is False:
|
|
299
|
+
return video_data, status
|
|
300
|
+
universal_data = self._get_universal_data(webpage, video_id)
|
|
244
301
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
302
|
+
if not universal_data:
|
|
303
|
+
message = 'Unable to extract universal data for rehydration'
|
|
304
|
+
if fatal:
|
|
305
|
+
raise ExtractorError(message)
|
|
306
|
+
self.report_warning(message, video_id=video_id)
|
|
307
|
+
return video_data, status
|
|
249
308
|
|
|
250
|
-
|
|
251
|
-
|
|
309
|
+
status = traverse_obj(universal_data, ('webapp.video-detail', 'statusCode', {int})) or 0
|
|
310
|
+
video_data = traverse_obj(universal_data, ('webapp.video-detail', 'itemInfo', 'itemStruct', {dict}))
|
|
252
311
|
|
|
253
312
|
if not traverse_obj(video_data, ('video', {dict})) and traverse_obj(video_data, ('isContentClassified', {bool})):
|
|
254
313
|
message = 'This post may not be comfortable for some audiences. Log in for access'
|
yt_dlp/version.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Autogenerated by devscripts/update-version.py
|
|
2
2
|
|
|
3
|
-
__version__ = '2026.01.
|
|
3
|
+
__version__ = '2026.01.25.233128'
|
|
4
4
|
|
|
5
|
-
RELEASE_GIT_HEAD = '
|
|
5
|
+
RELEASE_GIT_HEAD = 'e3f0d8b731b40176bcc632bf92cfe5149402b202'
|
|
6
6
|
|
|
7
7
|
VARIANT = 'pip'
|
|
8
8
|
|
|
@@ -12,4 +12,4 @@ CHANNEL = 'nightly'
|
|
|
12
12
|
|
|
13
13
|
ORIGIN = 'yt-dlp/yt-dlp-nightly-builds'
|
|
14
14
|
|
|
15
|
-
_pkg_version = '2026.01.
|
|
15
|
+
_pkg_version = '2026.01.25.233128dev'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: yt-dlp
|
|
3
|
-
Version: 2026.1.
|
|
3
|
+
Version: 2026.1.25.233128.dev0
|
|
4
4
|
Summary: A feature-rich command-line audio/video downloader
|
|
5
5
|
Project-URL: Documentation, https://github.com/yt-dlp/yt-dlp#readme
|
|
6
6
|
Project-URL: Repository, https://github.com/yt-dlp/yt-dlp
|
|
@@ -11,7 +11,7 @@ yt_dlp/options.py,sha256=-PQ5DyRSIU9YBSG2oh0RTHC-eg4EewNt4zewTZTuERM,100632
|
|
|
11
11
|
yt_dlp/plugins.py,sha256=EGmR0ydaahNspGrgszTNX4-YjHe93WOOhcw1gf6PZSs,8215
|
|
12
12
|
yt_dlp/socks.py,sha256=oAuAfWM6jxI8A5hHDLEKq2U2-k9NyMB_z6nrKzNE9fg,8936
|
|
13
13
|
yt_dlp/update.py,sha256=sY7gNFBQorzs7sEjRrqL5QOsTBNmGGa_FnpTtbxY1vA,25280
|
|
14
|
-
yt_dlp/version.py,sha256=
|
|
14
|
+
yt_dlp/version.py,sha256=jlXeK9_Clnj7MGlfVA4LmG9H4w-KIt4cbBnKAWpNWc4,360
|
|
15
15
|
yt_dlp/webvtt.py,sha256=ONkXaaNCZcX8pQhJn3iwIKyaQ34BtVDrMEdG6wRNZwM,11451
|
|
16
16
|
yt_dlp/__pyinstaller/__init__.py,sha256=-c4Zo8nQGKAm8wc_LDscxMtK7zr_YhZwRnC9CMruUBE,72
|
|
17
17
|
yt_dlp/__pyinstaller/hook-yt_dlp.py,sha256=5Rd0zV2pDskjY1KtT0wsjxv4hStx67sLCjUexsFvFus,1339
|
|
@@ -228,7 +228,7 @@ yt_dlp/extractor/curiositystream.py,sha256=bzhwbA9jBy28tWn8m_y08mHbTpDJ6xz0S4XDG
|
|
|
228
228
|
yt_dlp/extractor/cybrary.py,sha256=oSlXqw2Qzhu3Wy7utzEn-GreCD5h7KJ_KDcYysMTmao,6288
|
|
229
229
|
yt_dlp/extractor/dacast.py,sha256=iO7RiFA5hxduDqicYdzKhrphUZWV45K5yolk057M0ko,7498
|
|
230
230
|
yt_dlp/extractor/dailymail.py,sha256=AyLI8Shgpn0N3Nz4A1Yk_r4soa_sffB74gp3KnxhuII,3577
|
|
231
|
-
yt_dlp/extractor/dailymotion.py,sha256=
|
|
231
|
+
yt_dlp/extractor/dailymotion.py,sha256=lBHPkTNEfYumkhZj5ped3QZhybhUeTEkhNHfEdCkFJ8,24693
|
|
232
232
|
yt_dlp/extractor/dailywire.py,sha256=5dCO2J4ThJ-G4pthAaVs_DFlDm2N0Bgu8DB8Mbthvos,4857
|
|
233
233
|
yt_dlp/extractor/damtomo.py,sha256=wlRzM4PYbME7EfCU03pcw-PRHTeKAu-eIjcw2hvxMwY,5464
|
|
234
234
|
yt_dlp/extractor/dangalplay.py,sha256=LmBxCY7hnd1UJKCCDvYGOzv7ZCSDaKGrqQfGHqPraxM,9345
|
|
@@ -890,7 +890,7 @@ yt_dlp/extractor/thisoldhouse.py,sha256=YLnhWuuA0xMbY766tVDVKi-iaRwwmgOs8sdD-ydB
|
|
|
890
890
|
yt_dlp/extractor/thisvid.py,sha256=dxN-I56UieWt92iS2VSXK7IckobVNQ6BYPgvOJ9vWJI,8628
|
|
891
891
|
yt_dlp/extractor/threeqsdn.py,sha256=zNHqF2ELqulYpoUCp1YYPlf0tyPS2krsrEUkS0Cw8YQ,6219
|
|
892
892
|
yt_dlp/extractor/threespeak.py,sha256=agG3Ue0h19dAknJHwrK9a3RBQB4aja-5cx1crkOCIUc,4025
|
|
893
|
-
yt_dlp/extractor/tiktok.py,sha256=
|
|
893
|
+
yt_dlp/extractor/tiktok.py,sha256=oG320FyjXWBTtLf61aA3t6enQVFY4m7pBr5HPIrTWxo,76135
|
|
894
894
|
yt_dlp/extractor/tmz.py,sha256=Nu3xReAc7dKyZcxTGwXYjOpDjeMdfLKSIgJMpjSobNI,9626
|
|
895
895
|
yt_dlp/extractor/tnaflix.py,sha256=PAWzd7LtF97MF-aHSdUOpWpAmLUqBlAhP0FH0T3tpk0,13561
|
|
896
896
|
yt_dlp/extractor/toggle.py,sha256=unbnd9IcJJOKcpoYSySISehBRbYeqHhr1x-fHmeLba4,7892
|
|
@@ -1124,13 +1124,13 @@ yt_dlp/utils/progress.py,sha256=t9kVvJ0oWuEqRzo9fdFbIhHUBtO_8mg348QwZ1faqLo,3261
|
|
|
1124
1124
|
yt_dlp/utils/traversal.py,sha256=64E3RcZ56iSX50RI_HbKdDNftkETMLBaEPX791_b7yQ,18265
|
|
1125
1125
|
yt_dlp/utils/jslib/__init__.py,sha256=CbdJiRA7Eh5PnjF2V4lDTcg0J0XjBMaaq0H4pCfq9Tk,87
|
|
1126
1126
|
yt_dlp/utils/jslib/devalue.py,sha256=UtcQ1IEzt6HWBjB9Z_6rJMb3y2pFrbHXDNu1rrxXF1c,5583
|
|
1127
|
-
yt_dlp-2026.1.
|
|
1128
|
-
yt_dlp-2026.1.
|
|
1129
|
-
yt_dlp-2026.1.
|
|
1130
|
-
yt_dlp-2026.1.
|
|
1131
|
-
yt_dlp-2026.1.
|
|
1132
|
-
yt_dlp-2026.1.
|
|
1133
|
-
yt_dlp-2026.1.
|
|
1134
|
-
yt_dlp-2026.1.
|
|
1135
|
-
yt_dlp-2026.1.
|
|
1136
|
-
yt_dlp-2026.1.
|
|
1127
|
+
yt_dlp-2026.1.25.233128.dev0.data/data/share/bash-completion/completions/yt-dlp,sha256=KKMwJ7JkH-B2rXIR9n4wAHTqn5waHyxzPtVmsMoYkDI,6009
|
|
1128
|
+
yt_dlp-2026.1.25.233128.dev0.data/data/share/doc/yt_dlp/README.txt,sha256=ZT_UKIUjR5lFVtB7ctKc9FBVWkmnM-cFMNDpeVSwFfg,166159
|
|
1129
|
+
yt_dlp-2026.1.25.233128.dev0.data/data/share/fish/vendor_completions.d/yt-dlp.fish,sha256=L0JADRod-4ew2pvmYGiDUuXFgu1Ac8msk-ackiLYHCo,51632
|
|
1130
|
+
yt_dlp-2026.1.25.233128.dev0.data/data/share/man/man1/yt-dlp.1,sha256=blBneUC8ngI9Qjm2w4xDUIxeRQOLw6tnwNVs02fJFNM,160804
|
|
1131
|
+
yt_dlp-2026.1.25.233128.dev0.data/data/share/zsh/site-functions/_yt-dlp,sha256=VsiR8Dn2RqbVSZHAqQyDtTfrc3BIBdzwgrcaJqux8kQ,6005
|
|
1132
|
+
yt_dlp-2026.1.25.233128.dev0.dist-info/METADATA,sha256=BypzGNXFzud6-T5bgDjQDheRF-O1s3zqYflP2XBkX7Q,181945
|
|
1133
|
+
yt_dlp-2026.1.25.233128.dev0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
1134
|
+
yt_dlp-2026.1.25.233128.dev0.dist-info/entry_points.txt,sha256=vWfetvzYgZIwDfMW6BjCe0Cy4pmTZEXRNzxAkfYlRJA,103
|
|
1135
|
+
yt_dlp-2026.1.25.233128.dev0.dist-info/licenses/LICENSE,sha256=fhLl30uuEsshWBuhV87SDhmGoFCN0Q0Oikq5pM-U6Fw,1211
|
|
1136
|
+
yt_dlp-2026.1.25.233128.dev0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{yt_dlp-2026.1.19.233146.dev0.data → yt_dlp-2026.1.25.233128.dev0.data}/data/share/man/man1/yt-dlp.1
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{yt_dlp-2026.1.19.233146.dev0.dist-info → yt_dlp-2026.1.25.233128.dev0.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{yt_dlp-2026.1.19.233146.dev0.dist-info → yt_dlp-2026.1.25.233128.dev0.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|