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