]>
Commit | Line | Data |
---|---|---|
1 | from .common import InfoExtractor | |
2 | from ..utils import ( | |
3 | try_get, | |
4 | unified_timestamp | |
5 | ) | |
6 | ||
7 | ||
8 | class SovietsClosetBaseIE(InfoExtractor): | |
9 | MEDIADELIVERY_REFERER = {'Referer': 'https://iframe.mediadelivery.net/'} | |
10 | ||
11 | def parse_nuxt_jsonp(self, nuxt_jsonp_url, video_id, name): | |
12 | nuxt_jsonp = self._download_webpage(nuxt_jsonp_url, video_id, note=f'Downloading {name} __NUXT_JSONP__') | |
13 | return self._search_nuxt_data(nuxt_jsonp, video_id, '__NUXT_JSONP__') | |
14 | ||
15 | def video_meta(self, video_id, game_name, category_name, episode_number, stream_date): | |
16 | title = game_name | |
17 | if category_name and category_name != 'Misc': | |
18 | title += f' - {category_name}' | |
19 | if episode_number: | |
20 | title += f' #{episode_number}' | |
21 | ||
22 | timestamp = unified_timestamp(stream_date) | |
23 | ||
24 | return { | |
25 | 'id': video_id, | |
26 | 'title': title, | |
27 | 'http_headers': self.MEDIADELIVERY_REFERER, | |
28 | 'uploader': 'SovietWomble', | |
29 | 'creator': 'SovietWomble', | |
30 | 'release_timestamp': timestamp, | |
31 | 'timestamp': timestamp, | |
32 | 'uploader_id': 'SovietWomble', | |
33 | 'uploader_url': 'https://www.twitch.tv/SovietWomble', | |
34 | 'was_live': True, | |
35 | 'availability': 'public', | |
36 | 'series': game_name, | |
37 | 'season': category_name, | |
38 | 'episode_number': episode_number, | |
39 | } | |
40 | ||
41 | ||
42 | class SovietsClosetIE(SovietsClosetBaseIE): | |
43 | _VALID_URL = r'https?://(?:www\.)?sovietscloset\.com/video/(?P<id>[0-9]+)/?' | |
44 | _TESTS = [ | |
45 | { | |
46 | 'url': 'https://sovietscloset.com/video/1337', | |
47 | 'md5': 'bd012b04b261725510ca5383074cdd55', | |
48 | 'info_dict': { | |
49 | 'id': '1337', | |
50 | 'ext': 'mp4', | |
51 | 'title': 'The Witcher #13', | |
52 | 'thumbnail': r're:^https?://.*\.b-cdn\.net/2f0cfbf4-3588-43a9-a7d6-7c9ea3755e67/thumbnail\.jpg$', | |
53 | 'uploader': 'SovietWomble', | |
54 | 'creator': 'SovietWomble', | |
55 | 'release_timestamp': 1492091580, | |
56 | 'release_date': '20170413', | |
57 | 'timestamp': 1492091580, | |
58 | 'upload_date': '20170413', | |
59 | 'uploader_id': 'SovietWomble', | |
60 | 'uploader_url': 'https://www.twitch.tv/SovietWomble', | |
61 | 'duration': 7007, | |
62 | 'was_live': True, | |
63 | 'availability': 'public', | |
64 | 'series': 'The Witcher', | |
65 | 'season': 'Misc', | |
66 | 'episode_number': 13, | |
67 | 'episode': 'Episode 13', | |
68 | }, | |
69 | }, | |
70 | { | |
71 | 'url': 'https://sovietscloset.com/video/1105', | |
72 | 'md5': '89fa928f183893cb65a0b7be846d8a90', | |
73 | 'info_dict': { | |
74 | 'id': '1105', | |
75 | 'ext': 'mp4', | |
76 | 'title': 'Arma 3 - Zeus Games #5', | |
77 | 'uploader': 'SovietWomble', | |
78 | 'thumbnail': r're:^https?://.*\.b-cdn\.net/c0e5e76f-3a93-40b4-bf01-12343c2eec5d/thumbnail\.jpg$', | |
79 | 'creator': 'SovietWomble', | |
80 | 'release_timestamp': 1461157200, | |
81 | 'release_date': '20160420', | |
82 | 'timestamp': 1461157200, | |
83 | 'upload_date': '20160420', | |
84 | 'uploader_id': 'SovietWomble', | |
85 | 'uploader_url': 'https://www.twitch.tv/SovietWomble', | |
86 | 'duration': 8804, | |
87 | 'was_live': True, | |
88 | 'availability': 'public', | |
89 | 'series': 'Arma 3', | |
90 | 'season': 'Zeus Games', | |
91 | 'episode_number': 5, | |
92 | 'episode': 'Episode 5', | |
93 | }, | |
94 | }, | |
95 | ] | |
96 | ||
97 | def _extract_bunnycdn_iframe(self, video_id, bunnycdn_id): | |
98 | iframe = self._download_webpage( | |
99 | f'https://iframe.mediadelivery.net/embed/5105/{bunnycdn_id}', | |
100 | video_id, note='Downloading BunnyCDN iframe', headers=self.MEDIADELIVERY_REFERER) | |
101 | ||
102 | m3u8_url = self._search_regex(r'(https?://.*?\.m3u8)', iframe, 'm3u8 url') | |
103 | thumbnail_url = self._search_regex(r'(https?://.*?thumbnail\.jpg)', iframe, 'thumbnail url') | |
104 | ||
105 | m3u8_formats = self._extract_m3u8_formats(m3u8_url, video_id, headers=self.MEDIADELIVERY_REFERER) | |
106 | ||
107 | if not m3u8_formats: | |
108 | duration = None | |
109 | else: | |
110 | duration = self._extract_m3u8_vod_duration( | |
111 | m3u8_formats[0]['url'], video_id, headers=self.MEDIADELIVERY_REFERER) | |
112 | ||
113 | return { | |
114 | 'formats': m3u8_formats, | |
115 | 'thumbnail': thumbnail_url, | |
116 | 'duration': duration, | |
117 | } | |
118 | ||
119 | def _real_extract(self, url): | |
120 | video_id = self._match_id(url) | |
121 | webpage = self._download_webpage(url, video_id) | |
122 | ||
123 | static_assets_base = self._search_regex(r'(/_nuxt/static/\d+)', webpage, 'staticAssetsBase') | |
124 | static_assets_base = f'https://sovietscloset.com{static_assets_base}' | |
125 | ||
126 | stream = self.parse_nuxt_jsonp(f'{static_assets_base}/video/{video_id}/payload.js', video_id, 'video')['stream'] | |
127 | ||
128 | return { | |
129 | **self.video_meta( | |
130 | video_id=video_id, game_name=stream['game']['name'], | |
131 | category_name=try_get(stream, lambda x: x['subcategory']['name'], str), | |
132 | episode_number=stream.get('number'), stream_date=stream.get('date')), | |
133 | **self._extract_bunnycdn_iframe(video_id, stream['bunnyId']), | |
134 | } | |
135 | ||
136 | ||
137 | class SovietsClosetPlaylistIE(SovietsClosetBaseIE): | |
138 | _VALID_URL = r'https?://(?:www\.)?sovietscloset\.com/(?!video)(?P<id>[^#?]+)' | |
139 | _TESTS = [ | |
140 | ||
141 | { | |
142 | 'url': 'https://sovietscloset.com/The-Witcher', | |
143 | 'info_dict': { | |
144 | 'id': 'The-Witcher', | |
145 | 'title': 'The Witcher', | |
146 | }, | |
147 | 'playlist_mincount': 31, | |
148 | }, | |
149 | { | |
150 | 'url': 'https://sovietscloset.com/Arma-3/Zeus-Games', | |
151 | 'info_dict': { | |
152 | 'id': 'Arma-3/Zeus-Games', | |
153 | 'title': 'Arma 3 - Zeus Games', | |
154 | }, | |
155 | 'playlist_mincount': 3, | |
156 | }, | |
157 | { | |
158 | 'url': 'https://sovietscloset.com/arma-3/zeus-games/', | |
159 | 'info_dict': { | |
160 | 'id': 'arma-3/zeus-games', | |
161 | 'title': 'Arma 3 - Zeus Games', | |
162 | }, | |
163 | 'playlist_mincount': 3, | |
164 | }, | |
165 | { | |
166 | 'url': 'https://sovietscloset.com/Total-War-Warhammer', | |
167 | 'info_dict': { | |
168 | 'id': 'Total-War-Warhammer', | |
169 | 'title': 'Total War: Warhammer - Greenskins', | |
170 | }, | |
171 | 'playlist_mincount': 33, | |
172 | }, | |
173 | ] | |
174 | ||
175 | def _real_extract(self, url): | |
176 | playlist_id = self._match_id(url) | |
177 | if playlist_id.endswith('/'): | |
178 | playlist_id = playlist_id[:-1] | |
179 | ||
180 | webpage = self._download_webpage(url, playlist_id) | |
181 | ||
182 | static_assets_base = self._search_regex(r'(/_nuxt/static/\d+)', webpage, 'staticAssetsBase') | |
183 | static_assets_base = f'https://sovietscloset.com{static_assets_base}' | |
184 | ||
185 | sovietscloset = self.parse_nuxt_jsonp(f'{static_assets_base}/payload.js', playlist_id, 'global')['games'] | |
186 | ||
187 | if '/' in playlist_id: | |
188 | game_slug, category_slug = playlist_id.lower().split('/') | |
189 | else: | |
190 | game_slug = playlist_id.lower() | |
191 | category_slug = 'misc' | |
192 | ||
193 | game = next(game for game in sovietscloset if game['slug'].lower() == game_slug) | |
194 | category = next((cat for cat in game['subcategories'] if cat.get('slug', '').lower() == category_slug), | |
195 | game['subcategories'][0]) | |
196 | category_slug = category.get('slug', '').lower() or category_slug | |
197 | playlist_title = game.get('name') or game_slug | |
198 | if category_slug != 'misc': | |
199 | playlist_title += f' - {category.get("name") or category_slug}' | |
200 | entries = [{ | |
201 | **self.url_result(f'https://sovietscloset.com/video/{stream["id"]}', ie=SovietsClosetIE.ie_key()), | |
202 | **self.video_meta( | |
203 | video_id=stream['id'], game_name=game['name'], category_name=category.get('name'), | |
204 | episode_number=i + 1, stream_date=stream.get('date')), | |
205 | } for i, stream in enumerate(category['streams'])] | |
206 | ||
207 | return self.playlist_result(entries, playlist_id, playlist_title) |