1
0
mirror of https://github.com/yt-dlp/yt-dlp.git synced 2026-02-22 08:26:00 +00:00

[ie/vk] Solve JS challenges using native JS interpreter (#15992)

Closes #12970
Authored by: bashonly, 0xvd
This commit is contained in:
bashonly
2026-02-19 09:14:37 -06:00
committed by GitHub
parent 224fe478b0
commit acfc00a955

View File

@@ -1,6 +1,7 @@
import collections import collections
import hashlib import hashlib
import re import re
import urllib.parse
from .common import InfoExtractor from .common import InfoExtractor
from .dailymotion import DailymotionIE from .dailymotion import DailymotionIE
@@ -8,6 +9,7 @@ from .odnoklassniki import OdnoklassnikiIE
from .sibnet import SibnetEmbedIE from .sibnet import SibnetEmbedIE
from .vimeo import VimeoIE from .vimeo import VimeoIE
from .youtube import YoutubeIE from .youtube import YoutubeIE
from ..jsinterp import JSInterpreter
from ..utils import ( from ..utils import (
ExtractorError, ExtractorError,
UserNotLive, UserNotLive,
@@ -36,16 +38,38 @@ class VKBaseIE(InfoExtractor):
def _download_webpage_handle(self, url_or_request, video_id, *args, fatal=True, **kwargs): def _download_webpage_handle(self, url_or_request, video_id, *args, fatal=True, **kwargs):
response = super()._download_webpage_handle(url_or_request, video_id, *args, fatal=fatal, **kwargs) response = super()._download_webpage_handle(url_or_request, video_id, *args, fatal=fatal, **kwargs)
challenge_url, cookie = response[1].url if response else '', None if response is False:
if challenge_url.startswith('https://vk.com/429.html?'):
cookie = self._get_cookies(challenge_url).get('hash429')
if not cookie:
return response return response
hash429 = hashlib.md5(cookie.value.encode('ascii')).hexdigest() webpage, urlh = response
challenge_url = urlh.url
if urllib.parse.urlparse(challenge_url).path != '/challenge.html':
return response
self.to_screen(join_nonempty(
video_id and f'[{video_id}]',
'Received a JS challenge response',
delim=' '))
challenge_hash = traverse_obj(challenge_url, (
{parse_qs}, 'hash429', -1, {require('challenge hash')}))
func_code = self._search_regex(
r'(?s)var\s+salt\s*=\s*\(\s*function\s*\(\)\s*(\{.+?\})\s*\)\(\);\s*var\s+hash',
webpage, 'JS challenge salt function')
jsi = JSInterpreter(f'function salt() {func_code}')
salt = jsi.extract_function('salt')([])
self.write_debug(f'Generated salt with native JS interpreter: {salt}')
key_hash = hashlib.md5(f'{challenge_hash}:{salt}'.encode()).hexdigest()
self.write_debug(f'JS challenge key hash: {key_hash}')
# Request with the challenge key and the response should set a 'solution429' cookie
self._request_webpage( self._request_webpage(
update_url_query(challenge_url, {'key': hash429}), video_id, fatal=fatal, update_url_query(challenge_url, {'key': key_hash}), video_id,
note='Resolving WAF challenge', errnote='Failed to bypass WAF challenge') 'Submitting JS challenge solution', 'Unable to solve JS challenge', fatal=True)
return super()._download_webpage_handle(url_or_request, video_id, *args, fatal=True, **kwargs) return super()._download_webpage_handle(url_or_request, video_id, *args, fatal=True, **kwargs)
def _perform_login(self, username, password): def _perform_login(self, username, password):