yt-dlp 2025.12.29.233040.dev0__py3-none-any.whl → 2025.12.30.233018.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/_extractors.py +0 -4
- yt_dlp/extractor/facebook.py +0 -64
- yt_dlp/extractor/iqiyi.py +0 -184
- yt_dlp/extractor/lazy_extractors.py +2 -33
- yt_dlp/extractor/nebula.py +9 -1
- yt_dlp/extractor/twitter.py +37 -194
- yt_dlp/extractor/youtube/_video.py +14 -19
- yt_dlp/utils/_utils.py +1 -1
- yt_dlp/version.py +3 -3
- {yt_dlp-2025.12.29.233040.dev0.dist-info → yt_dlp-2025.12.30.233018.dev0.dist-info}/METADATA +1 -1
- {yt_dlp-2025.12.29.233040.dev0.dist-info → yt_dlp-2025.12.30.233018.dev0.dist-info}/RECORD +19 -20
- yt_dlp/extractor/scte.py +0 -137
- {yt_dlp-2025.12.29.233040.dev0.data → yt_dlp-2025.12.30.233018.dev0.data}/data/share/bash-completion/completions/yt-dlp +0 -0
- {yt_dlp-2025.12.29.233040.dev0.data → yt_dlp-2025.12.30.233018.dev0.data}/data/share/doc/yt_dlp/README.txt +0 -0
- {yt_dlp-2025.12.29.233040.dev0.data → yt_dlp-2025.12.30.233018.dev0.data}/data/share/fish/vendor_completions.d/yt-dlp.fish +0 -0
- {yt_dlp-2025.12.29.233040.dev0.data → yt_dlp-2025.12.30.233018.dev0.data}/data/share/man/man1/yt-dlp.1 +0 -0
- {yt_dlp-2025.12.29.233040.dev0.data → yt_dlp-2025.12.30.233018.dev0.data}/data/share/zsh/site-functions/_yt-dlp +0 -0
- {yt_dlp-2025.12.29.233040.dev0.dist-info → yt_dlp-2025.12.30.233018.dev0.dist-info}/WHEEL +0 -0
- {yt_dlp-2025.12.29.233040.dev0.dist-info → yt_dlp-2025.12.30.233018.dev0.dist-info}/entry_points.txt +0 -0
- {yt_dlp-2025.12.29.233040.dev0.dist-info → yt_dlp-2025.12.30.233018.dev0.dist-info}/licenses/LICENSE +0 -0
yt_dlp/extractor/_extractors.py
CHANGED
|
@@ -1825,10 +1825,6 @@ from .scrippsnetworks import (
|
|
|
1825
1825
|
ScrippsNetworksWatchIE,
|
|
1826
1826
|
)
|
|
1827
1827
|
from .scrolller import ScrolllerIE
|
|
1828
|
-
from .scte import (
|
|
1829
|
-
SCTEIE,
|
|
1830
|
-
SCTECourseIE,
|
|
1831
|
-
)
|
|
1832
1828
|
from .sejmpl import SejmIE
|
|
1833
1829
|
from .sen import SenIE
|
|
1834
1830
|
from .senalcolombia import SenalColombiaLiveIE
|
yt_dlp/extractor/facebook.py
CHANGED
|
@@ -4,8 +4,6 @@ import urllib.parse
|
|
|
4
4
|
|
|
5
5
|
from .common import InfoExtractor
|
|
6
6
|
from ..compat import compat_etree_fromstring
|
|
7
|
-
from ..networking import Request
|
|
8
|
-
from ..networking.exceptions import network_exceptions
|
|
9
7
|
from ..utils import (
|
|
10
8
|
ExtractorError,
|
|
11
9
|
clean_html,
|
|
@@ -64,9 +62,6 @@ class FacebookIE(InfoExtractor):
|
|
|
64
62
|
class=(?P<q1>[\'"])[^\'"]*\bfb-(?:video|post)\b[^\'"]*(?P=q1)[^>]+
|
|
65
63
|
data-href=(?P<q2>[\'"])(?P<url>(?:https?:)?//(?:www\.)?facebook.com/.+?)(?P=q2)''',
|
|
66
64
|
]
|
|
67
|
-
_LOGIN_URL = 'https://www.facebook.com/login.php?next=http%3A%2F%2Ffacebook.com%2Fhome.php&login_attempt=1'
|
|
68
|
-
_CHECKPOINT_URL = 'https://www.facebook.com/checkpoint/?next=http%3A%2F%2Ffacebook.com%2Fhome.php&_fb_noscript=1'
|
|
69
|
-
_NETRC_MACHINE = 'facebook'
|
|
70
65
|
IE_NAME = 'facebook'
|
|
71
66
|
|
|
72
67
|
_VIDEO_PAGE_TEMPLATE = 'https://www.facebook.com/video/video.php?v=%s'
|
|
@@ -469,65 +464,6 @@ class FacebookIE(InfoExtractor):
|
|
|
469
464
|
'graphURI': '/api/graphql/',
|
|
470
465
|
}
|
|
471
466
|
|
|
472
|
-
def _perform_login(self, username, password):
|
|
473
|
-
login_page_req = Request(self._LOGIN_URL)
|
|
474
|
-
self._set_cookie('facebook.com', 'locale', 'en_US')
|
|
475
|
-
login_page = self._download_webpage(login_page_req, None,
|
|
476
|
-
note='Downloading login page',
|
|
477
|
-
errnote='Unable to download login page')
|
|
478
|
-
lsd = self._search_regex(
|
|
479
|
-
r'<input type="hidden" name="lsd" value="([^"]*)"',
|
|
480
|
-
login_page, 'lsd')
|
|
481
|
-
lgnrnd = self._search_regex(r'name="lgnrnd" value="([^"]*?)"', login_page, 'lgnrnd')
|
|
482
|
-
|
|
483
|
-
login_form = {
|
|
484
|
-
'email': username,
|
|
485
|
-
'pass': password,
|
|
486
|
-
'lsd': lsd,
|
|
487
|
-
'lgnrnd': lgnrnd,
|
|
488
|
-
'next': 'http://facebook.com/home.php',
|
|
489
|
-
'default_persistent': '0',
|
|
490
|
-
'legacy_return': '1',
|
|
491
|
-
'timezone': '-60',
|
|
492
|
-
'trynum': '1',
|
|
493
|
-
}
|
|
494
|
-
request = Request(self._LOGIN_URL, urlencode_postdata(login_form))
|
|
495
|
-
request.headers['Content-Type'] = 'application/x-www-form-urlencoded'
|
|
496
|
-
try:
|
|
497
|
-
login_results = self._download_webpage(request, None,
|
|
498
|
-
note='Logging in', errnote='unable to fetch login page')
|
|
499
|
-
if re.search(r'<form(.*)name="login"(.*)</form>', login_results) is not None:
|
|
500
|
-
error = self._html_search_regex(
|
|
501
|
-
r'(?s)<div[^>]+class=(["\']).*?login_error_box.*?\1[^>]*><div[^>]*>.*?</div><div[^>]*>(?P<error>.+?)</div>',
|
|
502
|
-
login_results, 'login error', default=None, group='error')
|
|
503
|
-
if error:
|
|
504
|
-
raise ExtractorError(f'Unable to login: {error}', expected=True)
|
|
505
|
-
self.report_warning('unable to log in: bad username/password, or exceeded login rate limit (~3/min). Check credentials or wait.')
|
|
506
|
-
return
|
|
507
|
-
|
|
508
|
-
fb_dtsg = self._search_regex(
|
|
509
|
-
r'name="fb_dtsg" value="(.+?)"', login_results, 'fb_dtsg', default=None)
|
|
510
|
-
h = self._search_regex(
|
|
511
|
-
r'name="h"\s+(?:\w+="[^"]+"\s+)*?value="([^"]+)"', login_results, 'h', default=None)
|
|
512
|
-
|
|
513
|
-
if not fb_dtsg or not h:
|
|
514
|
-
return
|
|
515
|
-
|
|
516
|
-
check_form = {
|
|
517
|
-
'fb_dtsg': fb_dtsg,
|
|
518
|
-
'h': h,
|
|
519
|
-
'name_action_selected': 'dont_save',
|
|
520
|
-
}
|
|
521
|
-
check_req = Request(self._CHECKPOINT_URL, urlencode_postdata(check_form))
|
|
522
|
-
check_req.headers['Content-Type'] = 'application/x-www-form-urlencoded'
|
|
523
|
-
check_response = self._download_webpage(check_req, None,
|
|
524
|
-
note='Confirming login')
|
|
525
|
-
if re.search(r'id="checkpointSubmitButton"', check_response) is not None:
|
|
526
|
-
self.report_warning('Unable to confirm login, you have to login in your browser and authorize the login.')
|
|
527
|
-
except network_exceptions as err:
|
|
528
|
-
self.report_warning(f'unable to log in: {err}')
|
|
529
|
-
return
|
|
530
|
-
|
|
531
467
|
def _extract_from_url(self, url, video_id):
|
|
532
468
|
webpage = self._download_webpage(
|
|
533
469
|
url.replace('://m.facebook.com/', '://www.facebook.com/'), video_id)
|
yt_dlp/extractor/iqiyi.py
CHANGED
|
@@ -9,14 +9,12 @@ from .openload import PhantomJSwrapper
|
|
|
9
9
|
from ..utils import (
|
|
10
10
|
ExtractorError,
|
|
11
11
|
clean_html,
|
|
12
|
-
decode_packed_codes,
|
|
13
12
|
float_or_none,
|
|
14
13
|
format_field,
|
|
15
14
|
get_element_by_attribute,
|
|
16
15
|
get_element_by_id,
|
|
17
16
|
int_or_none,
|
|
18
17
|
js_to_json,
|
|
19
|
-
ohdave_rsa_encrypt,
|
|
20
18
|
parse_age_limit,
|
|
21
19
|
parse_duration,
|
|
22
20
|
parse_iso8601,
|
|
@@ -33,143 +31,12 @@ def md5_text(text):
|
|
|
33
31
|
return hashlib.md5(text.encode()).hexdigest()
|
|
34
32
|
|
|
35
33
|
|
|
36
|
-
class IqiyiSDK:
|
|
37
|
-
def __init__(self, target, ip, timestamp):
|
|
38
|
-
self.target = target
|
|
39
|
-
self.ip = ip
|
|
40
|
-
self.timestamp = timestamp
|
|
41
|
-
|
|
42
|
-
@staticmethod
|
|
43
|
-
def split_sum(data):
|
|
44
|
-
return str(sum(int(p, 16) for p in data))
|
|
45
|
-
|
|
46
|
-
@staticmethod
|
|
47
|
-
def digit_sum(num):
|
|
48
|
-
if isinstance(num, int):
|
|
49
|
-
num = str(num)
|
|
50
|
-
return str(sum(map(int, num)))
|
|
51
|
-
|
|
52
|
-
def even_odd(self):
|
|
53
|
-
even = self.digit_sum(str(self.timestamp)[::2])
|
|
54
|
-
odd = self.digit_sum(str(self.timestamp)[1::2])
|
|
55
|
-
return even, odd
|
|
56
|
-
|
|
57
|
-
def preprocess(self, chunksize):
|
|
58
|
-
self.target = md5_text(self.target)
|
|
59
|
-
chunks = []
|
|
60
|
-
for i in range(32 // chunksize):
|
|
61
|
-
chunks.append(self.target[chunksize * i:chunksize * (i + 1)])
|
|
62
|
-
if 32 % chunksize:
|
|
63
|
-
chunks.append(self.target[32 - 32 % chunksize:])
|
|
64
|
-
return chunks, list(map(int, self.ip.split('.')))
|
|
65
|
-
|
|
66
|
-
def mod(self, modulus):
|
|
67
|
-
chunks, ip = self.preprocess(32)
|
|
68
|
-
self.target = chunks[0] + ''.join(str(p % modulus) for p in ip)
|
|
69
|
-
|
|
70
|
-
def split(self, chunksize):
|
|
71
|
-
modulus_map = {
|
|
72
|
-
4: 256,
|
|
73
|
-
5: 10,
|
|
74
|
-
8: 100,
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
chunks, ip = self.preprocess(chunksize)
|
|
78
|
-
ret = ''
|
|
79
|
-
for i in range(len(chunks)):
|
|
80
|
-
ip_part = str(ip[i] % modulus_map[chunksize]) if i < 4 else ''
|
|
81
|
-
if chunksize == 8:
|
|
82
|
-
ret += ip_part + chunks[i]
|
|
83
|
-
else:
|
|
84
|
-
ret += chunks[i] + ip_part
|
|
85
|
-
self.target = ret
|
|
86
|
-
|
|
87
|
-
def handle_input16(self):
|
|
88
|
-
self.target = md5_text(self.target)
|
|
89
|
-
self.target = self.split_sum(self.target[:16]) + self.target + self.split_sum(self.target[16:])
|
|
90
|
-
|
|
91
|
-
def handle_input8(self):
|
|
92
|
-
self.target = md5_text(self.target)
|
|
93
|
-
ret = ''
|
|
94
|
-
for i in range(4):
|
|
95
|
-
part = self.target[8 * i:8 * (i + 1)]
|
|
96
|
-
ret += self.split_sum(part) + part
|
|
97
|
-
self.target = ret
|
|
98
|
-
|
|
99
|
-
def handleSum(self):
|
|
100
|
-
self.target = md5_text(self.target)
|
|
101
|
-
self.target = self.split_sum(self.target) + self.target
|
|
102
|
-
|
|
103
|
-
def date(self, scheme):
|
|
104
|
-
self.target = md5_text(self.target)
|
|
105
|
-
d = time.localtime(self.timestamp)
|
|
106
|
-
strings = {
|
|
107
|
-
'y': str(d.tm_year),
|
|
108
|
-
'm': '%02d' % d.tm_mon,
|
|
109
|
-
'd': '%02d' % d.tm_mday,
|
|
110
|
-
}
|
|
111
|
-
self.target += ''.join(strings[c] for c in scheme)
|
|
112
|
-
|
|
113
|
-
def split_time_even_odd(self):
|
|
114
|
-
even, odd = self.even_odd()
|
|
115
|
-
self.target = odd + md5_text(self.target) + even
|
|
116
|
-
|
|
117
|
-
def split_time_odd_even(self):
|
|
118
|
-
even, odd = self.even_odd()
|
|
119
|
-
self.target = even + md5_text(self.target) + odd
|
|
120
|
-
|
|
121
|
-
def split_ip_time_sum(self):
|
|
122
|
-
chunks, ip = self.preprocess(32)
|
|
123
|
-
self.target = str(sum(ip)) + chunks[0] + self.digit_sum(self.timestamp)
|
|
124
|
-
|
|
125
|
-
def split_time_ip_sum(self):
|
|
126
|
-
chunks, ip = self.preprocess(32)
|
|
127
|
-
self.target = self.digit_sum(self.timestamp) + chunks[0] + str(sum(ip))
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
class IqiyiSDKInterpreter:
|
|
131
|
-
def __init__(self, sdk_code):
|
|
132
|
-
self.sdk_code = sdk_code
|
|
133
|
-
|
|
134
|
-
def run(self, target, ip, timestamp):
|
|
135
|
-
self.sdk_code = decode_packed_codes(self.sdk_code)
|
|
136
|
-
|
|
137
|
-
functions = re.findall(r'input=([a-zA-Z0-9]+)\(input', self.sdk_code)
|
|
138
|
-
|
|
139
|
-
sdk = IqiyiSDK(target, ip, timestamp)
|
|
140
|
-
|
|
141
|
-
other_functions = {
|
|
142
|
-
'handleSum': sdk.handleSum,
|
|
143
|
-
'handleInput8': sdk.handle_input8,
|
|
144
|
-
'handleInput16': sdk.handle_input16,
|
|
145
|
-
'splitTimeEvenOdd': sdk.split_time_even_odd,
|
|
146
|
-
'splitTimeOddEven': sdk.split_time_odd_even,
|
|
147
|
-
'splitIpTimeSum': sdk.split_ip_time_sum,
|
|
148
|
-
'splitTimeIpSum': sdk.split_time_ip_sum,
|
|
149
|
-
}
|
|
150
|
-
for function in functions:
|
|
151
|
-
if re.match(r'mod\d+', function):
|
|
152
|
-
sdk.mod(int(function[3:]))
|
|
153
|
-
elif re.match(r'date[ymd]{3}', function):
|
|
154
|
-
sdk.date(function[4:])
|
|
155
|
-
elif re.match(r'split\d+', function):
|
|
156
|
-
sdk.split(int(function[5:]))
|
|
157
|
-
elif function in other_functions:
|
|
158
|
-
other_functions[function]()
|
|
159
|
-
else:
|
|
160
|
-
raise ExtractorError(f'Unknown function {function}')
|
|
161
|
-
|
|
162
|
-
return sdk.target
|
|
163
|
-
|
|
164
|
-
|
|
165
34
|
class IqiyiIE(InfoExtractor):
|
|
166
35
|
IE_NAME = 'iqiyi'
|
|
167
36
|
IE_DESC = '爱奇艺'
|
|
168
37
|
|
|
169
38
|
_VALID_URL = r'https?://(?:(?:[^.]+\.)?iqiyi\.com|www\.pps\.tv)/.+\.html'
|
|
170
39
|
|
|
171
|
-
_NETRC_MACHINE = 'iqiyi'
|
|
172
|
-
|
|
173
40
|
_TESTS = [{
|
|
174
41
|
'url': 'http://www.iqiyi.com/v_19rrojlavg.html',
|
|
175
42
|
# MD5 checksum differs on my machine and Travis CI
|
|
@@ -234,57 +101,6 @@ class IqiyiIE(InfoExtractor):
|
|
|
234
101
|
'18': 7, # 1080p
|
|
235
102
|
}
|
|
236
103
|
|
|
237
|
-
@staticmethod
|
|
238
|
-
def _rsa_fun(data):
|
|
239
|
-
# public key extracted from http://static.iqiyi.com/js/qiyiV2/20160129180840/jobs/i18n/i18nIndex.js
|
|
240
|
-
N = 0xab86b6371b5318aaa1d3c9e612a9f1264f372323c8c0f19875b5fc3b3fd3afcc1e5bec527aa94bfa85bffc157e4245aebda05389a5357b75115ac94f074aefcd
|
|
241
|
-
e = 65537
|
|
242
|
-
|
|
243
|
-
return ohdave_rsa_encrypt(data, e, N)
|
|
244
|
-
|
|
245
|
-
def _perform_login(self, username, password):
|
|
246
|
-
|
|
247
|
-
data = self._download_json(
|
|
248
|
-
'http://kylin.iqiyi.com/get_token', None,
|
|
249
|
-
note='Get token for logging', errnote='Unable to get token for logging')
|
|
250
|
-
sdk = data['sdk']
|
|
251
|
-
timestamp = int(time.time())
|
|
252
|
-
target = (
|
|
253
|
-
f'/apis/reglogin/login.action?lang=zh_TW&area_code=null&email={username}'
|
|
254
|
-
f'&passwd={self._rsa_fun(password.encode())}&agenttype=1&from=undefined&keeplogin=0&piccode=&fromurl=&_pos=1')
|
|
255
|
-
|
|
256
|
-
interp = IqiyiSDKInterpreter(sdk)
|
|
257
|
-
sign = interp.run(target, data['ip'], timestamp)
|
|
258
|
-
|
|
259
|
-
validation_params = {
|
|
260
|
-
'target': target,
|
|
261
|
-
'server': 'BEA3AA1908656AABCCFF76582C4C6660',
|
|
262
|
-
'token': data['token'],
|
|
263
|
-
'bird_src': 'f8d91d57af224da7893dd397d52d811a',
|
|
264
|
-
'sign': sign,
|
|
265
|
-
'bird_t': timestamp,
|
|
266
|
-
}
|
|
267
|
-
validation_result = self._download_json(
|
|
268
|
-
'http://kylin.iqiyi.com/validate?' + urllib.parse.urlencode(validation_params), None,
|
|
269
|
-
note='Validate credentials', errnote='Unable to validate credentials')
|
|
270
|
-
|
|
271
|
-
MSG_MAP = {
|
|
272
|
-
'P00107': 'please login via the web interface and enter the CAPTCHA code',
|
|
273
|
-
'P00117': 'bad username or password',
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
code = validation_result['code']
|
|
277
|
-
if code != 'A00000':
|
|
278
|
-
msg = MSG_MAP.get(code)
|
|
279
|
-
if not msg:
|
|
280
|
-
msg = f'error {code}'
|
|
281
|
-
if validation_result.get('msg'):
|
|
282
|
-
msg += ': ' + validation_result['msg']
|
|
283
|
-
self.report_warning('unable to log in: ' + msg)
|
|
284
|
-
return False
|
|
285
|
-
|
|
286
|
-
return True
|
|
287
|
-
|
|
288
104
|
def get_raw_data(self, tvid, video_id):
|
|
289
105
|
tm = int(time.time() * 1000)
|
|
290
106
|
|