[tvplay] Capture and output native error message
[youtube-dl] / youtube_dl / extractor / tvplay.py
1 # coding: utf-8
2 from __future__ import unicode_literals
3
4 import re
5
6 from .common import InfoExtractor
7 from ..compat import (
8     compat_HTTPError,
9     compat_str,
10     compat_urlparse,
11 )
12 from ..utils import (
13     determine_ext,
14     ExtractorError,
15     int_or_none,
16     parse_iso8601,
17     qualities,
18     update_url_query,
19 )
20
21
22 class TVPlayIE(InfoExtractor):
23     IE_DESC = 'TV3Play and related services'
24     _VALID_URL = r'''(?x)https?://(?:www\.)?
25         (?:tvplay(?:\.skaties)?\.lv/parraides|
26            (?:tv3play|play\.tv3)\.lt/programos|
27            tv3play(?:\.tv3)?\.ee/sisu|
28            tv(?:3|6|8|10)play\.se/program|
29            (?:(?:tv3play|viasat4play|tv6play)\.no|tv3play\.dk)/programmer|
30            play\.novatv\.bg/programi
31         )/[^/]+/(?P<id>\d+)
32         '''
33     _TESTS = [
34         {
35             'url': 'http://www.tvplay.lv/parraides/vinas-melo-labak/418113?autostart=true',
36             'md5': 'a1612fe0849455423ad8718fe049be21',
37             'info_dict': {
38                 'id': '418113',
39                 'ext': 'mp4',
40                 'title': 'Kādi ir īri? - Viņas melo labāk',
41                 'description': 'Baiba apsmej īrus, kādi tie ir un ko viņi dara.',
42                 'series': 'Viņas melo labāk',
43                 'season': '2.sezona',
44                 'season_number': 2,
45                 'duration': 25,
46                 'timestamp': 1406097056,
47                 'upload_date': '20140723',
48             },
49         },
50         {
51             'url': 'http://play.tv3.lt/programos/moterys-meluoja-geriau/409229?autostart=true',
52             'info_dict': {
53                 'id': '409229',
54                 'ext': 'flv',
55                 'title': 'Moterys meluoja geriau',
56                 'description': 'md5:9aec0fc68e2cbc992d2a140bd41fa89e',
57                 'series': 'Moterys meluoja geriau',
58                 'episode_number': 47,
59                 'season': '1 sezonas',
60                 'season_number': 1,
61                 'duration': 1330,
62                 'timestamp': 1403769181,
63                 'upload_date': '20140626',
64             },
65             'params': {
66                 # rtmp download
67                 'skip_download': True,
68             },
69         },
70         {
71             'url': 'http://www.tv3play.ee/sisu/kodu-keset-linna/238551?autostart=true',
72             'info_dict': {
73                 'id': '238551',
74                 'ext': 'flv',
75                 'title': 'Kodu keset linna 398537',
76                 'description': 'md5:7df175e3c94db9e47c0d81ffa5d68701',
77                 'duration': 1257,
78                 'timestamp': 1292449761,
79                 'upload_date': '20101215',
80             },
81             'params': {
82                 # rtmp download
83                 'skip_download': True,
84             },
85         },
86         {
87             'url': 'http://www.tv3play.se/program/husraddarna/395385?autostart=true',
88             'info_dict': {
89                 'id': '395385',
90                 'ext': 'mp4',
91                 'title': 'Husräddarna S02E07',
92                 'description': 'md5:f210c6c89f42d4fc39faa551be813777',
93                 'duration': 2574,
94                 'timestamp': 1400596321,
95                 'upload_date': '20140520',
96             },
97             'params': {
98                 'skip_download': True,
99             },
100         },
101         {
102             'url': 'http://www.tv6play.se/program/den-sista-dokusapan/266636?autostart=true',
103             'info_dict': {
104                 'id': '266636',
105                 'ext': 'mp4',
106                 'title': 'Den sista dokusåpan S01E08',
107                 'description': 'md5:295be39c872520221b933830f660b110',
108                 'duration': 1492,
109                 'timestamp': 1330522854,
110                 'upload_date': '20120229',
111                 'age_limit': 18,
112             },
113             'params': {
114                 'skip_download': True,
115             },
116         },
117         {
118             'url': 'http://www.tv8play.se/program/antikjakten/282756?autostart=true',
119             'info_dict': {
120                 'id': '282756',
121                 'ext': 'mp4',
122                 'title': 'Antikjakten S01E10',
123                 'description': 'md5:1b201169beabd97e20c5ad0ad67b13b8',
124                 'duration': 2646,
125                 'timestamp': 1348575868,
126                 'upload_date': '20120925',
127             },
128             'params': {
129                 'skip_download': True,
130             },
131         },
132         {
133             'url': 'http://www.tv3play.no/programmer/anna-anka-soker-assistent/230898?autostart=true',
134             'info_dict': {
135                 'id': '230898',
136                 'ext': 'mp4',
137                 'title': 'Anna Anka søker assistent - Ep. 8',
138                 'description': 'md5:f80916bf5bbe1c5f760d127f8dd71474',
139                 'duration': 2656,
140                 'timestamp': 1277720005,
141                 'upload_date': '20100628',
142             },
143             'params': {
144                 'skip_download': True,
145             },
146         },
147         {
148             'url': 'http://www.viasat4play.no/programmer/budbringerne/21873?autostart=true',
149             'info_dict': {
150                 'id': '21873',
151                 'ext': 'mp4',
152                 'title': 'Budbringerne program 10',
153                 'description': 'md5:4db78dc4ec8a85bb04fd322a3ee5092d',
154                 'duration': 1297,
155                 'timestamp': 1254205102,
156                 'upload_date': '20090929',
157             },
158             'params': {
159                 'skip_download': True,
160             },
161         },
162         {
163             'url': 'http://www.tv6play.no/programmer/hotelinspektor-alex-polizzi/361883?autostart=true',
164             'info_dict': {
165                 'id': '361883',
166                 'ext': 'mp4',
167                 'title': 'Hotelinspektør Alex Polizzi - Ep. 10',
168                 'description': 'md5:3ecf808db9ec96c862c8ecb3a7fdaf81',
169                 'duration': 2594,
170                 'timestamp': 1393236292,
171                 'upload_date': '20140224',
172             },
173             'params': {
174                 'skip_download': True,
175             },
176         },
177         {
178             'url': 'http://play.novatv.bg/programi/zdravei-bulgariya/624952?autostart=true',
179             'info_dict': {
180                 'id': '624952',
181                 'ext': 'flv',
182                 'title': 'Здравей, България (12.06.2015 г.) ',
183                 'description': 'md5:99f3700451ac5bb71a260268b8daefd7',
184                 'duration': 8838,
185                 'timestamp': 1434100372,
186                 'upload_date': '20150612',
187             },
188             'params': {
189                 # rtmp download
190                 'skip_download': True,
191             },
192         },
193         {
194             'url': 'http://tvplay.skaties.lv/parraides/vinas-melo-labak/418113?autostart=true',
195             'only_matching': True,
196         },
197         {
198             'url': 'http://tv3play.tv3.ee/sisu/kodu-keset-linna/238551?autostart=true',
199             'only_matching': True,
200         }
201     ]
202
203     def _real_extract(self, url):
204         video_id = self._match_id(url)
205
206         video = self._download_json(
207             'http://playapi.mtgx.tv/v1/videos/%s' % video_id, video_id, 'Downloading video JSON')
208
209         title = video['title']
210
211         try:
212             streams = self._download_json(
213                 'http://playapi.mtgx.tv/v1/videos/stream/%s' % video_id,
214                 video_id, 'Downloading streams JSON')
215         except ExtractorError as e:
216             if isinstance(e.cause, compat_HTTPError) and e.cause.code == 403:
217                 msg = self._parse_json(e.cause.read().decode('utf-8'), video_id)
218                 raise ExtractorError(msg['msg'], expected=True)
219             raise
220
221         quality = qualities(['hls', 'medium', 'high'])
222         formats = []
223         for format_id, video_url in streams.get('streams', {}).items():
224             if not video_url or not isinstance(video_url, compat_str):
225                 continue
226             ext = determine_ext(video_url)
227             if ext == 'f4m':
228                 formats.extend(self._extract_f4m_formats(
229                     update_url_query(video_url, {
230                         'hdcore': '3.5.0',
231                         'plugin': 'aasp-3.5.0.151.81'
232                     }), video_id, f4m_id='hds', fatal=False))
233             elif ext == 'm3u8':
234                 formats.extend(self._extract_m3u8_formats(
235                     video_url, video_id, 'mp4', 'm3u8_native',
236                     m3u8_id='hls', fatal=False))
237             else:
238                 fmt = {
239                     'format_id': format_id,
240                     'quality': quality(format_id),
241                     'ext': ext,
242                 }
243                 if video_url.startswith('rtmp'):
244                     m = re.search(
245                         r'^(?P<url>rtmp://[^/]+/(?P<app>[^/]+))/(?P<playpath>.+)$', video_url)
246                     if not m:
247                         continue
248                     fmt.update({
249                         'ext': 'flv',
250                         'url': m.group('url'),
251                         'app': m.group('app'),
252                         'play_path': m.group('playpath'),
253                     })
254                 else:
255                     fmt.update({
256                         'url': video_url,
257                     })
258                 formats.append(fmt)
259
260         if not formats and video.get('is_geo_blocked'):
261             self.raise_geo_restricted(
262                 'This content might not be available in your country due to copyright reasons')
263
264         self._sort_formats(formats)
265
266         # TODO: webvtt in m3u8
267         subtitles = {}
268         sami_path = video.get('sami_path')
269         if sami_path:
270             lang = self._search_regex(
271                 r'_([a-z]{2})\.xml', sami_path, 'lang',
272                 default=compat_urlparse.urlparse(url).netloc.rsplit('.', 1)[-1])
273             subtitles[lang] = [{
274                 'url': sami_path,
275             }]
276
277         series = video.get('format_title')
278         episode_number = int_or_none(video.get('format_position', {}).get('episode'))
279         season = video.get('_embedded', {}).get('season', {}).get('title')
280         season_number = int_or_none(video.get('format_position', {}).get('season'))
281
282         return {
283             'id': video_id,
284             'title': title,
285             'description': video.get('description'),
286             'series': series,
287             'episode_number': episode_number,
288             'season': season,
289             'season_number': season_number,
290             'duration': int_or_none(video.get('duration')),
291             'timestamp': parse_iso8601(video.get('created_at')),
292             'view_count': int_or_none(video.get('views', {}).get('total')),
293             'age_limit': int_or_none(video.get('age_limit', 0)),
294             'formats': formats,
295             'subtitles': subtitles,
296         }