]>
Commit | Line | Data |
---|---|---|
7d254639 | 1 | # coding: utf-8 |
f2b8db57 S |
2 | from __future__ import unicode_literals |
3 | ||
1335c3ac S |
4 | from .common import InfoExtractor |
5 | from ..utils import ( | |
6 | ExtractorError, | |
6066d03d S |
7 | int_or_none, |
8 | float_or_none, | |
9 | mimetype2ext, | |
1335c3ac | 10 | parse_iso8601, |
5e9e3d0f | 11 | remove_end, |
2c15db82 | 12 | update_url_query, |
1335c3ac | 13 | ) |
f2b8db57 S |
14 | |
15 | ||
18c1c424 | 16 | class DRTVIE(InfoExtractor): |
2c15db82 | 17 | _VALID_URL = r'https?://(?:www\.)?dr\.dk/(?:tv/se|nyheder|radio/ondemand)/(?:[^/]+/)*(?P<id>[\da-z-]+)(?:[/#?]|$)' |
96182695 S |
18 | _GEO_BYPASS = False |
19 | _GEO_COUNTRIES = ['DK'] | |
2c15db82 | 20 | IE_NAME = 'drtv' |
5e9e3d0f | 21 | _TESTS = [{ |
3fcce302 | 22 | 'url': 'https://www.dr.dk/tv/se/boern/ultra/klassen-ultra/klassen-darlig-taber-10', |
8d65880e | 23 | 'md5': '7ae17b4e18eb5d29212f424a7511c184', |
f2b8db57 | 24 | 'info_dict': { |
3fcce302 | 25 | 'id': 'klassen-darlig-taber-10', |
f2b8db57 | 26 | 'ext': 'mp4', |
3fcce302 S |
27 | 'title': 'Klassen - Dårlig taber (10)', |
28 | 'description': 'md5:815fe1b7fa656ed80580f31e8b3c79aa', | |
29 | 'timestamp': 1471991907, | |
30 | 'upload_date': '20160823', | |
31 | 'duration': 606.84, | |
32 | }, | |
5e9e3d0f | 33 | }, { |
8d65880e | 34 | # embed |
5e9e3d0f | 35 | 'url': 'https://www.dr.dk/nyheder/indland/live-christianias-rydning-af-pusher-street-er-i-gang', |
5e9e3d0f SB |
36 | 'info_dict': { |
37 | 'id': 'christiania-pusher-street-ryddes-drdkrjpo', | |
38 | 'ext': 'mp4', | |
39 | 'title': 'LIVE Christianias rydning af Pusher Street er i gang', | |
8d65880e | 40 | 'description': 'md5:2a71898b15057e9b97334f61d04e6eb5', |
5e9e3d0f SB |
41 | 'timestamp': 1472800279, |
42 | 'upload_date': '20160902', | |
43 | 'duration': 131.4, | |
3fcce302 | 44 | }, |
8d65880e S |
45 | 'params': { |
46 | 'skip_download': True, | |
47 | }, | |
b972fb03 | 48 | }, { |
8d65880e | 49 | # with SignLanguage formats |
b972fb03 | 50 | 'url': 'https://www.dr.dk/tv/se/historien-om-danmark/-/historien-om-danmark-stenalder', |
b972fb03 RR |
51 | 'info_dict': { |
52 | 'id': 'historien-om-danmark-stenalder', | |
53 | 'ext': 'mp4', | |
54 | 'title': 'Historien om Danmark: Stenalder (1)', | |
8d65880e | 55 | 'description': 'md5:8c66dcbc1669bbc6f873879880f37f2a', |
b972fb03 RR |
56 | 'timestamp': 1490401996, |
57 | 'upload_date': '20170325', | |
58 | 'duration': 3502.04, | |
8d65880e S |
59 | 'formats': 'mincount:20', |
60 | }, | |
61 | 'params': { | |
62 | 'skip_download': True, | |
b972fb03 | 63 | }, |
5e9e3d0f | 64 | }] |
f2b8db57 S |
65 | |
66 | def _real_extract(self, url): | |
6ad4013d | 67 | video_id = self._match_id(url) |
f2b8db57 | 68 | |
78271e33 S |
69 | webpage = self._download_webpage(url, video_id) |
70 | ||
aff84bec S |
71 | if '>Programmet er ikke længere tilgængeligt' in webpage: |
72 | raise ExtractorError( | |
73 | 'Video %s is not available' % video_id, expected=True) | |
74 | ||
78271e33 | 75 | video_id = self._search_regex( |
5e9e3d0f SB |
76 | (r'data-(?:material-identifier|episode-slug)="([^"]+)"', |
77 | r'data-resource="[^>"]+mu/programcard/expanded/([^"]+)"'), | |
78271e33 | 78 | webpage, 'video id') |
f2b8db57 | 79 | |
78271e33 S |
80 | programcard = self._download_json( |
81 | 'http://www.dr.dk/mu/programcard/expanded/%s' % video_id, | |
82 | video_id, 'Downloading video JSON') | |
f2b8db57 S |
83 | data = programcard['Data'][0] |
84 | ||
6066d03d S |
85 | title = remove_end(self._og_search_title( |
86 | webpage, default=None), ' | TV | DR') or data['Title'] | |
87 | description = self._og_search_description( | |
88 | webpage, default=None) or data.get('Description') | |
5e9e3d0f | 89 | |
6066d03d | 90 | timestamp = parse_iso8601(data.get('CreatedTime')) |
f2b8db57 S |
91 | |
92 | thumbnail = None | |
93 | duration = None | |
94 | ||
95 | restricted_to_denmark = False | |
96 | ||
97 | formats = [] | |
98 | subtitles = {} | |
99 | ||
100 | for asset in data['Assets']: | |
2c15db82 RA |
101 | kind = asset.get('Kind') |
102 | if kind == 'Image': | |
6066d03d | 103 | thumbnail = asset.get('Uri') |
8d65880e | 104 | elif kind in ('VideoResource', 'AudioResource'): |
6066d03d S |
105 | duration = float_or_none(asset.get('DurationInMilliseconds'), 1000) |
106 | restricted_to_denmark = asset.get('RestrictedToDenmark') | |
8d65880e | 107 | asset_target = asset.get('Target') |
6066d03d S |
108 | for link in asset.get('Links', []): |
109 | uri = link.get('Uri') | |
110 | if not uri: | |
111 | continue | |
112 | target = link.get('Target') | |
113 | format_id = target or '' | |
8d65880e S |
114 | preference = None |
115 | if asset_target in ('SpokenSubtitles', 'SignLanguage'): | |
b972fb03 | 116 | preference = -1 |
8d65880e | 117 | format_id += '-%s' % asset_target |
1335c3ac | 118 | if target == 'HDS': |
2c15db82 | 119 | f4m_formats = self._extract_f4m_formats( |
1335c3ac | 120 | uri + '?hdcore=3.3.0&plugin=aasp-3.3.0.99.43', |
9a0942ad | 121 | video_id, preference, f4m_id=format_id, fatal=False) |
2c15db82 RA |
122 | if kind == 'AudioResource': |
123 | for f in f4m_formats: | |
124 | f['vcodec'] = 'none' | |
125 | formats.extend(f4m_formats) | |
1335c3ac S |
126 | elif target == 'HLS': |
127 | formats.extend(self._extract_m3u8_formats( | |
6066d03d | 128 | uri, video_id, 'mp4', entry_protocol='m3u8_native', |
9a0942ad S |
129 | preference=preference, m3u8_id=format_id, |
130 | fatal=False)) | |
1335c3ac S |
131 | else: |
132 | bitrate = link.get('Bitrate') | |
133 | if bitrate: | |
134 | format_id += '-%s' % bitrate | |
135 | formats.append({ | |
136 | 'url': uri, | |
137 | 'format_id': format_id, | |
6066d03d | 138 | 'tbr': int_or_none(bitrate), |
1335c3ac | 139 | 'ext': link.get('FileFormat'), |
2c15db82 | 140 | 'vcodec': 'none' if kind == 'AudioResource' else None, |
1335c3ac | 141 | }) |
f2b8db57 S |
142 | subtitles_list = asset.get('SubtitlesList') |
143 | if isinstance(subtitles_list, list): | |
144 | LANGS = { | |
6fa73386 | 145 | 'Danish': 'da', |
f2b8db57 S |
146 | } |
147 | for subs in subtitles_list: | |
6066d03d S |
148 | if not subs.get('Uri'): |
149 | continue | |
150 | lang = subs.get('Language') or 'da' | |
151 | subtitles.setdefault(LANGS.get(lang, lang), []).append({ | |
152 | 'url': subs['Uri'], | |
153 | 'ext': mimetype2ext(subs.get('MimeType')) or 'vtt' | |
154 | }) | |
f2b8db57 S |
155 | |
156 | if not formats and restricted_to_denmark: | |
6066d03d S |
157 | self.raise_geo_restricted( |
158 | 'Unfortunately, DR is not allowed to show this program outside Denmark.', | |
96182695 | 159 | countries=self._GEO_COUNTRIES) |
f2b8db57 S |
160 | |
161 | self._sort_formats(formats) | |
162 | ||
f2b8db57 S |
163 | return { |
164 | 'id': video_id, | |
165 | 'title': title, | |
166 | 'description': description, | |
167 | 'thumbnail': thumbnail, | |
168 | 'timestamp': timestamp, | |
169 | 'duration': duration, | |
170 | 'formats': formats, | |
18c1c424 | 171 | 'subtitles': subtitles, |
f2b8db57 | 172 | } |
2c15db82 RA |
173 | |
174 | ||
175 | class DRTVLiveIE(InfoExtractor): | |
176 | IE_NAME = 'drtv:live' | |
177 | _VALID_URL = r'https?://(?:www\.)?dr\.dk/(?:tv|TV)/live/(?P<id>[\da-z-]+)' | |
fc11ad38 | 178 | _GEO_COUNTRIES = ['DK'] |
2c15db82 RA |
179 | _TEST = { |
180 | 'url': 'https://www.dr.dk/tv/live/dr1', | |
181 | 'info_dict': { | |
182 | 'id': 'dr1', | |
183 | 'ext': 'mp4', | |
184 | 'title': 're:^DR1 [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}$', | |
185 | }, | |
186 | 'params': { | |
187 | # m3u8 download | |
188 | 'skip_download': True, | |
189 | }, | |
190 | } | |
191 | ||
192 | def _real_extract(self, url): | |
193 | channel_id = self._match_id(url) | |
194 | channel_data = self._download_json( | |
195 | 'https://www.dr.dk/mu-online/api/1.0/channel/' + channel_id, | |
196 | channel_id) | |
197 | title = self._live_title(channel_data['Title']) | |
198 | ||
199 | formats = [] | |
200 | for streaming_server in channel_data.get('StreamingServers', []): | |
201 | server = streaming_server.get('Server') | |
202 | if not server: | |
203 | continue | |
204 | link_type = streaming_server.get('LinkType') | |
205 | for quality in streaming_server.get('Qualities', []): | |
206 | for stream in quality.get('Streams', []): | |
207 | stream_path = stream.get('Stream') | |
208 | if not stream_path: | |
209 | continue | |
210 | stream_url = update_url_query( | |
211 | '%s/%s' % (server, stream_path), {'b': ''}) | |
212 | if link_type == 'HLS': | |
213 | formats.extend(self._extract_m3u8_formats( | |
214 | stream_url, channel_id, 'mp4', | |
215 | m3u8_id=link_type, fatal=False, live=True)) | |
216 | elif link_type == 'HDS': | |
217 | formats.extend(self._extract_f4m_formats(update_url_query( | |
218 | '%s/%s' % (server, stream_path), {'hdcore': '3.7.0'}), | |
219 | channel_id, f4m_id=link_type, fatal=False)) | |
220 | self._sort_formats(formats) | |
221 | ||
222 | return { | |
223 | 'id': channel_id, | |
224 | 'title': title, | |
225 | 'thumbnail': channel_data.get('PrimaryImageUri'), | |
226 | 'formats': formats, | |
227 | 'is_live': True, | |
228 | } |