]>
Commit | Line | Data |
---|---|---|
1362bbbb S |
1 | from __future__ import unicode_literals |
2 | ||
30bd1c16 | 3 | import re |
4 | ||
1362bbbb | 5 | from .common import InfoExtractor |
7079f8ff | 6 | from ..compat import compat_str |
1362bbbb S |
7 | from ..utils import ( |
8 | parse_duration, | |
9 | unified_strdate, | |
10 | str_to_int, | |
30bd1c16 | 11 | int_or_none, |
607841af YCH |
12 | float_or_none, |
13 | ISO639Utils, | |
402ca40c | 14 | determine_ext, |
1362bbbb S |
15 | ) |
16 | ||
17 | ||
2c3b9f35 | 18 | class AdobeTVBaseIE(InfoExtractor): |
19 | _API_BASE_URL = 'http://tv.adobe.com/api/v4/' | |
20 | ||
21 | ||
22 | class AdobeTVIE(AdobeTVBaseIE): | |
30bd1c16 | 23 | _VALID_URL = r'https?://tv\.adobe\.com/(?:(?P<language>fr|de|es|jp)/)?watch/(?P<show_urlname>[^/]+)/(?P<id>[^/]+)' |
1362bbbb S |
24 | |
25 | _TEST = { | |
26 | 'url': 'http://tv.adobe.com/watch/the-complete-picture-with-julieanne-kost/quick-tip-how-to-draw-a-circle-around-an-object-in-photoshop/', | |
27 | 'md5': '9bc5727bcdd55251f35ad311ca74fa1e', | |
28 | 'info_dict': { | |
30bd1c16 | 29 | 'id': '10981', |
1362bbbb S |
30 | 'ext': 'mp4', |
31 | 'title': 'Quick Tip - How to Draw a Circle Around an Object in Photoshop', | |
32 | 'description': 'md5:99ec318dc909d7ba2a1f2b038f7d2311', | |
ec85ded8 | 33 | 'thumbnail': r're:https?://.*\.jpg$', |
1362bbbb S |
34 | 'upload_date': '20110914', |
35 | 'duration': 60, | |
36 | 'view_count': int, | |
37 | }, | |
38 | } | |
39 | ||
40 | def _real_extract(self, url): | |
30bd1c16 | 41 | language, show_urlname, urlname = re.match(self._VALID_URL, url).groups() |
42 | if not language: | |
43 | language = 'en' | |
1362bbbb | 44 | |
30bd1c16 | 45 | video_data = self._download_json( |
2c3b9f35 | 46 | self._API_BASE_URL + 'episode/get/?language=%s&show_urlname=%s&urlname=%s&disclosure=standard' % (language, show_urlname, urlname), |
30bd1c16 | 47 | urlname)['data'][0] |
1362bbbb S |
48 | |
49 | formats = [{ | |
30bd1c16 | 50 | 'url': source['url'], |
51 | 'format_id': source.get('quality_level') or source['url'].split('-')[-1].split('.')[0] or None, | |
52 | 'width': int_or_none(source.get('width')), | |
53 | 'height': int_or_none(source.get('height')), | |
54 | 'tbr': int_or_none(source.get('video_data_rate')), | |
55 | } for source in video_data['videos']] | |
1362bbbb S |
56 | self._sort_formats(formats) |
57 | ||
58 | return { | |
7079f8ff | 59 | 'id': compat_str(video_data['id']), |
30bd1c16 | 60 | 'title': video_data['title'], |
61 | 'description': video_data.get('description'), | |
62 | 'thumbnail': video_data.get('thumbnail'), | |
63 | 'upload_date': unified_strdate(video_data.get('start_date')), | |
64 | 'duration': parse_duration(video_data.get('duration')), | |
65 | 'view_count': str_to_int(video_data.get('playcount')), | |
1362bbbb S |
66 | 'formats': formats, |
67 | } | |
607841af YCH |
68 | |
69 | ||
2c3b9f35 | 70 | class AdobeTVPlaylistBaseIE(AdobeTVBaseIE): |
9a605c88 | 71 | def _parse_page_data(self, page_data): |
72 | return [self.url_result(self._get_element_url(element_data)) for element_data in page_data] | |
73 | ||
74 | def _extract_playlist_entries(self, url, display_id): | |
75 | page = self._download_json(url, display_id) | |
76 | entries = self._parse_page_data(page['data']) | |
77 | for page_num in range(2, page['paging']['pages'] + 1): | |
78 | entries.extend(self._parse_page_data( | |
79 | self._download_json(url + '&page=%d' % page_num, display_id)['data'])) | |
80 | return entries | |
81 | ||
82 | ||
83 | class AdobeTVShowIE(AdobeTVPlaylistBaseIE): | |
84 | _VALID_URL = r'https?://tv\.adobe\.com/(?:(?P<language>fr|de|es|jp)/)?show/(?P<id>[^/]+)' | |
85 | ||
86 | _TEST = { | |
87 | 'url': 'http://tv.adobe.com/show/the-complete-picture-with-julieanne-kost', | |
88 | 'info_dict': { | |
89 | 'id': '36', | |
90 | 'title': 'The Complete Picture with Julieanne Kost', | |
91 | 'description': 'md5:fa50867102dcd1aa0ddf2ab039311b27', | |
92 | }, | |
93 | 'playlist_mincount': 136, | |
94 | } | |
95 | ||
96 | def _get_element_url(self, element_data): | |
97 | return element_data['urls'][0] | |
98 | ||
99 | def _real_extract(self, url): | |
100 | language, show_urlname = re.match(self._VALID_URL, url).groups() | |
101 | if not language: | |
102 | language = 'en' | |
103 | query = 'language=%s&show_urlname=%s' % (language, show_urlname) | |
104 | ||
2c3b9f35 | 105 | show_data = self._download_json(self._API_BASE_URL + 'show/get/?%s' % query, show_urlname)['data'][0] |
9a605c88 | 106 | |
107 | return self.playlist_result( | |
2c3b9f35 | 108 | self._extract_playlist_entries(self._API_BASE_URL + 'episode/?%s' % query, show_urlname), |
7079f8ff | 109 | compat_str(show_data['id']), |
9a605c88 | 110 | show_data['show_name'], |
111 | show_data['show_description']) | |
112 | ||
113 | ||
114 | class AdobeTVChannelIE(AdobeTVPlaylistBaseIE): | |
115 | _VALID_URL = r'https?://tv\.adobe\.com/(?:(?P<language>fr|de|es|jp)/)?channel/(?P<id>[^/]+)(?:/(?P<category_urlname>[^/]+))?' | |
116 | ||
117 | _TEST = { | |
118 | 'url': 'http://tv.adobe.com/channel/development', | |
119 | 'info_dict': { | |
120 | 'id': 'development', | |
121 | }, | |
122 | 'playlist_mincount': 96, | |
123 | } | |
124 | ||
125 | def _get_element_url(self, element_data): | |
126 | return element_data['url'] | |
127 | ||
128 | def _real_extract(self, url): | |
129 | language, channel_urlname, category_urlname = re.match(self._VALID_URL, url).groups() | |
130 | if not language: | |
131 | language = 'en' | |
132 | query = 'language=%s&channel_urlname=%s' % (language, channel_urlname) | |
133 | if category_urlname: | |
134 | query += '&category_urlname=%s' % category_urlname | |
135 | ||
136 | return self.playlist_result( | |
2c3b9f35 | 137 | self._extract_playlist_entries(self._API_BASE_URL + 'show/?%s' % query, channel_urlname), |
9a605c88 | 138 | channel_urlname) |
139 | ||
140 | ||
607841af YCH |
141 | class AdobeTVVideoIE(InfoExtractor): |
142 | _VALID_URL = r'https?://video\.tv\.adobe\.com/v/(?P<id>\d+)' | |
143 | ||
144 | _TEST = { | |
a5158f38 | 145 | # From https://helpx.adobe.com/acrobat/how-to/new-experience-acrobat-dc.html?set=acrobat--get-started--essential-beginners |
607841af YCH |
146 | 'url': 'https://video.tv.adobe.com/v/2456/', |
147 | 'md5': '43662b577c018ad707a63766462b1e87', | |
148 | 'info_dict': { | |
149 | 'id': '2456', | |
150 | 'ext': 'mp4', | |
151 | 'title': 'New experience with Acrobat DC', | |
152 | 'description': 'New experience with Acrobat DC', | |
153 | 'duration': 248.667, | |
154 | }, | |
155 | } | |
156 | ||
157 | def _real_extract(self, url): | |
158 | video_id = self._match_id(url) | |
26264cb0 YCH |
159 | webpage = self._download_webpage(url, video_id) |
160 | ||
161 | video_data = self._parse_json(self._search_regex( | |
162 | r'var\s+bridge\s*=\s*([^;]+);', webpage, 'bridged data'), video_id) | |
607841af YCH |
163 | |
164 | formats = [{ | |
402ca40c | 165 | 'format_id': '%s-%s' % (determine_ext(source['src']), source.get('height')), |
607841af | 166 | 'url': source['src'], |
402ca40c | 167 | 'width': int_or_none(source.get('width')), |
168 | 'height': int_or_none(source.get('height')), | |
169 | 'tbr': int_or_none(source.get('bitrate')), | |
170 | } for source in video_data['sources']] | |
171 | self._sort_formats(formats) | |
607841af YCH |
172 | |
173 | # For both metadata and downloaded files the duration varies among | |
174 | # formats. I just pick the max one | |
175 | duration = max(filter(None, [ | |
176 | float_or_none(source.get('duration'), scale=1000) | |
402ca40c | 177 | for source in video_data['sources']])) |
607841af YCH |
178 | |
179 | subtitles = {} | |
402ca40c | 180 | for translation in video_data.get('translations', []): |
607841af YCH |
181 | lang_id = translation.get('language_w3c') or ISO639Utils.long2short(translation['language_medium']) |
182 | if lang_id not in subtitles: | |
183 | subtitles[lang_id] = [] | |
184 | subtitles[lang_id].append({ | |
185 | 'url': translation['vttPath'], | |
186 | 'ext': 'vtt', | |
187 | }) | |
188 | ||
189 | return { | |
190 | 'id': video_id, | |
191 | 'formats': formats, | |
402ca40c | 192 | 'title': video_data['title'], |
193 | 'description': video_data.get('description'), | |
194 | 'thumbnail': video_data['video'].get('poster'), | |
607841af YCH |
195 | 'duration': duration, |
196 | 'subtitles': subtitles, | |
197 | } |