]>
Commit | Line | Data |
---|---|---|
1 | import re | |
2 | ||
3 | from .common import InfoExtractor | |
4 | from .xstream import XstreamIE | |
5 | from ..utils import ( | |
6 | ExtractorError, | |
7 | float_or_none, | |
8 | try_get, | |
9 | ) | |
10 | ||
11 | ||
12 | class VGTVIE(XstreamIE): # XXX: Do not subclass from concrete IE | |
13 | IE_DESC = 'VGTV, BTTV, FTV, Aftenposten and Aftonbladet' | |
14 | _GEO_BYPASS = False | |
15 | ||
16 | _HOST_TO_APPNAME = { | |
17 | 'tv.vg.no': 'vgtv', | |
18 | 'vgtv.no': 'vgtv', | |
19 | 'bt.no/tv': 'bttv', | |
20 | 'aftenbladet.no/tv': 'satv', | |
21 | 'fvn.no/fvntv': 'fvntv', | |
22 | 'aftenposten.no/webtv': 'aptv', | |
23 | 'ap.vgtv.no/webtv': 'aptv', | |
24 | 'tv.aftonbladet.se': 'abtv', | |
25 | # obsolete URL schemas, kept in order to save one HTTP redirect | |
26 | 'tv.aftonbladet.se/abtv': 'abtv', | |
27 | 'www.aftonbladet.se/tv': 'abtv', | |
28 | } | |
29 | ||
30 | _APP_NAME_TO_VENDOR = { | |
31 | 'vgtv': 'vgtv', | |
32 | 'bttv': 'bt', | |
33 | 'satv': 'sa', | |
34 | 'fvntv': 'fvn', | |
35 | 'aptv': 'ap', | |
36 | 'abtv': 'ab', | |
37 | } | |
38 | ||
39 | _VALID_URL = r'''(?x) | |
40 | (?:https?://(?:www\.)? | |
41 | (?P<host> | |
42 | %s | |
43 | ) | |
44 | /? | |
45 | (?: | |
46 | (?:\#!/)?(?:video|live)/| | |
47 | embed?.*id=| | |
48 | a(?:rticles)?/ | |
49 | )| | |
50 | (?P<appname> | |
51 | %s | |
52 | ):) | |
53 | (?P<id>\d+) | |
54 | ''' % ('|'.join(_HOST_TO_APPNAME.keys()), '|'.join(_APP_NAME_TO_VENDOR.keys())) | |
55 | ||
56 | _TESTS = [ | |
57 | { | |
58 | # streamType: vod | |
59 | 'url': 'http://www.vgtv.no/#!/video/84196/hevnen-er-soet-episode-10-abu', | |
60 | 'md5': 'b8be7a234cebb840c0d512c78013e02f', | |
61 | 'info_dict': { | |
62 | 'id': '84196', | |
63 | 'ext': 'mp4', | |
64 | 'title': 'Hevnen er søt: Episode 10 - Abu', | |
65 | 'description': 'md5:e25e4badb5f544b04341e14abdc72234', | |
66 | 'thumbnail': r're:^https?://.*\.jpg', | |
67 | 'duration': 648.000, | |
68 | 'timestamp': 1404626400, | |
69 | 'upload_date': '20140706', | |
70 | 'view_count': int, | |
71 | }, | |
72 | }, | |
73 | { | |
74 | # streamType: wasLive | |
75 | 'url': 'http://www.vgtv.no/#!/live/100764/opptak-vgtv-foelger-em-kvalifiseringen', | |
76 | 'info_dict': { | |
77 | 'id': '100764', | |
78 | 'ext': 'flv', | |
79 | 'title': 'OPPTAK: VGTV følger EM-kvalifiseringen', | |
80 | 'description': 'md5:3772d9c0dc2dff92a886b60039a7d4d3', | |
81 | 'thumbnail': r're:^https?://.*\.jpg', | |
82 | 'duration': 9103.0, | |
83 | 'timestamp': 1410113864, | |
84 | 'upload_date': '20140907', | |
85 | 'view_count': int, | |
86 | }, | |
87 | 'params': { | |
88 | # m3u8 download | |
89 | 'skip_download': True, | |
90 | }, | |
91 | 'skip': 'Video is no longer available', | |
92 | }, | |
93 | { | |
94 | # streamType: wasLive | |
95 | 'url': 'http://www.vgtv.no/#!/live/113063/direkte-v75-fra-solvalla', | |
96 | 'info_dict': { | |
97 | 'id': '113063', | |
98 | 'ext': 'mp4', | |
99 | 'title': 'V75 fra Solvalla 30.05.15', | |
100 | 'description': 'md5:b3743425765355855f88e096acc93231', | |
101 | 'thumbnail': r're:^https?://.*\.jpg', | |
102 | 'duration': 25966, | |
103 | 'timestamp': 1432975582, | |
104 | 'upload_date': '20150530', | |
105 | 'view_count': int, | |
106 | }, | |
107 | 'params': { | |
108 | # m3u8 download | |
109 | 'skip_download': True, | |
110 | }, | |
111 | }, | |
112 | { | |
113 | 'url': 'http://www.aftenposten.no/webtv/#!/video/21039/trailer-sweatshop-i-can-t-take-any-more', | |
114 | 'md5': 'fd828cd29774a729bf4d4425fe192972', | |
115 | 'info_dict': { | |
116 | 'id': '21039', | |
117 | 'ext': 'mp4', | |
118 | 'title': 'TRAILER: «SWEATSHOP» - I can´t take any more', | |
119 | 'description': 'md5:21891f2b0dd7ec2f78d84a50e54f8238', | |
120 | 'duration': 66, | |
121 | 'timestamp': 1417002452, | |
122 | 'upload_date': '20141126', | |
123 | 'view_count': int, | |
124 | }, | |
125 | 'params': { | |
126 | # m3u8 download | |
127 | 'skip_download': True, | |
128 | }, | |
129 | }, | |
130 | { | |
131 | 'url': 'https://tv.vg.no/video/241779/politiets-ekstremkjoering', | |
132 | 'only_matching': True, | |
133 | }, | |
134 | { | |
135 | 'url': 'http://www.bt.no/tv/#!/video/100250/norling-dette-er-forskjellen-paa-1-divisjon-og-eliteserien', | |
136 | 'only_matching': True, | |
137 | }, | |
138 | { | |
139 | 'url': 'http://ap.vgtv.no/webtv#!/video/111084/de-nye-bysyklene-lettere-bedre-gir-stoerre-hjul-og-feste-til-mobil', | |
140 | 'only_matching': True, | |
141 | }, | |
142 | { | |
143 | # geoblocked | |
144 | 'url': 'http://www.vgtv.no/#!/video/127205/inside-the-mind-of-favela-funk', | |
145 | 'only_matching': True, | |
146 | }, | |
147 | { | |
148 | 'url': 'https://tv.aftonbladet.se/video/36015/vulkanutbrott-i-rymden-nu-slapper-nasa-bilderna', | |
149 | 'only_matching': True, | |
150 | }, | |
151 | { | |
152 | 'url': 'http://tv.aftonbladet.se/abtv/articles/36015', | |
153 | 'only_matching': True, | |
154 | }, | |
155 | { | |
156 | 'url': 'https://www.aftonbladet.se/tv/a/36015', | |
157 | 'only_matching': True, | |
158 | }, | |
159 | { | |
160 | 'url': 'abtv:140026', | |
161 | 'only_matching': True, | |
162 | }, | |
163 | { | |
164 | 'url': 'http://www.vgtv.no/video/84196/hevnen-er-soet-episode-10-abu', | |
165 | 'only_matching': True, | |
166 | }, | |
167 | ] | |
168 | ||
169 | def _real_extract(self, url): | |
170 | mobj = self._match_valid_url(url) | |
171 | video_id = mobj.group('id') | |
172 | host = mobj.group('host') | |
173 | appname = self._HOST_TO_APPNAME[host] if host else mobj.group('appname') | |
174 | vendor = self._APP_NAME_TO_VENDOR[appname] | |
175 | ||
176 | data = self._download_json( | |
177 | 'http://svp.vg.no/svp/api/v1/%s/assets/%s?appName=%s-website' | |
178 | % (vendor, video_id, appname), | |
179 | video_id, 'Downloading media JSON') | |
180 | ||
181 | if data.get('status') == 'inactive': | |
182 | raise ExtractorError( | |
183 | 'Video %s is no longer available' % video_id, expected=True) | |
184 | ||
185 | info = { | |
186 | 'formats': [], | |
187 | } | |
188 | if len(video_id) == 5: | |
189 | if appname == 'bttv': | |
190 | info = self._extract_video_info('btno', video_id) | |
191 | ||
192 | streams = data['streamUrls'] | |
193 | stream_type = data.get('streamType') | |
194 | is_live = stream_type == 'live' | |
195 | formats = [] | |
196 | ||
197 | hls_url = streams.get('hls') | |
198 | if hls_url: | |
199 | formats.extend(self._extract_m3u8_formats( | |
200 | hls_url, video_id, 'mp4', live=is_live, m3u8_id='hls', fatal=False)) | |
201 | ||
202 | hds_url = streams.get('hds') | |
203 | if hds_url: | |
204 | hdcore_sign = 'hdcore=3.7.0' | |
205 | f4m_formats = self._extract_f4m_formats( | |
206 | hds_url + '?%s' % hdcore_sign, video_id, f4m_id='hds', fatal=False) | |
207 | if f4m_formats: | |
208 | for entry in f4m_formats: | |
209 | # URLs without the extra param induce an 404 error | |
210 | entry.update({'extra_param_to_segment_url': hdcore_sign}) | |
211 | formats.append(entry) | |
212 | ||
213 | mp4_urls = streams.get('pseudostreaming') or [] | |
214 | mp4_url = streams.get('mp4') | |
215 | if mp4_url: | |
216 | mp4_urls.append(mp4_url) | |
217 | for mp4_url in mp4_urls: | |
218 | format_info = { | |
219 | 'url': mp4_url, | |
220 | } | |
221 | mobj = re.search(r'(\d+)_(\d+)_(\d+)', mp4_url) | |
222 | if mobj: | |
223 | tbr = int(mobj.group(3)) | |
224 | format_info.update({ | |
225 | 'width': int(mobj.group(1)), | |
226 | 'height': int(mobj.group(2)), | |
227 | 'tbr': tbr, | |
228 | 'format_id': 'mp4-%s' % tbr, | |
229 | }) | |
230 | formats.append(format_info) | |
231 | ||
232 | info['formats'].extend(formats) | |
233 | ||
234 | if not info['formats']: | |
235 | properties = try_get( | |
236 | data, lambda x: x['streamConfiguration']['properties'], list) | |
237 | if properties and 'geoblocked' in properties: | |
238 | raise self.raise_geo_restricted( | |
239 | countries=[host.rpartition('.')[-1].partition('/')[0].upper()]) | |
240 | ||
241 | info.update({ | |
242 | 'id': video_id, | |
243 | 'title': data['title'], | |
244 | 'description': data['description'], | |
245 | 'thumbnail': data['images']['main'] + '?t[]=900x506q80', | |
246 | 'timestamp': data['published'], | |
247 | 'duration': float_or_none(data['duration'], 1000), | |
248 | 'view_count': data['displays'], | |
249 | 'is_live': is_live, | |
250 | }) | |
251 | return info | |
252 | ||
253 | ||
254 | class BTArticleIE(InfoExtractor): | |
255 | IE_NAME = 'bt:article' | |
256 | IE_DESC = 'Bergens Tidende Articles' | |
257 | _VALID_URL = r'https?://(?:www\.)?bt\.no/(?:[^/]+/)+(?P<id>[^/]+)-\d+\.html' | |
258 | _TEST = { | |
259 | 'url': 'http://www.bt.no/nyheter/lokalt/Kjemper-for-internatet-1788214.html', | |
260 | 'md5': '2acbe8ad129b3469d5ae51b1158878df', | |
261 | 'info_dict': { | |
262 | 'id': '23199', | |
263 | 'ext': 'mp4', | |
264 | 'title': 'Alrekstad internat', | |
265 | 'description': 'md5:dc81a9056c874fedb62fc48a300dac58', | |
266 | 'thumbnail': r're:^https?://.*\.jpg', | |
267 | 'duration': 191, | |
268 | 'timestamp': 1289991323, | |
269 | 'upload_date': '20101117', | |
270 | 'view_count': int, | |
271 | }, | |
272 | } | |
273 | ||
274 | def _real_extract(self, url): | |
275 | webpage = self._download_webpage(url, self._match_id(url)) | |
276 | video_id = self._search_regex( | |
277 | r'<video[^>]+data-id="(\d+)"', webpage, 'video id') | |
278 | return self.url_result('bttv:%s' % video_id, 'VGTV') | |
279 | ||
280 | ||
281 | class BTVestlendingenIE(InfoExtractor): | |
282 | IE_NAME = 'bt:vestlendingen' | |
283 | IE_DESC = 'Bergens Tidende - Vestlendingen' | |
284 | _VALID_URL = r'https?://(?:www\.)?bt\.no/spesial/vestlendingen/#!/(?P<id>\d+)' | |
285 | _TESTS = [{ | |
286 | 'url': 'http://www.bt.no/spesial/vestlendingen/#!/86588', | |
287 | 'md5': 'd7d17e3337dc80de6d3a540aefbe441b', | |
288 | 'info_dict': { | |
289 | 'id': '86588', | |
290 | 'ext': 'mov', | |
291 | 'title': 'Otto Wollertsen', | |
292 | 'description': 'Vestlendingen Otto Fredrik Wollertsen', | |
293 | 'timestamp': 1430473209, | |
294 | 'upload_date': '20150501', | |
295 | }, | |
296 | 'skip': '404 Error', | |
297 | }, { | |
298 | 'url': 'http://www.bt.no/spesial/vestlendingen/#!/86255', | |
299 | 'md5': 'a2893f8632e96389f4bdf36aa9463ceb', | |
300 | 'info_dict': { | |
301 | 'id': '86255', | |
302 | 'ext': 'mov', | |
303 | 'title': 'Du må tåle å fryse og være sulten', | |
304 | 'description': 'md5:b8046f4d022d5830ddab04865791d063', | |
305 | 'upload_date': '20150321', | |
306 | 'timestamp': 1426942023, | |
307 | }, | |
308 | }] | |
309 | ||
310 | def _real_extract(self, url): | |
311 | return self.url_result('bttv:%s' % self._match_id(url), 'VGTV') |