2 from __future__
import unicode_literals
7 from .common
import InfoExtractor
16 class TrovoBaseIE(InfoExtractor
):
17 _VALID_URL_BASE
= r
'https?://(?:www\.)?trovo\.live/'
18 _HEADERS
= {'Origin': 'https://trovo.live'}
20 def _extract_streamer_info(self
, data
):
21 streamer_info
= data
.get('streamerInfo') or {}
22 username
= streamer_info
.get('userName')
24 'uploader': streamer_info
.get('nickName'),
25 'uploader_id': str_or_none(streamer_info
.get('uid')),
26 'uploader_url': 'https://trovo.live/' + username
if username
else None,
30 class TrovoIE(TrovoBaseIE
):
31 _VALID_URL
= TrovoBaseIE
._VALID
_URL
_BASE
+ r
'(?!(?:clip|video)/)(?P<id>[^/?&#]+)'
33 def _real_extract(self
, url
):
34 username
= self
._match
_id
(url
)
35 live_info
= self
._download
_json
(
36 'https://gql.trovo.live/', username
, query
={
38 getLiveInfo(params: {userName: "%s"}) {
56 })['data']['getLiveInfo']
57 if live_info
.get('isLive') == 0:
58 raise ExtractorError('%s is offline' % username
, expected
=True)
59 program_info
= live_info
['programInfo']
60 program_id
= program_info
['id']
61 title
= self
._live
_title
(program_info
['title'])
64 for stream_info
in (program_info
.get('streamInfo') or []):
65 play_url
= stream_info
.get('playUrl')
68 format_id
= stream_info
.get('desc')
70 'format_id': format_id
,
71 'height': int_or_none(format_id
[:-1]) if format_id
else None,
73 'http_headers': self
._HEADERS
,
75 self
._sort
_formats
(formats
)
81 'thumbnail': program_info
.get('coverUrl'),
84 info
.update(self
._extract
_streamer
_info
(live_info
))
88 class TrovoVodIE(TrovoBaseIE
):
89 _VALID_URL
= TrovoBaseIE
._VALID
_URL
_BASE
+ r
'(?:clip|video)/(?P<id>[^/?&#]+)'
91 'url': 'https://trovo.live/video/ltv-100095501_100095501_1609596043',
93 'id': 'ltv-100095501_100095501_1609596043',
95 'title': 'Spontaner 12 Stunden Stream! - Ok Boomer!',
97 'timestamp': 1609640305,
98 'upload_date': '20210103',
99 'uploader_id': '100095501',
103 'comment_count': int,
104 'comments': 'mincount:8',
105 'categories': ['Grand Theft Auto V'],
108 'url': 'https://trovo.live/clip/lc-5285890810184026005',
109 'only_matching': True,
112 def _real_extract(self
, url
):
113 vid
= self
._match
_id
(url
)
114 resp
= self
._download
_json
(
115 'https://gql.trovo.live/', vid
, data
=json
.dumps([{
117 batchGetVodDetailInfo(params: {vids: ["%s"]}) {
123 getCommentList(params: {appInfo: {postID: "%s"}, pageSize: 1000000000, preview: {}}) {
136 }]).encode(), headers
={
137 'Content-Type': 'application/json',
139 vod_detail_info
= resp
[0]['data']['batchGetVodDetailInfo']['VodDetailInfos'][vid
]
140 vod_info
= vod_detail_info
['vodInfo']
141 title
= vod_info
['title']
143 language
= vod_info
.get('languageName')
145 for play_info
in (vod_info
.get('playInfos') or []):
146 play_url
= play_info
.get('playUrl')
149 format_id
= play_info
.get('desc')
152 'filesize': int_or_none(play_info
.get('fileSize')),
153 'format_id': format_id
,
154 'height': int_or_none(format_id
[:-1]) if format_id
else None,
155 'language': language
,
156 'protocol': 'm3u8_native',
157 'tbr': int_or_none(play_info
.get('bitrate')),
159 'http_headers': self
._HEADERS
,
161 self
._sort
_formats
(formats
)
163 category
= vod_info
.get('categoryName')
164 get_count
= lambda x
: int_or_none(vod_info
.get(x
+ 'Num'))
166 comment_list
= try_get(resp
, lambda x
: x
[1]['data']['getCommentList']['commentList'], list) or []
168 for comment
in comment_list
:
169 content
= comment
.get('content')
172 author
= comment
.get('author') or {}
173 parent
= comment
.get('parentID')
175 'author': author
.get('nickName'),
176 'author_id': str_or_none(author
.get('uid')),
177 'id': str_or_none(comment
.get('commentID')),
179 'timestamp': int_or_none(comment
.get('createdAt')),
180 'parent': 'root' if parent
== 0 else str_or_none(parent
),
187 'thumbnail': vod_info
.get('coverUrl'),
188 'timestamp': int_or_none(vod_info
.get('publishTs')),
189 'duration': int_or_none(vod_info
.get('duration')),
190 'view_count': get_count('watch'),
191 'like_count': get_count('like'),
192 'comment_count': get_count('comment'),
193 'comments': comments
,
194 'categories': [category
] if category
else None,
196 info
.update(self
._extract
_streamer
_info
(vod_detail_info
))
200 class TrovoChannelBaseIE(InfoExtractor
):
201 def _get_vod_json(self
, page
, uid
):
202 raise NotImplementedError('This method must be implemented by subclasses')
204 def _entries(self
, uid
):
205 for page
in itertools
.count(1):
206 vod_json
= self
._get
_vod
_json
(page
, uid
)
207 vods
= vod_json
.get('vodInfos', [])
209 yield self
.url_result(
210 'https://trovo.live/%s/%s' % (self
._TYPE
, vod
.get('vid')),
211 ie
=TrovoVodIE
.ie_key())
212 has_more
= vod_json
['hasMore']
216 def _real_extract(self
, url
):
217 id = self
._match
_id
(url
)
218 uid
= str(self
._download
_json
('https://gql.trovo.live/', id, query
={
219 'query': '{getLiveInfo(params:{userName:"%s"}){streamerInfo{uid}}}' % id
220 })['data']['getLiveInfo']['streamerInfo']['uid'])
221 return self
.playlist_result(self
._entries
(uid
), playlist_id
=uid
)
224 class TrovoChannelVodIE(TrovoChannelBaseIE
):
225 _VALID_URL
= r
'trovovod:(?P<id>[^\s]+)'
226 IE_DESC
= 'All VODs of a trovo.live channel, "trovovod" keyword'
229 'url': 'trovovod:OneTappedYou',
230 'playlist_mincount': 24,
236 _QUERY
= '{getChannelLtvVideoInfos(params:{pageSize:99,currPage:%d,channelID:%s}){hasMore,vodInfos{vid}}}'
239 def _get_vod_json(self
, page
, uid
):
240 return self
._download
_json
('https://gql.trovo.live/', uid
, query
={
241 'query': self
._QUERY
% (page
, uid
)
242 })['data']['getChannelLtvVideoInfos']
245 class TrovoChannelClipIE(TrovoChannelBaseIE
):
246 _VALID_URL
= r
'trovoclip:(?P<id>[^\s]+)'
247 IE_DESC
= 'All Clips of a trovo.live channel, "trovoclip" keyword'
250 'url': 'trovoclip:OneTappedYou',
251 'playlist_mincount': 29,
257 _QUERY
= '{getChannelClipVideoInfos(params:{pageSize:99,currPage:%d,channelID:%s,albumType:VOD_CLIP_ALBUM_TYPE_LATEST}){hasMore,vodInfos{vid}}}'
260 def _get_vod_json(self
, page
, uid
):
261 return self
._download
_json
('https://gql.trovo.live/', uid
, query
={
262 'query': self
._QUERY
% (page
, uid
)
263 })['data']['getChannelClipVideoInfos']