diff --git a/yt_dlp/extractor/iqiyi.py b/yt_dlp/extractor/iqiyi.py index 20539f0100..74e20a54a1 100644 --- a/yt_dlp/extractor/iqiyi.py +++ b/yt_dlp/extractor/iqiyi.py @@ -10,6 +10,7 @@ from ..compat import ( compat_str, compat_urllib_parse_urlencode, + compat_urllib_parse_unquote ) from .openload import PhantomJSwrapper from ..utils import ( @@ -17,6 +18,7 @@ decode_packed_codes, ExtractorError, float_or_none, + format_field, get_element_by_id, get_element_by_attribute, int_or_none, @@ -431,6 +433,10 @@ class IqIE(InfoExtractor): 'format': '500', }, 'expected_warnings': ['format is restricted'] + }, { + # VIP-restricted video + 'url': 'https://www.iq.com/play/mermaid-in-the-fog-2021-gbdpx13bs4', + 'only_matching': True }] _BID_TAGS = { '100': '240P', @@ -457,8 +463,8 @@ class IqIE(InfoExtractor): _DASH_JS = ''' console.log(page.evaluate(function() { var tvid = "%(tvid)s"; var vid = "%(vid)s"; var src = "%(src)s"; - var dfp = "%(dfp)s"; var mode = "%(mode)s"; var lang = "%(lang)s"; var bid_list = %(bid_list)s; - var tm = new Date().getTime(); + var uid = "%(uid)s"; var dfp = "%(dfp)s"; var mode = "%(mode)s"; var lang = "%(lang)s"; + var bid_list = %(bid_list)s; var ut_list = %(ut_list)s; var tm = new Date().getTime(); var cmd5x_func = %(cmd5x_func)s; var cmd5x_exporter = {}; cmd5x_func({}, cmd5x_exporter, {}); var cmd5x = cmd5x_exporter.cmd5x; var authKey = cmd5x(cmd5x('') + tm + '' + tvid); var k_uid = Array.apply(null, Array(32)).map(function() {return Math.floor(Math.random() * 15).toString(16)}).join(''); @@ -472,7 +478,7 @@ class IqIE(InfoExtractor): 'src': src, 'vt': 0, 'rs': 1, - 'uid': 0, + 'uid': uid, 'ori': 'pcw', 'ps': 1, 'k_uid': k_uid, @@ -509,12 +515,14 @@ class IqIE(InfoExtractor): 'version': '10.0', 'dfp': dfp }), - 'ut': 0, // TODO: Set ut param for VIP members }; var enc_params = []; for (var prop in query) { enc_params.push(encodeURIComponent(prop) + '=' + encodeURIComponent(query[prop])); } + ut_list.forEach(function(ut) { + enc_params.push('ut=' + ut); + }) var dash_path = '/dash?' + enc_params.join('&'); dash_path += '&vf=' + cmd5x(dash_path); dash_paths[bid] = dash_path; }); @@ -571,17 +579,37 @@ def _real_extract(self, url): page_data = next_props['initialState']['play'] video_info = page_data['curVideoInfo'] + uid = traverse_obj( + self._parse_json( + self._get_cookie('I00002', '{}'), video_id, transform_source=compat_urllib_parse_unquote, fatal=False), + ('data', 'uid'), default=0) + + if uid: + vip_data = self._download_json( + 'https://pcw-api.iq.com/api/vtype', video_id, note='Downloading VIP data', errnote='Unable to download VIP data', query={ + 'batch': 1, + 'platformId': 3, + 'modeCode': self._get_cookie('mod', 'intl'), + 'langCode': self._get_cookie('lang', 'en_us'), + 'deviceId': self._get_cookie('QC005', '') + }, fatal=False) + ut_list = traverse_obj(vip_data, ('data', 'all_vip', ..., 'vipType'), expected_type=str_or_none, default=[]) + else: + ut_list = ['0'] + # bid 0 as an initial format checker dash_paths = self._parse_json(PhantomJSwrapper(self).get( url, html='', video_id=video_id, note2='Executing signature code', jscode=self._DASH_JS % { 'tvid': video_info['tvId'], 'vid': video_info['vid'], 'src': traverse_obj(next_props, ('initialProps', 'pageProps', 'ptid'), - expected_type=str, default='01010031010018000000'), + expected_type=str, default='04022001010011000000'), + 'uid': uid, 'dfp': self._get_cookie('dfp', ''), 'mode': self._get_cookie('mod', 'intl'), 'lang': self._get_cookie('lang', 'en_us'), 'bid_list': '[' + ','.join(['0', *self._BID_TAGS.keys()]) + ']', + 'ut_list': '[' + ','.join(ut_list) + ']', 'cmd5x_func': self._extract_cmd5x_function(webpage, video_id), })[1].strip(), video_id) @@ -590,9 +618,10 @@ def _real_extract(self, url): urljoin('https://cache-video.iq.com', dash_paths['0']), video_id, note='Downloading initial video format info', errnote='Unable to download initial video format info')['data'] - preview_time = traverse_obj(initial_format_data, ('boss_ts', 'data', 'previewTime'), expected_type=float_or_none) - if preview_time: - self.report_warning(f'This preview video is limited to {preview_time} seconds') + preview_time = traverse_obj( + initial_format_data, ('boss_ts', (None, 'data'), ('previewTime', 'rtime')), expected_type=float_or_none, get_all=False) + if traverse_obj(initial_format_data, ('boss_ts', 'data', 'prv'), expected_type=int_or_none): + self.report_warning('This preview video is limited%s' % format_field(preview_time, template='to %s seconds')) # TODO: Extract audio-only formats for bid in set(traverse_obj(initial_format_data, ('program', 'video', ..., 'bid'), expected_type=str_or_none, default=[])):