7 from .common
import InfoExtractor
19 from ..utils
.traversal
import traverse_obj
22 class HuyaLiveIE(InfoExtractor
):
23 _VALID_URL
= r
'https?://(?:www\.|m\.)?huya\.com/(?!(?:video/play/))(?P<id>[^/#?&]+)(?:\D|$)'
27 'url': 'https://www.huya.com/572329',
37 'skip_download': True,
40 'url': 'https://www.huya.com/xiaoyugame',
41 'only_matching': True,
59 def _real_extract(self
, url
):
60 video_id
= self
._match
_id
(url
)
61 webpage
= self
._download
_webpage
(url
, video_id
=video_id
)
62 stream_data
= self
._search
_json
(r
'stream:\s', webpage
, 'stream', video_id
=video_id
, default
=None)
63 room_info
= try_get(stream_data
, lambda x
: x
['data'][0]['gameLiveInfo'])
65 raise ExtractorError('Can not extract the room info', expected
=True)
66 title
= room_info
.get('roomName') or room_info
.get('introduction') or self
._html
_extract
_title
(webpage
)
67 screen_type
= room_info
.get('screenType')
68 live_source_type
= room_info
.get('liveSourceType')
69 stream_info_list
= stream_data
['data'][0]['gameStreamInfoList']
70 if not stream_info_list
:
71 raise ExtractorError('Video is offline', expected
=True)
73 for stream_info
in stream_info_list
:
74 stream_url
= stream_info
.get('sFlvUrl')
77 stream_name
= stream_info
.get('sStreamName')
78 re_secret
= not screen_type
and live_source_type
in (0, 8, 13)
79 params
= dict(urllib
.parse
.parse_qsl(unescapeHTML(stream_info
['sFlvAntiCode'])))
82 fm
, ss
= self
.encrypt(params
, stream_info
, stream_name
)
83 for si
in stream_data
.get('vMultiStreamInfo'):
84 display_name
, bitrate
= re
.fullmatch(
85 r
'(.+?)(?:(\d+)M)?', si
.get('sDisplayName')).groups()
86 rate
= si
.get('iBitRate')
88 params
['ratio'] = rate
90 params
.pop('ratio', None)
92 rate
= int(bitrate
) * 1000
94 params
['wsSecret'] = hashlib
.md5(
95 '_'.join([fm
, params
['u'], stream_name
, ss
, params
['wsTime']]))
97 'ext': stream_info
.get('sFlvUrlSuffix'),
98 'format_id': str_or_none(stream_info
.get('iLineIndex')),
100 'url': update_url_query(f
'{stream_url}/{stream_name}.{stream_info.get("sFlvUrlSuffix")}',
102 **self
._RESOLUTION
.get(display_name
, {}),
109 'view_count': room_info
.get('totalCount'),
110 'thumbnail': room_info
.get('screenshot'),
111 'description': room_info
.get('contentIntro'),
113 'Origin': 'https://www.huya.com',
114 'Referer': 'https://www.huya.com/',
118 def encrypt(self
, params
, stream_info
, stream_name
):
119 ct
= int_or_none(params
.get('wsTime'), 16) + random
.random()
120 presenter_uid
= stream_info
['lPresenterUid']
121 if not stream_name
.startswith(str(presenter_uid
)):
124 uid
= int_or_none(ct
% 1e7
* 1e6
% 0xffffffff)
125 u1
= uid
& 0xffffffff00000000
126 u2
= uid
& 0xffffffff
128 u
= u1 | u2
>> 24 | u3
<< 8
131 'seqid': str_or_none(int_or_none(ct
* 1000) + uid
),
133 'uuid': int_or_none(ct
% 1e7
* 1e6
% 0xffffffff),
136 fm
= base64
.b64decode(params
['fm']).decode().split('_', 1)[0]
137 ss
= hashlib
.md5('|'.join([params
['seqid'], params
['ctype'], params
['t']]))
141 class HuyaVideoIE(InfoExtractor
):
142 _VALID_URL
= r
'https?://(?:www\.)?huya\.com/video/play/(?P<id>\d+)\.html'
143 IE_NAME
= 'huya:video'
147 'url': 'https://www.huya.com/video/play/1002412640.html',
152 'thumbnail': r
're:https?://.*\.jpg',
154 'uploader': '虎牙-ATS欧卡车队青木',
155 'uploader_id': '1564376151',
156 'upload_date': '20240803',
158 'comment_count': int,
163 'url': 'https://www.huya.com/video/play/556054543.html',
167 'title': '我不挑事 也不怕事',
168 'thumbnail': r
're:https?://.*\.jpg',
171 'uploader_id': '367138632',
172 'upload_date': '20210811',
174 'comment_count': int,
179 def _real_extract(self
, url
: str):
180 video_id
= self
._match
_id
(url
)
181 video_data
= self
._download
_json
(
182 'https://liveapi.huya.com/moment/getMomentContent', video_id
,
183 query
={'videoId': video_id
})['data']['moment']['videoInfo']
186 for definition
in traverse_obj(video_data
, ('definitions', lambda _
, v
: url_or_none(v
['url']))):
188 'url': definition
['url'],
189 **traverse_obj(definition
, {
190 'format_id': ('defName', {str}
),
191 'width': ('width', {int_or_none}
),
192 'height': ('height', {int_or_none}
),
193 'filesize': ('size', {int_or_none}
),
200 **traverse_obj(video_data
, {
201 'title': ('videoTitle', {str}
),
202 'thumbnail': ('videoCover', {url_or_none}
),
203 'duration': ('videoDuration', {parse_duration}
),
204 'uploader': ('nickName', {str}
),
205 'uploader_id': ('uid', {str_or_none}
),
206 'upload_date': ('videoUploadTime', {unified_strdate}
),
207 'view_count': ('videoPlayNum', {int_or_none}
),
208 'comment_count': ('videoCommentNum', {int_or_none}
),
209 'like_count': ('favorCount', {int_or_none}
),