]> git.bitcoin.ninja Git - youtube-dl/blob - youtube_dl/extractor/nrk.py
[utils] Correct octal/hexadecimal number detection in js_to_json
[youtube-dl] / youtube_dl / extractor / nrk.py
1 # encoding: utf-8
2 from __future__ import unicode_literals
3
4 import re
5
6 from .common import InfoExtractor
7 from ..compat import compat_urllib_parse_unquote
8 from ..utils import (
9     ExtractorError,
10     int_or_none,
11     parse_age_limit,
12     parse_duration,
13 )
14
15
16 class NRKBaseIE(InfoExtractor):
17     def _extract_formats(self, manifest_url, video_id, fatal=True):
18         formats = []
19         formats.extend(self._extract_f4m_formats(
20             manifest_url + '?hdcore=3.5.0&plugin=aasp-3.5.0.151.81',
21             video_id, f4m_id='hds', fatal=fatal))
22         formats.extend(self._extract_m3u8_formats(manifest_url.replace(
23             'akamaihd.net/z/', 'akamaihd.net/i/').replace('/manifest.f4m', '/master.m3u8'),
24             video_id, 'mp4', 'm3u8_native', m3u8_id='hls', fatal=fatal))
25         return formats
26
27     def _real_extract(self, url):
28         video_id = self._match_id(url)
29
30         data = self._download_json(
31             'http://%s/mediaelement/%s' % (self._API_HOST, video_id),
32             video_id, 'Downloading mediaelement JSON')
33
34         title = data.get('fullTitle') or data.get('mainTitle') or data['title']
35         video_id = data.get('id') or video_id
36
37         entries = []
38
39         media_assets = data.get('mediaAssets')
40         if media_assets and isinstance(media_assets, list):
41             def video_id_and_title(idx):
42                 return ((video_id, title) if len(media_assets) == 1
43                         else ('%s-%d' % (video_id, idx), '%s (Part %d)' % (title, idx)))
44             for num, asset in enumerate(media_assets, 1):
45                 asset_url = asset.get('url')
46                 if not asset_url:
47                     continue
48                 formats = self._extract_formats(asset_url, video_id, fatal=False)
49                 if not formats:
50                     continue
51                 self._sort_formats(formats)
52                 entry_id, entry_title = video_id_and_title(num)
53                 duration = parse_duration(asset.get('duration'))
54                 subtitles = {}
55                 for subtitle in ('webVtt', 'timedText'):
56                     subtitle_url = asset.get('%sSubtitlesUrl' % subtitle)
57                     if subtitle_url:
58                         subtitles.setdefault('no', []).append({
59                             'url': compat_urllib_parse_unquote(subtitle_url)
60                         })
61                 entries.append({
62                     'id': asset.get('carrierId') or entry_id,
63                     'title': entry_title,
64                     'duration': duration,
65                     'subtitles': subtitles,
66                     'formats': formats,
67                 })
68
69         if not entries:
70             media_url = data.get('mediaUrl')
71             if media_url:
72                 formats = self._extract_formats(media_url, video_id)
73                 self._sort_formats(formats)
74                 duration = parse_duration(data.get('duration'))
75                 entries = [{
76                     'id': video_id,
77                     'title': title,
78                     'duration': duration,
79                     'formats': formats,
80                 }]
81
82         if not entries:
83             if data.get('usageRights', {}).get('isGeoBlocked'):
84                 raise ExtractorError(
85                     'NRK har ikke rettigheter til å vise dette programmet utenfor Norge',
86                     expected=True)
87
88         conviva = data.get('convivaStatistics') or {}
89         series = conviva.get('seriesName') or data.get('seriesTitle')
90         episode = conviva.get('episodeName') or data.get('episodeNumberOrDate')
91
92         thumbnails = None
93         images = data.get('images')
94         if images and isinstance(images, dict):
95             web_images = images.get('webImages')
96             if isinstance(web_images, list):
97                 thumbnails = [{
98                     'url': image['imageUrl'],
99                     'width': int_or_none(image.get('width')),
100                     'height': int_or_none(image.get('height')),
101                 } for image in web_images if image.get('imageUrl')]
102
103         description = data.get('description')
104
105         common_info = {
106             'description': description,
107             'series': series,
108             'episode': episode,
109             'age_limit': parse_age_limit(data.get('legalAge')),
110             'thumbnails': thumbnails,
111         }
112
113         vcodec = 'none' if data.get('mediaType') == 'Audio' else None
114
115         # TODO: extract chapters when https://github.com/rg3/youtube-dl/pull/9409 is merged
116
117         for entry in entries:
118             entry.update(common_info)
119             for f in entry['formats']:
120                 f['vcodec'] = vcodec
121
122         return self.playlist_result(entries, video_id, title, description)
123
124
125 class NRKIE(NRKBaseIE):
126     _VALID_URL = r'(?:nrk:|https?://(?:www\.)?nrk\.no/video/PS\*)(?P<id>\d+)'
127     _API_HOST = 'v8.psapi.nrk.no'
128     _TESTS = [{
129         # video
130         'url': 'http://www.nrk.no/video/PS*150533',
131         'md5': '2f7f6eeb2aacdd99885f355428715cfa',
132         'info_dict': {
133             'id': '150533',
134             'ext': 'mp4',
135             'title': 'Dompap og andre fugler i Piip-Show',
136             'description': 'md5:d9261ba34c43b61c812cb6b0269a5c8f',
137             'duration': 263,
138         }
139     }, {
140         # audio
141         'url': 'http://www.nrk.no/video/PS*154915',
142         # MD5 is unstable
143         'info_dict': {
144             'id': '154915',
145             'ext': 'flv',
146             'title': 'Slik høres internett ut når du er blind',
147             'description': 'md5:a621f5cc1bd75c8d5104cb048c6b8568',
148             'duration': 20,
149         }
150     }]
151
152
153 class NRKTVIE(NRKBaseIE):
154     IE_DESC = 'NRK TV and NRK Radio'
155     _VALID_URL = r'https?://(?:tv|radio)\.nrk(?:super)?\.no/(?:serie/[^/]+|program)/(?P<id>[a-zA-Z]{4}\d{8})(?:/\d{2}-\d{2}-\d{4})?(?:#del=(?P<part_id>\d+))?'
156     _API_HOST = 'psapi-we.nrk.no'
157
158     _TESTS = [{
159         'url': 'https://tv.nrk.no/serie/20-spoersmaal-tv/MUHH48000314/23-05-2014',
160         'md5': '4e9ca6629f09e588ed240fb11619922a',
161         'info_dict': {
162             'id': 'MUHH48000314AA',
163             'ext': 'mp4',
164             'title': '20 spørsmål 23.05.2014',
165             'description': 'md5:bdea103bc35494c143c6a9acdd84887a',
166             'duration': 1741,
167         },
168     }, {
169         'url': 'https://tv.nrk.no/program/mdfp15000514',
170         'md5': '43d0be26663d380603a9cf0c24366531',
171         'info_dict': {
172             'id': 'MDFP15000514CA',
173             'ext': 'mp4',
174             'title': 'Grunnlovsjubiléet - Stor ståhei for ingenting 24.05.2014',
175             'description': 'md5:89290c5ccde1b3a24bb8050ab67fe1db',
176             'duration': 4605,
177         },
178     }, {
179         # single playlist video
180         'url': 'https://tv.nrk.no/serie/tour-de-ski/MSPO40010515/06-01-2015#del=2',
181         'md5': 'adbd1dbd813edaf532b0a253780719c2',
182         'info_dict': {
183             'id': 'MSPO40010515-part2',
184             'ext': 'flv',
185             'title': 'Tour de Ski: Sprint fri teknikk, kvinner og menn 06.01.2015 (del 2:2)',
186             'description': 'md5:238b67b97a4ac7d7b4bf0edf8cc57d26',
187         },
188         'skip': 'Only works from Norway',
189     }, {
190         'url': 'https://tv.nrk.no/serie/tour-de-ski/MSPO40010515/06-01-2015',
191         'playlist': [{
192             'md5': '9480285eff92d64f06e02a5367970a7a',
193             'info_dict': {
194                 'id': 'MSPO40010515-part1',
195                 'ext': 'flv',
196                 'title': 'Tour de Ski: Sprint fri teknikk, kvinner og menn 06.01.2015 (del 1:2)',
197                 'description': 'md5:238b67b97a4ac7d7b4bf0edf8cc57d26',
198             },
199         }, {
200             'md5': 'adbd1dbd813edaf532b0a253780719c2',
201             'info_dict': {
202                 'id': 'MSPO40010515-part2',
203                 'ext': 'flv',
204                 'title': 'Tour de Ski: Sprint fri teknikk, kvinner og menn 06.01.2015 (del 2:2)',
205                 'description': 'md5:238b67b97a4ac7d7b4bf0edf8cc57d26',
206             },
207         }],
208         'info_dict': {
209             'id': 'MSPO40010515',
210             'title': 'Tour de Ski: Sprint fri teknikk, kvinner og menn',
211             'description': 'md5:238b67b97a4ac7d7b4bf0edf8cc57d26',
212             'duration': 6947.52,
213         },
214         'skip': 'Only works from Norway',
215     }, {
216         'url': 'https://radio.nrk.no/serie/dagsnytt/NPUB21019315/12-07-2015#',
217         'only_matching': True,
218     }]
219
220
221 class NRKPlaylistIE(InfoExtractor):
222     _VALID_URL = r'https?://(?:www\.)?nrk\.no/(?!video|skole)(?:[^/]+/)+(?P<id>[^/]+)'
223
224     _TESTS = [{
225         'url': 'http://www.nrk.no/troms/gjenopplev-den-historiske-solformorkelsen-1.12270763',
226         'info_dict': {
227             'id': 'gjenopplev-den-historiske-solformorkelsen-1.12270763',
228             'title': 'Gjenopplev den historiske solformørkelsen',
229             'description': 'md5:c2df8ea3bac5654a26fc2834a542feed',
230         },
231         'playlist_count': 2,
232     }, {
233         'url': 'http://www.nrk.no/kultur/bok/rivertonprisen-til-karin-fossum-1.12266449',
234         'info_dict': {
235             'id': 'rivertonprisen-til-karin-fossum-1.12266449',
236             'title': 'Rivertonprisen til Karin Fossum',
237             'description': 'Første kvinne på 15 år til å vinne krimlitteraturprisen.',
238         },
239         'playlist_count': 5,
240     }]
241
242     def _real_extract(self, url):
243         playlist_id = self._match_id(url)
244
245         webpage = self._download_webpage(url, playlist_id)
246
247         entries = [
248             self.url_result('nrk:%s' % video_id, 'NRK')
249             for video_id in re.findall(
250                 r'class="[^"]*\brich\b[^"]*"[^>]+data-video-id="([^"]+)"',
251                 webpage)
252         ]
253
254         playlist_title = self._og_search_title(webpage)
255         playlist_description = self._og_search_description(webpage)
256
257         return self.playlist_result(
258             entries, playlist_id, playlist_title, playlist_description)
259
260
261 class NRKSkoleIE(InfoExtractor):
262     IE_DESC = 'NRK Skole'
263     _VALID_URL = r'https?://(?:www\.)?nrk\.no/skole/?\?.*\bmediaId=(?P<id>\d+)'
264
265     _TESTS = [{
266         'url': 'https://www.nrk.no/skole/?page=search&q=&mediaId=14099',
267         'md5': '6bc936b01f9dd8ed45bc58b252b2d9b6',
268         'info_dict': {
269             'id': '6021',
270             'ext': 'mp4',
271             'title': 'Genetikk og eneggede tvillinger',
272             'description': 'md5:3aca25dcf38ec30f0363428d2b265f8d',
273             'duration': 399,
274         },
275     }, {
276         'url': 'https://www.nrk.no/skole/?page=objectives&subject=naturfag&objective=K15114&mediaId=19355',
277         'only_matching': True,
278     }]
279
280     def _real_extract(self, url):
281         video_id = self._match_id(url)
282
283         webpage = self._download_webpage(
284             'https://mimir.nrk.no/plugin/1.0/static?mediaId=%s' % video_id,
285             video_id)
286
287         nrk_id = self._parse_json(
288             self._search_regex(
289                 r'<script[^>]+type=["\']application/json["\'][^>]*>({.+?})</script>',
290                 webpage, 'application json'),
291             video_id)['activeMedia']['psId']
292
293         return self.url_result('nrk:%s' % nrk_id)