[dplay] Extract subtitles (Closes #9284)
[youtube-dl] / youtube_dl / extractor / dplay.py
1 # coding: utf-8
2 from __future__ import unicode_literals
3
4 import json
5 import re
6 import time
7
8 from .common import InfoExtractor
9 from ..utils import int_or_none
10
11
12 class DPlayIE(InfoExtractor):
13     _VALID_URL = r'https?://(?P<domain>it\.dplay\.com|www\.dplay\.(?:dk|se|no))/[^/]+/(?P<id>[^/?#]+)'
14
15     _TESTS = [{
16         # geo restricted, via direct unsigned hls URL
17         'url': 'http://it.dplay.com/take-me-out/stagione-1-episodio-25/',
18         'info_dict': {
19             'id': '1255600',
20             'display_id': 'stagione-1-episodio-25',
21             'ext': 'mp4',
22             'title': 'Episodio 25',
23             'description': 'md5:cae5f40ad988811b197d2d27a53227eb',
24             'duration': 2761,
25             'timestamp': 1454701800,
26             'upload_date': '20160205',
27             'creator': 'RTIT',
28             'series': 'Take me out',
29             'season_number': 1,
30             'episode_number': 25,
31             'age_limit': 0,
32         },
33         'expected_warnings': ['Unable to download f4m manifest'],
34     }, {
35         # non geo restricted, via secure api
36         'url': 'http://www.dplay.se/nugammalt-77-handelser-som-format-sverige/season-1-svensken-lar-sig-njuta-av-livet/',
37         'info_dict': {
38             'id': '3172',
39             'display_id': 'season-1-svensken-lar-sig-njuta-av-livet',
40             'ext': 'flv',
41             'title': 'Svensken lär sig njuta av livet',
42             'description': 'md5:d3819c9bccffd0fe458ca42451dd50d8',
43             'duration': 2650,
44             'timestamp': 1365454320,
45             'upload_date': '20130408',
46             'creator': 'Kanal 5 (Home)',
47             'series': 'Nugammalt - 77 händelser som format Sverige',
48             'season_number': 1,
49             'episode_number': 1,
50             'age_limit': 0,
51         },
52     }, {
53         # geo restricted, via secure api
54         'url': 'http://www.dplay.dk/mig-og-min-mor/season-6-episode-12/',
55         'info_dict': {
56             'id': '70816',
57             'display_id': 'season-6-episode-12',
58             'ext': 'flv',
59             'title': 'Episode 12',
60             'description': 'md5:9c86e51a93f8a4401fc9641ef9894c90',
61             'duration': 2563,
62             'timestamp': 1429696800,
63             'upload_date': '20150422',
64             'creator': 'Kanal 4',
65             'series': 'Mig og min mor',
66             'season_number': 6,
67             'episode_number': 12,
68             'age_limit': 0,
69         },
70     }, {
71         # geo restricted, via direct unsigned hls URL
72         'url': 'http://www.dplay.no/pga-tour/season-1-hoydepunkter-18-21-februar/',
73         'only_matching': True,
74     }]
75
76     def _real_extract(self, url):
77         mobj = re.match(self._VALID_URL, url)
78         display_id = mobj.group('id')
79         domain = mobj.group('domain')
80
81         webpage = self._download_webpage(url, display_id)
82
83         video_id = self._search_regex(
84             r'data-video-id=["\'](\d+)', webpage, 'video id')
85
86         info = self._download_json(
87             'http://%s/api/v2/ajax/videos?video_id=%s' % (domain, video_id),
88             video_id)['data'][0]
89
90         title = info['title']
91
92         PROTOCOLS = ('hls', 'hds')
93         formats = []
94
95         def extract_formats(protocol, manifest_url):
96             if protocol == 'hls':
97                 formats.extend(self._extract_m3u8_formats(
98                     manifest_url, video_id, ext='mp4',
99                     entry_protocol='m3u8_native', m3u8_id=protocol, fatal=False))
100             elif protocol == 'hds':
101                 formats.extend(self._extract_f4m_formats(
102                     manifest_url + '&hdcore=3.8.0&plugin=flowplayer-3.8.0.0',
103                     video_id, f4m_id=protocol, fatal=False))
104
105         domain_tld = domain.split('.')[-1]
106         if domain_tld in ('se', 'dk', 'no'):
107             for protocol in PROTOCOLS:
108                 # Providing dsc-geo allows to bypass geo restriction in some cases
109                 self._set_cookie(
110                     'secure.dplay.%s' % domain_tld, 'dsc-geo',
111                     json.dumps({
112                         'countryCode': domain_tld.upper(),
113                         'expiry': (time.time() + 20 * 60) * 1000,
114                     }))
115                 stream = self._download_json(
116                     'https://secure.dplay.%s/secure/api/v2/user/authorization/stream/%s?stream_type=%s'
117                     % (domain_tld, video_id, protocol), video_id,
118                     'Downloading %s stream JSON' % protocol, fatal=False)
119                 if stream and stream.get(protocol):
120                     extract_formats(protocol, stream[protocol])
121
122         # The last resort is to try direct unsigned hls/hds URLs from info dictionary.
123         # Sometimes this does work even when secure API with dsc-geo has failed (e.g.
124         # http://www.dplay.no/pga-tour/season-1-hoydepunkter-18-21-februar/).
125         if not formats:
126             for protocol in PROTOCOLS:
127                 if info.get(protocol):
128                     extract_formats(protocol, info[protocol])
129
130         self._sort_formats(formats)
131
132         subtitles = {}
133         for lang in ('se', 'sv', 'da', 'nl', 'no'):
134             for format_id in ('web_vtt', 'vtt', 'srt'):
135                 subtitle_url = info.get('subtitles_%s_%s' % (lang, format_id))
136                 if subtitle_url:
137                     subtitles.setdefault(lang, []).append({'url': subtitle_url})
138
139         return {
140             'id': video_id,
141             'display_id': display_id,
142             'title': title,
143             'description': info.get('video_metadata_longDescription'),
144             'duration': int_or_none(info.get('video_metadata_length'), scale=1000),
145             'timestamp': int_or_none(info.get('video_publish_date')),
146             'creator': info.get('video_metadata_homeChannel'),
147             'series': info.get('video_metadata_show'),
148             'season_number': int_or_none(info.get('season')),
149             'episode_number': int_or_none(info.get('episode')),
150             'age_limit': int_or_none(info.get('minimum_age')),
151             'formats': formats,
152             'subtitles': subtitles,
153         }