wasdtv.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. from .common import InfoExtractor
  2. from ..utils import (
  3. ExtractorError,
  4. int_or_none,
  5. parse_iso8601,
  6. traverse_obj,
  7. try_get,
  8. )
  9. class WASDTVBaseIE(InfoExtractor):
  10. def _fetch(self, path, video_id, description, query={}):
  11. response = self._download_json(
  12. f'https://wasd.tv/api/{path}', video_id, query=query,
  13. note=f'Downloading {description} metadata',
  14. errnote=f'Unable to download {description} metadata')
  15. error = response.get('error')
  16. if error:
  17. raise ExtractorError(f'{self.IE_NAME} returned error: {error}', expected=True)
  18. return response.get('result')
  19. def _extract_thumbnails(self, thumbnails_dict):
  20. return [{
  21. 'url': url,
  22. 'preference': index,
  23. } for index, url in enumerate(
  24. traverse_obj(thumbnails_dict, (('small', 'medium', 'large'),))) if url]
  25. def _real_extract(self, url):
  26. container = self._get_container(url)
  27. stream = traverse_obj(container, ('media_container_streams', 0))
  28. media = try_get(stream, lambda x: x['stream_media'][0])
  29. if not media:
  30. raise ExtractorError('Can not extract media data.', expected=True)
  31. media_meta = media.get('media_meta')
  32. media_url, is_live = self._get_media_url(media_meta)
  33. video_id = media.get('media_id') or container.get('media_container_id')
  34. formats, subtitles = self._extract_m3u8_formats_and_subtitles(media_url, video_id, 'mp4')
  35. return {
  36. 'id': str(video_id),
  37. 'title': container.get('media_container_name') or self._og_search_title(self._download_webpage(url, video_id)),
  38. 'description': container.get('media_container_description'),
  39. 'thumbnails': self._extract_thumbnails(media_meta.get('media_preview_images')),
  40. 'timestamp': parse_iso8601(container.get('created_at')),
  41. 'view_count': int_or_none(stream.get('stream_current_viewers' if is_live else 'stream_total_viewers')),
  42. 'is_live': is_live,
  43. 'formats': formats,
  44. 'subtitles': subtitles,
  45. }
  46. def _get_container(self, url):
  47. raise NotImplementedError('Subclass for get media container')
  48. def _get_media_url(self, media_meta):
  49. raise NotImplementedError('Subclass for get media url')
  50. class WASDTVStreamIE(WASDTVBaseIE):
  51. IE_NAME = 'wasdtv:stream'
  52. _VALID_URL = r'https?://wasd\.tv/(?P<id>[^/#?]+)$'
  53. _TESTS = [{
  54. 'url': 'https://wasd.tv/24_7',
  55. 'info_dict': {
  56. 'id': '559738',
  57. 'ext': 'mp4',
  58. 'title': 'Live 24/7 Music',
  59. 'description': '24&#x2F;7 Music',
  60. 'timestamp': int,
  61. 'upload_date': r're:^\d{8}$',
  62. 'is_live': True,
  63. 'view_count': int,
  64. },
  65. }]
  66. def _get_container(self, url):
  67. nickname = self._match_id(url)
  68. channel = self._fetch(f'channels/nicknames/{nickname}', video_id=nickname, description='channel')
  69. channel_id = channel.get('channel_id')
  70. containers = self._fetch(
  71. 'v2/media-containers', channel_id, 'running media containers',
  72. query={
  73. 'channel_id': channel_id,
  74. 'media_container_type': 'SINGLE',
  75. 'media_container_status': 'RUNNING',
  76. })
  77. if not containers:
  78. raise ExtractorError(f'{nickname} is offline', expected=True)
  79. return containers[0]
  80. def _get_media_url(self, media_meta):
  81. return media_meta['media_url'], True
  82. class WASDTVRecordIE(WASDTVBaseIE):
  83. IE_NAME = 'wasdtv:record'
  84. _VALID_URL = r'https?://wasd\.tv/[^/#?]+(?:/videos)?\?record=(?P<id>\d+)$'
  85. _TESTS = [{
  86. 'url': 'https://wasd.tv/spacemita/videos?record=907755',
  87. 'md5': 'c9899dd85be4cc997816ff9f9ca516ce',
  88. 'info_dict': {
  89. 'id': '906825',
  90. 'ext': 'mp4',
  91. 'title': 'Музыкальный',
  92. 'description': 'md5:f510388d929ff60ae61d4c3cab3137cc',
  93. 'timestamp': 1645812079,
  94. 'upload_date': '20220225',
  95. 'thumbnail': r're:^https?://.+\.jpg',
  96. 'is_live': False,
  97. 'view_count': int,
  98. },
  99. }, {
  100. 'url': 'https://wasd.tv/spacemita?record=907755',
  101. 'only_matching': True,
  102. }]
  103. def _get_container(self, url):
  104. container_id = self._match_id(url)
  105. return self._fetch(
  106. f'v2/media-containers/{container_id}', container_id, 'media container')
  107. def _get_media_url(self, media_meta):
  108. media_archive_url = media_meta.get('media_archive_url')
  109. if media_archive_url:
  110. return media_archive_url, False
  111. return media_meta['media_url'], True
  112. class WASDTVClipIE(WASDTVBaseIE):
  113. IE_NAME = 'wasdtv:clip'
  114. _VALID_URL = r'https?://wasd\.tv/[^/#?]+/clips\?clip=(?P<id>\d+)$'
  115. _TESTS = [{
  116. 'url': 'https://wasd.tv/spacemita/clips?clip=26804',
  117. 'md5': '818885e720143d7a4e776ff66fcff148',
  118. 'info_dict': {
  119. 'id': '26804',
  120. 'ext': 'mp4',
  121. 'title': 'Пуш флексит на голове стримера',
  122. 'timestamp': 1646682908,
  123. 'upload_date': '20220307',
  124. 'thumbnail': r're:^https?://.+\.jpg',
  125. 'view_count': int,
  126. },
  127. }]
  128. def _real_extract(self, url):
  129. clip_id = self._match_id(url)
  130. clip = self._fetch(f'v2/clips/{clip_id}', video_id=clip_id, description='clip')
  131. clip_data = clip.get('clip_data')
  132. formats, subtitles = self._extract_m3u8_formats_and_subtitles(clip_data.get('url'), video_id=clip_id, ext='mp4')
  133. return {
  134. 'id': clip_id,
  135. 'title': clip.get('clip_title') or self._og_search_title(self._download_webpage(url, clip_id, fatal=False)),
  136. 'thumbnails': self._extract_thumbnails(clip_data.get('preview')),
  137. 'timestamp': parse_iso8601(clip.get('created_at')),
  138. 'view_count': int_or_none(clip.get('clip_views_count')),
  139. 'formats': formats,
  140. 'subtitles': subtitles,
  141. }