hitbox.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. import re
  2. from .common import InfoExtractor
  3. from ..compat import compat_str
  4. from ..utils import (
  5. clean_html,
  6. determine_ext,
  7. float_or_none,
  8. int_or_none,
  9. parse_iso8601,
  10. )
  11. class HitboxIE(InfoExtractor):
  12. IE_NAME = 'hitbox'
  13. _VALID_URL = r'https?://(?:www\.)?(?:hitbox|smashcast)\.tv/(?:[^/]+/)*videos?/(?P<id>[0-9]+)'
  14. _TESTS = [{
  15. 'url': 'http://www.hitbox.tv/video/203213',
  16. 'info_dict': {
  17. 'id': '203213',
  18. 'title': 'hitbox @ gamescom, Sub Button Hype extended, Giveaway - hitbox News Update with Oxy',
  19. 'alt_title': 'hitboxlive - Aug 9th #6',
  20. 'description': '',
  21. 'ext': 'mp4',
  22. 'thumbnail': r're:^https?://.*\.jpg$',
  23. 'duration': 215.1666,
  24. 'resolution': 'HD 720p',
  25. 'uploader': 'hitboxlive',
  26. 'view_count': int,
  27. 'timestamp': 1407576133,
  28. 'upload_date': '20140809',
  29. 'categories': ['Live Show'],
  30. },
  31. 'params': {
  32. # m3u8 download
  33. 'skip_download': True,
  34. },
  35. }, {
  36. 'url': 'https://www.smashcast.tv/hitboxlive/videos/203213',
  37. 'only_matching': True,
  38. }]
  39. def _extract_metadata(self, url, video_id):
  40. thumb_base = 'https://edge.sf.hitbox.tv'
  41. metadata = self._download_json(
  42. '%s/%s' % (url, video_id), video_id, 'Downloading metadata JSON')
  43. date = 'media_live_since'
  44. media_type = 'livestream'
  45. if metadata.get('media_type') == 'video':
  46. media_type = 'video'
  47. date = 'media_date_added'
  48. video_meta = metadata.get(media_type, [])[0]
  49. title = video_meta.get('media_status')
  50. alt_title = video_meta.get('media_title')
  51. description = clean_html(
  52. video_meta.get('media_description')
  53. or video_meta.get('media_description_md'))
  54. duration = float_or_none(video_meta.get('media_duration'))
  55. uploader = video_meta.get('media_user_name')
  56. views = int_or_none(video_meta.get('media_views'))
  57. timestamp = parse_iso8601(video_meta.get(date), ' ')
  58. categories = [video_meta.get('category_name')]
  59. thumbs = [{
  60. 'url': thumb_base + video_meta.get('media_thumbnail'),
  61. 'width': 320,
  62. 'height': 180
  63. }, {
  64. 'url': thumb_base + video_meta.get('media_thumbnail_large'),
  65. 'width': 768,
  66. 'height': 432
  67. }]
  68. return {
  69. 'id': video_id,
  70. 'title': title,
  71. 'alt_title': alt_title,
  72. 'description': description,
  73. 'ext': 'mp4',
  74. 'thumbnails': thumbs,
  75. 'duration': duration,
  76. 'uploader': uploader,
  77. 'view_count': views,
  78. 'timestamp': timestamp,
  79. 'categories': categories,
  80. }
  81. def _real_extract(self, url):
  82. video_id = self._match_id(url)
  83. player_config = self._download_json(
  84. 'https://www.smashcast.tv/api/player/config/video/%s' % video_id,
  85. video_id, 'Downloading video JSON')
  86. formats = []
  87. for video in player_config['clip']['bitrates']:
  88. label = video.get('label')
  89. if label == 'Auto':
  90. continue
  91. video_url = video.get('url')
  92. if not video_url:
  93. continue
  94. bitrate = int_or_none(video.get('bitrate'))
  95. if determine_ext(video_url) == 'm3u8':
  96. if not video_url.startswith('http'):
  97. continue
  98. formats.append({
  99. 'url': video_url,
  100. 'ext': 'mp4',
  101. 'tbr': bitrate,
  102. 'format_note': label,
  103. 'protocol': 'm3u8_native',
  104. })
  105. else:
  106. formats.append({
  107. 'url': video_url,
  108. 'tbr': bitrate,
  109. 'format_note': label,
  110. })
  111. metadata = self._extract_metadata(
  112. 'https://www.smashcast.tv/api/media/video', video_id)
  113. metadata['formats'] = formats
  114. return metadata
  115. class HitboxLiveIE(HitboxIE): # XXX: Do not subclass from concrete IE
  116. IE_NAME = 'hitbox:live'
  117. _VALID_URL = r'https?://(?:www\.)?(?:hitbox|smashcast)\.tv/(?P<id>[^/?#&]+)'
  118. _TESTS = [{
  119. 'url': 'http://www.hitbox.tv/dimak',
  120. 'info_dict': {
  121. 'id': 'dimak',
  122. 'ext': 'mp4',
  123. 'description': 'md5:c9f80fa4410bc588d7faa40003fc7d0e',
  124. 'timestamp': int,
  125. 'upload_date': compat_str,
  126. 'title': compat_str,
  127. 'uploader': 'Dimak',
  128. },
  129. 'params': {
  130. # live
  131. 'skip_download': True,
  132. },
  133. }, {
  134. 'url': 'https://www.smashcast.tv/dimak',
  135. 'only_matching': True,
  136. }]
  137. @classmethod
  138. def suitable(cls, url):
  139. return False if HitboxIE.suitable(url) else super(HitboxLiveIE, cls).suitable(url)
  140. def _real_extract(self, url):
  141. video_id = self._match_id(url)
  142. player_config = self._download_json(
  143. 'https://www.smashcast.tv/api/player/config/live/%s' % video_id,
  144. video_id)
  145. formats = []
  146. cdns = player_config.get('cdns')
  147. servers = []
  148. for cdn in cdns:
  149. # Subscribe URLs are not playable
  150. if cdn.get('rtmpSubscribe') is True:
  151. continue
  152. base_url = cdn.get('netConnectionUrl')
  153. host = re.search(r'.+\.([^\.]+\.[^\./]+)/.+', base_url).group(1)
  154. if base_url not in servers:
  155. servers.append(base_url)
  156. for stream in cdn.get('bitrates'):
  157. label = stream.get('label')
  158. if label == 'Auto':
  159. continue
  160. stream_url = stream.get('url')
  161. if not stream_url:
  162. continue
  163. bitrate = int_or_none(stream.get('bitrate'))
  164. if stream.get('provider') == 'hls' or determine_ext(stream_url) == 'm3u8':
  165. if not stream_url.startswith('http'):
  166. continue
  167. formats.append({
  168. 'url': stream_url,
  169. 'ext': 'mp4',
  170. 'tbr': bitrate,
  171. 'format_note': label,
  172. 'rtmp_live': True,
  173. })
  174. else:
  175. formats.append({
  176. 'url': '%s/%s' % (base_url, stream_url),
  177. 'ext': 'mp4',
  178. 'tbr': bitrate,
  179. 'rtmp_live': True,
  180. 'format_note': host,
  181. 'page_url': url,
  182. 'player_url': 'http://www.hitbox.tv/static/player/flowplayer/flowplayer.commercial-3.2.16.swf',
  183. })
  184. metadata = self._extract_metadata(
  185. 'https://www.smashcast.tv/api/media/live', video_id)
  186. metadata['formats'] = formats
  187. metadata['is_live'] = True
  188. metadata['title'] = metadata.get('title')
  189. return metadata