+ _WORKING_CDNS = [
+ 'gcp_cdn', # live-global-cdn-v02.afreecatv.com
+ 'gs_cdn_pc_app', # pc-app.stream.afreecatv.com
+ 'gs_cdn_mobile_web', # mobile-web.stream.afreecatv.com
+ 'gs_cdn_pc_web', # pc-web.stream.afreecatv.com
+ ]
+ _BAD_CDNS = [
+ 'gs_cdn', # chromecast.afreeca.gscdn.com (cannot resolve)
+ 'gs_cdn_chromecast', # chromecast.stream.afreecatv.com (HTTP Error 400)
+ 'azure_cdn', # live-global-cdn-v01.afreecatv.com (cannot resolve)
+ 'aws_cf', # live-global-cdn-v03.afreecatv.com (cannot resolve)
+ 'kt_cdn', # kt.stream.afreecatv.com (HTTP Error 400)
+ ]
+
+ def _extract_formats(self, channel_info, broadcast_no, aid):
+ stream_base_url = channel_info.get('RMD') or 'https://livestream-manager.afreecatv.com'
+
+ # If user has not passed CDN IDs, try API-provided CDN ID followed by other working CDN IDs
+ default_cdn_ids = orderedSet([
+ *traverse_obj(channel_info, ('CDN', {str}, all, lambda _, v: v not in self._BAD_CDNS)),
+ *self._WORKING_CDNS,
+ ])
+ cdn_ids = self._configuration_arg('cdn', default_cdn_ids)
+
+ for attempt, cdn_id in enumerate(cdn_ids, start=1):
+ m3u8_url = traverse_obj(self._download_json(
+ urljoin(stream_base_url, 'broad_stream_assign.html'), broadcast_no,
+ f'Downloading {cdn_id} stream info', f'Unable to download {cdn_id} stream info',
+ fatal=False, query={
+ 'return_type': cdn_id,
+ 'broad_key': f'{broadcast_no}-common-master-hls',
+ }), ('view_url', {url_or_none}))
+ try:
+ return self._extract_m3u8_formats(
+ m3u8_url, broadcast_no, 'mp4', m3u8_id='hls', query={'aid': aid},
+ headers={'Referer': 'https://play.afreecatv.com/'})
+ except ExtractorError as e:
+ if attempt == len(cdn_ids):
+ raise
+ self.report_warning(
+ f'{e.cause or e.msg}. Retrying... (attempt {attempt} of {len(cdn_ids)})')