Compare commits

..

No commits in common. "0a4fb0d3fe8ff5f55e9bfd9aec47ed251fd41615" and "40268a79745695a51846700c6af5d11e9a4e6e2a" have entirely different histories.

5 changed files with 25 additions and 28 deletions

View File

@ -947,11 +947,12 @@ class BiliIntlIE(BiliIntlBaseIE):
video_id = ep_id or aid video_id = ep_id or aid
webpage = self._download_webpage(url, video_id) webpage = self._download_webpage(url, video_id)
# Bstation layout # Bstation layout
initial_data = ( initial_data = self._parse_json(self._search_regex(
self._search_json(r'window\.__INITIAL_(?:DATA|STATE)__\s*=', webpage, 'preload state', video_id, default={}) r'window\.__INITIAL_(?:DATA|STATE)__\s*=\s*({.+?});', webpage,
or self._search_nuxt_data(webpage, video_id, '__initialState', fatal=False, traverse=None)) 'preload state', default='{}'), video_id, fatal=False) or {}
video_data = traverse_obj( video_data = (
initial_data, ('OgvVideo', 'epDetail'), ('UgcVideo', 'videoData'), ('ugc', 'archive'), expected_type=dict) traverse_obj(initial_data, ('OgvVideo', 'epDetail'), expected_type=dict)
or traverse_obj(initial_data, ('UgcVideo', 'videoData'), expected_type=dict) or {})
if season_id and not video_data: if season_id and not video_data:
# Non-Bstation layout, read through episode list # Non-Bstation layout, read through episode list
@ -959,7 +960,7 @@ class BiliIntlIE(BiliIntlBaseIE):
video_data = traverse_obj(season_json, video_data = traverse_obj(season_json,
('sections', ..., 'episodes', lambda _, v: str(v['episode_id']) == ep_id), ('sections', ..., 'episodes', lambda _, v: str(v['episode_id']) == ep_id),
expected_type=dict, get_all=False) expected_type=dict, get_all=False)
return self._extract_video_info(video_data or {}, ep_id=ep_id, aid=aid) return self._extract_video_info(video_data, ep_id=ep_id, aid=aid)
class BiliIntlSeriesIE(BiliIntlBaseIE): class BiliIntlSeriesIE(BiliIntlBaseIE):

View File

@ -1588,13 +1588,15 @@ class InfoExtractor:
webpage, 'next.js data', fatal=fatal, **kw), webpage, 'next.js data', fatal=fatal, **kw),
video_id, transform_source=transform_source, fatal=fatal) video_id, transform_source=transform_source, fatal=fatal)
def _search_nuxt_data(self, webpage, video_id, context_name='__NUXT__', *, fatal=True, traverse=('data', 0)): def _search_nuxt_data(self, webpage, video_id, context_name='__NUXT__', return_full_data=False):
"""Parses Nuxt.js metadata. This works as long as the function __NUXT__ invokes is a pure function""" ''' Parses Nuxt.js metadata. This works as long as the function __NUXT__ invokes is a pure function. '''
# not all website do this, but it can be changed
# https://stackoverflow.com/questions/67463109/how-to-change-or-hide-nuxt-and-nuxt-keyword-in-page-source
rectx = re.escape(context_name) rectx = re.escape(context_name)
FUNCTION_RE = r'\(function\((?P<arg_keys>.*?)\){return\s+(?P<js>{.*?})\s*;?\s*}\((?P<arg_vals>.*?)\)'
js, arg_keys, arg_vals = self._search_regex( js, arg_keys, arg_vals = self._search_regex(
(rf'<script>\s*window\.{rectx}={FUNCTION_RE}\s*\)\s*;?\s*</script>', rf'{rectx}\(.*?{FUNCTION_RE}'), (r'<script>window\.%s=\(function\((?P<arg_keys>.*?)\)\{return\s(?P<js>\{.*?\})\}\((?P<arg_vals>.+?)\)\);?</script>' % rectx,
webpage, context_name, group=('js', 'arg_keys', 'arg_vals'), fatal=fatal) r'%s\(.*?\(function\((?P<arg_keys>.*?)\)\{return\s(?P<js>\{.*?\})\}\((?P<arg_vals>.*?)\)' % rectx),
webpage, context_name, group=['js', 'arg_keys', 'arg_vals'])
args = dict(zip(arg_keys.split(','), arg_vals.split(','))) args = dict(zip(arg_keys.split(','), arg_vals.split(',')))
@ -1602,8 +1604,10 @@ class InfoExtractor:
if val in ('undefined', 'void 0'): if val in ('undefined', 'void 0'):
args[key] = 'null' args[key] = 'null'
ret = self._parse_json(js, video_id, transform_source=functools.partial(js_to_json, vars=args), fatal=fatal) ret = self._parse_json(js_to_json(js, args), video_id)
return traverse_obj(ret, traverse) or {} if return_full_data:
return ret
return ret['data'][0]
@staticmethod @staticmethod
def _hidden_inputs(html): def _hidden_inputs(html):

