logo

youtube-dl

[mirror] Download/Watch videos from video hostersgit clone https://hacktivis.me/git/mirror/youtube-dl.git

trovo.py (6306B)


  1. # coding: utf-8
  2. from __future__ import unicode_literals
  3. import json
  4. from .common import InfoExtractor
  5. from ..utils import (
  6. ExtractorError,
  7. int_or_none,
  8. str_or_none,
  9. try_get,
  10. )
  11. class TrovoBaseIE(InfoExtractor):
  12. _VALID_URL_BASE = r'https?://(?:www\.)?trovo\.live/'
  13. def _extract_streamer_info(self, data):
  14. streamer_info = data.get('streamerInfo') or {}
  15. username = streamer_info.get('userName')
  16. return {
  17. 'uploader': streamer_info.get('nickName'),
  18. 'uploader_id': str_or_none(streamer_info.get('uid')),
  19. 'uploader_url': 'https://trovo.live/' + username if username else None,
  20. }
  21. class TrovoIE(TrovoBaseIE):
  22. _VALID_URL = TrovoBaseIE._VALID_URL_BASE + r'(?!(?:clip|video)/)(?P<id>[^/?&#]+)'
  23. def _real_extract(self, url):
  24. username = self._match_id(url)
  25. live_info = self._download_json(
  26. 'https://gql.trovo.live/', username, query={
  27. 'query': '''{
  28. getLiveInfo(params: {userName: "%s"}) {
  29. isLive
  30. programInfo {
  31. coverUrl
  32. id
  33. streamInfo {
  34. desc
  35. playUrl
  36. }
  37. title
  38. }
  39. streamerInfo {
  40. nickName
  41. uid
  42. userName
  43. }
  44. }
  45. }''' % username,
  46. })['data']['getLiveInfo']
  47. if live_info.get('isLive') == 0:
  48. raise ExtractorError('%s is offline' % username, expected=True)
  49. program_info = live_info['programInfo']
  50. program_id = program_info['id']
  51. title = self._live_title(program_info['title'])
  52. formats = []
  53. for stream_info in (program_info.get('streamInfo') or []):
  54. play_url = stream_info.get('playUrl')
  55. if not play_url:
  56. continue
  57. format_id = stream_info.get('desc')
  58. formats.append({
  59. 'format_id': format_id,
  60. 'height': int_or_none(format_id[:-1]) if format_id else None,
  61. 'url': play_url,
  62. })
  63. self._sort_formats(formats)
  64. info = {
  65. 'id': program_id,
  66. 'title': title,
  67. 'formats': formats,
  68. 'thumbnail': program_info.get('coverUrl'),
  69. 'is_live': True,
  70. }
  71. info.update(self._extract_streamer_info(live_info))
  72. return info
  73. class TrovoVodIE(TrovoBaseIE):
  74. _VALID_URL = TrovoBaseIE._VALID_URL_BASE + r'(?:clip|video)/(?P<id>[^/?&#]+)'
  75. _TESTS = [{
  76. 'url': 'https://trovo.live/video/ltv-100095501_100095501_1609596043',
  77. 'info_dict': {
  78. 'id': 'ltv-100095501_100095501_1609596043',
  79. 'ext': 'mp4',
  80. 'title': 'Spontaner 12 Stunden Stream! - Ok Boomer!',
  81. 'uploader': 'Exsl',
  82. 'timestamp': 1609640305,
  83. 'upload_date': '20210103',
  84. 'uploader_id': '100095501',
  85. 'duration': 43977,
  86. 'view_count': int,
  87. 'like_count': int,
  88. 'comment_count': int,
  89. 'comments': 'mincount:8',
  90. 'categories': ['Grand Theft Auto V'],
  91. },
  92. }, {
  93. 'url': 'https://trovo.live/clip/lc-5285890810184026005',
  94. 'only_matching': True,
  95. }]
  96. def _real_extract(self, url):
  97. vid = self._match_id(url)
  98. resp = self._download_json(
  99. 'https://gql.trovo.live/', vid, data=json.dumps([{
  100. 'query': '''{
  101. batchGetVodDetailInfo(params: {vids: ["%s"]}) {
  102. VodDetailInfos
  103. }
  104. }''' % vid,
  105. }, {
  106. 'query': '''{
  107. getCommentList(params: {appInfo: {postID: "%s"}, pageSize: 1000000000, preview: {}}) {
  108. commentList {
  109. author {
  110. nickName
  111. uid
  112. }
  113. commentID
  114. content
  115. createdAt
  116. parentID
  117. }
  118. }
  119. }''' % vid,
  120. }]).encode(), headers={
  121. 'Content-Type': 'application/json',
  122. })
  123. vod_detail_info = resp[0]['data']['batchGetVodDetailInfo']['VodDetailInfos'][vid]
  124. vod_info = vod_detail_info['vodInfo']
  125. title = vod_info['title']
  126. language = vod_info.get('languageName')
  127. formats = []
  128. for play_info in (vod_info.get('playInfos') or []):
  129. play_url = play_info.get('playUrl')
  130. if not play_url:
  131. continue
  132. format_id = play_info.get('desc')
  133. formats.append({
  134. 'ext': 'mp4',
  135. 'filesize': int_or_none(play_info.get('fileSize')),
  136. 'format_id': format_id,
  137. 'height': int_or_none(format_id[:-1]) if format_id else None,
  138. 'language': language,
  139. 'protocol': 'm3u8_native',
  140. 'tbr': int_or_none(play_info.get('bitrate')),
  141. 'url': play_url,
  142. 'http_headers': {'Origin': 'https://trovo.live'},
  143. })
  144. self._sort_formats(formats)
  145. category = vod_info.get('categoryName')
  146. get_count = lambda x: int_or_none(vod_info.get(x + 'Num'))
  147. comment_list = try_get(resp, lambda x: x[1]['data']['getCommentList']['commentList'], list) or []
  148. comments = []
  149. for comment in comment_list:
  150. content = comment.get('content')
  151. if not content:
  152. continue
  153. author = comment.get('author') or {}
  154. parent = comment.get('parentID')
  155. comments.append({
  156. 'author': author.get('nickName'),
  157. 'author_id': str_or_none(author.get('uid')),
  158. 'id': str_or_none(comment.get('commentID')),
  159. 'text': content,
  160. 'timestamp': int_or_none(comment.get('createdAt')),
  161. 'parent': 'root' if parent == 0 else str_or_none(parent),
  162. })
  163. info = {
  164. 'id': vid,
  165. 'title': title,
  166. 'formats': formats,
  167. 'thumbnail': vod_info.get('coverUrl'),
  168. 'timestamp': int_or_none(vod_info.get('publishTs')),
  169. 'duration': int_or_none(vod_info.get('duration')),
  170. 'view_count': get_count('watch'),
  171. 'like_count': get_count('like'),
  172. 'comment_count': get_count('comment'),
  173. 'comments': comments,
  174. 'categories': [category] if category else None,
  175. }
  176. info.update(self._extract_streamer_info(vod_detail_info))
  177. return info