yt-dlp 2025.12.29.233040.dev0__py3-none-any.whl → 2025.12.31.233056.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.
Files changed (20) hide show
  1. yt_dlp/extractor/_extractors.py +0 -4
  2. yt_dlp/extractor/facebook.py +0 -64
  3. yt_dlp/extractor/iqiyi.py +0 -184
  4. yt_dlp/extractor/lazy_extractors.py +2 -33
  5. yt_dlp/extractor/nebula.py +9 -1
  6. yt_dlp/extractor/twitter.py +37 -194
  7. yt_dlp/extractor/youtube/_video.py +53 -28
  8. yt_dlp/utils/_utils.py +1 -1
  9. yt_dlp/version.py +3 -3
  10. {yt_dlp-2025.12.29.233040.dev0.dist-info → yt_dlp-2025.12.31.233056.dev0.dist-info}/METADATA +1 -1
  11. {yt_dlp-2025.12.29.233040.dev0.dist-info → yt_dlp-2025.12.31.233056.dev0.dist-info}/RECORD +19 -20
  12. yt_dlp/extractor/scte.py +0 -137
  13. {yt_dlp-2025.12.29.233040.dev0.data → yt_dlp-2025.12.31.233056.dev0.data}/data/share/bash-completion/completions/yt-dlp +0 -0
  14. {yt_dlp-2025.12.29.233040.dev0.data → yt_dlp-2025.12.31.233056.dev0.data}/data/share/doc/yt_dlp/README.txt +0 -0
  15. {yt_dlp-2025.12.29.233040.dev0.data → yt_dlp-2025.12.31.233056.dev0.data}/data/share/fish/vendor_completions.d/yt-dlp.fish +0 -0
  16. {yt_dlp-2025.12.29.233040.dev0.data → yt_dlp-2025.12.31.233056.dev0.data}/data/share/man/man1/yt-dlp.1 +0 -0
  17. {yt_dlp-2025.12.29.233040.dev0.data → yt_dlp-2025.12.31.233056.dev0.data}/data/share/zsh/site-functions/_yt-dlp +0 -0
  18. {yt_dlp-2025.12.29.233040.dev0.dist-info → yt_dlp-2025.12.31.233056.dev0.dist-info}/WHEEL +0 -0
  19. {yt_dlp-2025.12.29.233040.dev0.dist-info → yt_dlp-2025.12.31.233056.dev0.dist-info}/entry_points.txt +0 -0
  20. {yt_dlp-2025.12.29.233040.dev0.dist-info → yt_dlp-2025.12.31.233056.dev0.dist-info}/licenses/LICENSE +0 -0
@@ -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
@@ -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