]> jfr.im git - yt-dlp.git/blob - yt_dlp/extractor/sovietscloset.py
[cleanup] Misc (#8182)
[yt-dlp.git] / yt_dlp / extractor / sovietscloset.py
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)