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