View File

@ -322,7 +322,7 @@ class WatchESPNIE(AdobePassIE):
video_id)['playbackState'] video_id)['playbackState']
# ESPN+ subscription required, through cookies # ESPN+ subscription required, through cookies
if 'DTC' in video_data.get('sourceId'): if video_data.get('sourceId') == 'ESPN_DTC':
cookie = self._get_cookies(url).get('ESPN-ONESITE.WEB-PROD.token') cookie = self._get_cookies(url).get('ESPN-ONESITE.WEB-PROD.token')
if not cookie: if not cookie:
self.raise_login_required(method='cookies') self.raise_login_required(method='cookies')
@ -366,13 +366,6 @@ class WatchESPNIE(AdobePassIE):
}) })
m3u8_url, headers = playback['stream']['complete'][0]['url'], {'authorization': token} m3u8_url, headers = playback['stream']['complete'][0]['url'], {'authorization': token}
# No login required
elif video_data.get('sourceId') == 'ESPN_FREE':
asset = self._download_json(
f'https://watch.auth.api.espn.com/video/auth/media/{video_id}/asset?apikey=uiqlbgzdwuru14v627vdusswb',
video_id)
m3u8_url, headers = asset['stream'], {}
# TV Provider required # TV Provider required
else: else:
resource = self._get_mvpd_resource('ESPN', video_data['name'], video_id, None) resource = self._get_mvpd_resource('ESPN', video_data['name'], video_id, None)

View File

@ -1,5 +1,8 @@
from .common import InfoExtractor from .common import InfoExtractor
from ..utils import traverse_obj, unified_timestamp from ..utils import (
traverse_obj,
unified_timestamp,
)
class FourZeroStudioArchiveIE(InfoExtractor): class FourZeroStudioArchiveIE(InfoExtractor):
@ -22,7 +25,7 @@ class FourZeroStudioArchiveIE(InfoExtractor):
def _real_extract(self, url): def _real_extract(self, url):
video_id, uploader_id = self._match_valid_url(url).group('id', 'uploader_id') video_id, uploader_id = self._match_valid_url(url).group('id', 'uploader_id')
webpage = self._download_webpage(url, video_id) webpage = self._download_webpage(url, video_id)
nuxt_data = self._search_nuxt_data(webpage, video_id, traverse=None) nuxt_data = self._search_nuxt_data(webpage, video_id, return_full_data=True)
pcb = traverse_obj(nuxt_data, ('ssrRefs', lambda _, v: v['__typename'] == 'PublicCreatorBroadcast'), get_all=False) pcb = traverse_obj(nuxt_data, ('ssrRefs', lambda _, v: v['__typename'] == 'PublicCreatorBroadcast'), get_all=False)
uploader_internal_id = traverse_obj(nuxt_data, ( uploader_internal_id = traverse_obj(nuxt_data, (
@ -79,7 +82,7 @@ class FourZeroStudioClipIE(InfoExtractor):
def _real_extract(self, url): def _real_extract(self, url):
video_id, uploader_id = self._match_valid_url(url).group('id', 'uploader_id') video_id, uploader_id = self._match_valid_url(url).group('id', 'uploader_id')
webpage = self._download_webpage(url, video_id) webpage = self._download_webpage(url, video_id)
nuxt_data = self._search_nuxt_data(webpage, video_id, traverse=None) nuxt_data = self._search_nuxt_data(webpage, video_id, return_full_data=True)
clip_info = traverse_obj(nuxt_data, ('ssrRefs', lambda _, v: v['__typename'] == 'PublicCreatorArchivedClip'), get_all=False) clip_info = traverse_obj(nuxt_data, ('ssrRefs', lambda _, v: v['__typename'] == 'PublicCreatorArchivedClip'), get_all=False)

View File

@ -3216,11 +3216,7 @@ def js_to_json(code, vars={}):
return '"%s"' % v return '"%s"' % v
def create_map(mobj):
return json.dumps(dict(json.loads(js_to_json(mobj.group(1) or '[]', vars=vars))))
code = re.sub(r'new Date\((".+")\)', r'\g<1>', code) code = re.sub(r'new Date\((".+")\)', r'\g<1>', code)
code = re.sub(r'new Map\((\[.*?\])?\)', create_map, code)
return re.sub(r'''(?sx) return re.sub(r'''(?sx)
"(?:[^"\\]*(?:\\\\|\\['"nurtbfx/\n]))*[^"\\]*"| "(?:[^"\\]*(?:\\\\|\\['"nurtbfx/\n]))*[^"\\]*"|