[wdr] Support radio players (closes #6147)
[youtube-dl] / youtube_dl / extractor / wdr.py
1 # -*- coding: utf-8 -*-
2 from __future__ import unicode_literals
3
4 import re
5
6 from .common import InfoExtractor
7 from ..utils import (
8     determine_ext,
9     js_to_json,
10     strip_jsonp,
11     unified_strdate,
12     ExtractorError,
13     urlhandle_detect_ext,
14 )
15
16
17 class WDRIE(InfoExtractor):
18     _CURRENT_MAUS_URL = r'https?://(?:www\.)wdrmaus.de/(?:[^/]+/){1,2}[^/?#]+\.php5'
19     _PAGE_REGEX = r'/(?:mediathek/)?(?P<media_type>[^/]+)/(?P<type>[^/]+)/(?P<display_id>.+)\.html'
20     _VALID_URL = r'(?P<page_url>https?://(?:www\d\.)?wdr\d?\.de)' + _PAGE_REGEX + '|' + _CURRENT_MAUS_URL
21
22     _TESTS = [
23         {
24             'url': 'http://www1.wdr.de/mediathek/video/sendungen/doku-am-freitag/video-geheimnis-aachener-dom-100.html',
25             # HDS download, MD5 is unstable
26             'info_dict': {
27                 'id': 'mdb-1058683',
28                 'ext': 'flv',
29                 'display_id': 'doku-am-freitag/video-geheimnis-aachener-dom-100',
30                 'title': 'Geheimnis Aachener Dom',
31                 'alt_title': 'Doku am Freitag',
32                 'upload_date': '20160304',
33                 'description': 'md5:87be8ff14d8dfd7a7ee46f0299b52318',
34                 'is_live': False,
35                 'subtitles': {'de': [{
36                     'url': 'http://ondemand-ww.wdr.de/medp/fsk0/105/1058683/1058683_12220974.xml'
37                 }]},
38             },
39         },
40         {
41             'url': 'http://www1.wdr.de/mediathek/audio/wdr3/wdr3-gespraech-am-samstag/audio-schriftstellerin-juli-zeh-100.html',
42             'md5': 'f4c1f96d01cf285240f53ea4309663d8',
43             'info_dict': {
44                 'id': 'mdb-1072000',
45                 'ext': 'mp3',
46                 'display_id': 'wdr3-gespraech-am-samstag/audio-schriftstellerin-juli-zeh-100',
47                 'title': 'Schriftstellerin Juli Zeh',
48                 'alt_title': 'WDR 3 Gespräch am Samstag',
49                 'upload_date': '20160312',
50                 'description': 'md5:e127d320bc2b1f149be697ce044a3dd7',
51                 'is_live': False,
52                 'subtitles': {}
53             },
54         },
55         {
56             'url': 'http://www1.wdr.de/mediathek/video/live/index.html',
57             'info_dict': {
58                 'id': 'mdb-103364',
59                 'ext': 'mp4',
60                 'display_id': 'index',
61                 'title': r're:^WDR Fernsehen im Livestream [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}$',
62                 'alt_title': 'WDR Fernsehen Live',
63                 'upload_date': None,
64                 'description': 'md5:ae2ff888510623bf8d4b115f95a9b7c9',
65                 'is_live': True,
66                 'subtitles': {}
67             },
68             'params': {
69                 'skip_download': True,  # m3u8 download
70             },
71         },
72         {
73             'url': 'http://www1.wdr.de/mediathek/video/sendungen/aktuelle-stunde/aktuelle-stunde-120.html',
74             'playlist_mincount': 8,
75             'info_dict': {
76                 'id': 'aktuelle-stunde/aktuelle-stunde-120',
77             },
78         },
79         {
80             'url': 'http://www.wdrmaus.de/aktuelle-sendung/index.php5',
81             'info_dict': {
82                 'id': 'mdb-1096487',
83                 'ext': 'flv',
84                 'upload_date': 're:^[0-9]{8}$',
85                 'title': 're:^Die Sendung mit der Maus vom [0-9.]{10}$',
86                 'description': '- Die Sendung mit der Maus -',
87             },
88             'skip': 'The id changes from week to week because of the new episode'
89         },
90         {
91             'url': 'http://www.wdrmaus.de/sachgeschichten/sachgeschichten/achterbahn.php5',
92             # HDS download, MD5 is unstable
93             'info_dict': {
94                 'id': 'mdb-186083',
95                 'ext': 'flv',
96                 'upload_date': '20130919',
97                 'title': 'Sachgeschichte - Achterbahn ',
98                 'description': '- Die Sendung mit der Maus -',
99             },
100         },
101         {
102             'url': 'http://www1.wdr.de/radio/player/radioplayer116~_layout-popupVersion.html',
103             'info_dict': {
104                 'id': 'mdb-869971',
105                 'ext': 'mp3',
106                 'title': 'Funkhaus Europa Livestream',
107                 'description': 'md5:2309992a6716c347891c045be50992e4',
108                 'upload_date': '20160101',
109             },
110         }
111     ]
112
113     def _real_extract(self, url):
114         mobj = re.match(self._VALID_URL, url)
115         url_type = mobj.group('type')
116         page_url = mobj.group('page_url')
117         display_id = mobj.group('display_id')
118         webpage = self._download_webpage(url, display_id)
119
120         # for wdr.de the data-extension is in a tag with the class "mediaLink"
121         # for wdr.de radio players, in a tag with the class "wdrrPlayerPlayBtn"
122         # for wdrmaus its in a link to the page in a multiline "videoLink"-tag
123         json_metadata = self._html_search_regex(
124             r'class=(?:"(?:mediaLink|wdrrPlayerPlayBtn)\b[^"]*"[^>]+|"videoLink\b[^"]*"[\s]*>\n[^\n]*)data-extension="([^"]+)"',
125             webpage, 'media link', default=None, flags=re.MULTILINE)
126
127         if not json_metadata:
128             entries = [
129                 self.url_result(page_url + href[0], 'WDR')
130                 for href in re.findall(
131                     r'<a href="(%s)"[^>]+data-extension=' % self._PAGE_REGEX,
132                     webpage)
133             ]
134
135             if entries:  # Playlist page
136                 return self.playlist_result(entries, playlist_id=display_id)
137
138             raise ExtractorError('No downloadable streams found', expected=True)
139
140         media_link_obj = self._parse_json(json_metadata, display_id,
141                                           transform_source=js_to_json)
142         jsonp_url = media_link_obj['mediaObj']['url']
143
144         metadata = self._download_json(
145             jsonp_url, 'metadata', transform_source=strip_jsonp)
146
147         metadata_tracker_data = metadata['trackerData']
148         metadata_media_resource = metadata['mediaResource']
149
150         formats = []
151
152         # check if the metadata contains a direct URL to a file
153         metadata_media_alt = metadata_media_resource.get('alt')
154         if metadata_media_alt:
155             for tag_name in ['videoURL', 'audioURL']:
156                 if tag_name in metadata_media_alt:
157                     alt_url = metadata_media_alt[tag_name]
158                     ext = determine_ext(alt_url)
159                     if ext == 'm3u8':
160                         m3u_fmt = self._extract_m3u8_formats(
161                             alt_url, display_id, 'mp4', 'm3u8_native',
162                             m3u8_id='hls')
163                         formats.extend(m3u_fmt)
164                     else:
165                         a_format = {
166                             'url': alt_url
167                         }
168                         if ext == 'unknown_video':
169                             urlh = self._request_webpage(
170                                 alt_url, display_id, note='Determining extension')
171                             ext = urlhandle_detect_ext(urlh)
172                             a_format['ext'] = ext
173                         formats.append(a_format)
174
175         # check if there are flash-streams for this video
176         if 'dflt' in metadata_media_resource and 'videoURL' in metadata_media_resource['dflt']:
177             video_url = metadata_media_resource['dflt']['videoURL']
178             if video_url.endswith('.f4m'):
179                 full_video_url = video_url + '?hdcore=3.2.0&plugin=aasp-3.2.0.77.18'
180                 formats.extend(self._extract_f4m_formats(full_video_url, display_id, f4m_id='hds', fatal=False))
181             elif video_url.endswith('.smil'):
182                 formats.extend(self._extract_smil_formats(video_url, 'stream', fatal=False))
183
184         subtitles = {}
185         caption_url = metadata_media_resource.get('captionURL')
186         if caption_url:
187             subtitles['de'] = [{
188                 'url': caption_url
189             }]
190
191         title = metadata_tracker_data.get('trackerClipTitle')
192         is_live = url_type == 'live'
193
194         if is_live:
195             title = self._live_title(title)
196             upload_date = None
197         elif 'trackerClipAirTime' in metadata_tracker_data:
198             upload_date = metadata_tracker_data['trackerClipAirTime']
199         else:
200             upload_date = self._html_search_meta('DC.Date', webpage, 'upload date')
201
202         if upload_date:
203             upload_date = unified_strdate(upload_date)
204
205         self._sort_formats(formats)
206
207         return {
208             'id': metadata_tracker_data.get('trackerClipId', display_id),
209             'display_id': display_id,
210             'title': title,
211             'alt_title': metadata_tracker_data.get('trackerClipSubcategory'),
212             'formats': formats,
213             'upload_date': upload_date,
214             'description': self._html_search_meta('Description', webpage),
215             'is_live': is_live,
216             'subtitles': subtitles,
217         }
218
219
220 class WDRMobileIE(InfoExtractor):
221     _VALID_URL = r'''(?x)
222         https?://mobile-ondemand\.wdr\.de/
223         .*?/fsk(?P<age_limit>[0-9]+)
224         /[0-9]+/[0-9]+/
225         (?P<id>[0-9]+)_(?P<title>[0-9]+)'''
226     IE_NAME = 'wdr:mobile'
227     _TEST = {
228         'url': 'http://mobile-ondemand.wdr.de/CMS2010/mdb/ondemand/weltweit/fsk0/42/421735/421735_4283021.mp4',
229         'info_dict': {
230             'title': '4283021',
231             'id': '421735',
232             'ext': 'mp4',
233             'age_limit': 0,
234         },
235         'skip': 'Problems with loading data.'
236     }
237
238     def _real_extract(self, url):
239         mobj = re.match(self._VALID_URL, url)
240         return {
241             'id': mobj.group('id'),
242             'title': mobj.group('title'),
243             'age_limit': int(mobj.group('age_limit')),
244             'url': url,
245             'http_headers': {
246                 'User-Agent': 'mobile',
247             },
248         }