]>
Commit | Line | Data |
---|---|---|
4cd70099 | 1 | # coding: utf-8 |
2 | from __future__ import unicode_literals | |
3 | ||
4 | import re | |
5 | ||
6 | from .common import InfoExtractor | |
ca5ed022 | 7 | from ..compat import compat_str |
4cd70099 | 8 | from ..utils import ( |
9 | xpath_text, | |
10 | xpath_element, | |
11 | int_or_none, | |
12 | parse_duration, | |
13 | ) | |
14 | ||
15 | ||
71cdcb23 | 16 | class HBOBaseIE(InfoExtractor): |
4cd70099 | 17 | _FORMATS_INFO = { |
ca5ed022 RA |
18 | 'pro7': { |
19 | 'width': 1280, | |
20 | 'height': 720, | |
21 | }, | |
4cd70099 | 22 | '1920': { |
23 | 'width': 1280, | |
24 | 'height': 720, | |
25 | }, | |
ca5ed022 RA |
26 | 'pro6': { |
27 | 'width': 768, | |
28 | 'height': 432, | |
29 | }, | |
4cd70099 | 30 | '640': { |
31 | 'width': 768, | |
32 | 'height': 432, | |
33 | }, | |
ca5ed022 RA |
34 | 'pro5': { |
35 | 'width': 640, | |
36 | 'height': 360, | |
37 | }, | |
4cd70099 | 38 | 'highwifi': { |
39 | 'width': 640, | |
40 | 'height': 360, | |
41 | }, | |
42 | 'high3g': { | |
43 | 'width': 640, | |
44 | 'height': 360, | |
45 | }, | |
46 | 'medwifi': { | |
47 | 'width': 400, | |
48 | 'height': 224, | |
49 | }, | |
50 | 'med3g': { | |
51 | 'width': 400, | |
52 | 'height': 224, | |
53 | }, | |
54 | } | |
55 | ||
71cdcb23 | 56 | def _extract_from_id(self, video_id): |
4cd70099 | 57 | video_data = self._download_xml( |
58 | 'http://render.lv3.hbo.com/data/content/global/videos/data/%s.xml' % video_id, video_id) | |
59 | title = xpath_text(video_data, 'title', 'title', True) | |
60 | ||
61 | formats = [] | |
62 | for source in xpath_element(video_data, 'videos', 'sources', True): | |
63 | if source.tag == 'size': | |
64 | path = xpath_text(source, './/path') | |
65 | if not path: | |
66 | continue | |
67 | width = source.attrib.get('width') | |
68 | format_info = self._FORMATS_INFO.get(width, {}) | |
69 | height = format_info.get('height') | |
70 | fmt = { | |
71 | 'url': path, | |
72 | 'format_id': 'http%s' % ('-%dp' % height if height else ''), | |
73 | 'width': format_info.get('width'), | |
74 | 'height': height, | |
75 | } | |
76 | rtmp = re.search(r'^(?P<url>rtmpe?://[^/]+/(?P<app>.+))/(?P<playpath>mp4:.+)$', path) | |
77 | if rtmp: | |
78 | fmt.update({ | |
79 | 'url': rtmp.group('url'), | |
80 | 'play_path': rtmp.group('playpath'), | |
81 | 'app': rtmp.group('app'), | |
82 | 'ext': 'flv', | |
83 | 'format_id': fmt['format_id'].replace('http', 'rtmp'), | |
84 | }) | |
85 | formats.append(fmt) | |
86 | else: | |
87 | video_url = source.text | |
88 | if not video_url: | |
89 | continue | |
90 | if source.tag == 'tarball': | |
91 | formats.extend(self._extract_m3u8_formats( | |
92 | video_url.replace('.tar', '/base_index_w8.m3u8'), | |
93 | video_id, 'mp4', 'm3u8_native', m3u8_id='hls', fatal=False)) | |
ca5ed022 RA |
94 | elif source.tag == 'hls': |
95 | # #EXT-X-BYTERANGE is not supported by native hls downloader | |
96 | # and ffmpeg (#10955) | |
97 | # formats.extend(self._extract_m3u8_formats( | |
98 | # video_url.replace('.tar', '/base_index.m3u8'), | |
99 | # video_id, 'mp4', 'm3u8_native', m3u8_id='hls', fatal=False)) | |
100 | continue | |
101 | elif source.tag == 'dash': | |
102 | formats.extend(self._extract_mpd_formats( | |
103 | video_url.replace('.tar', '/manifest.mpd'), | |
104 | video_id, mpd_id='dash', fatal=False)) | |
4cd70099 | 105 | else: |
106 | format_info = self._FORMATS_INFO.get(source.tag, {}) | |
107 | formats.append({ | |
108 | 'format_id': 'http-%s' % source.tag, | |
109 | 'url': video_url, | |
110 | 'width': format_info.get('width'), | |
111 | 'height': format_info.get('height'), | |
112 | }) | |
113 | self._sort_formats(formats, ('width', 'height', 'tbr', 'format_id')) | |
114 | ||
115 | thumbnails = [] | |
116 | card_sizes = xpath_element(video_data, 'titleCardSizes') | |
117 | if card_sizes is not None: | |
118 | for size in card_sizes: | |
119 | path = xpath_text(size, 'path') | |
120 | if not path: | |
121 | continue | |
122 | width = int_or_none(size.get('width')) | |
123 | thumbnails.append({ | |
124 | 'id': width, | |
125 | 'url': path, | |
126 | 'width': width, | |
127 | }) | |
128 | ||
129 | return { | |
130 | 'id': video_id, | |
131 | 'title': title, | |
71cdcb23 | 132 | 'duration': parse_duration(xpath_text(video_data, 'duration/tv14')), |
4cd70099 | 133 | 'formats': formats, |
134 | 'thumbnails': thumbnails, | |
135 | } | |
71cdcb23 YCH |
136 | |
137 | ||
138 | class HBOIE(HBOBaseIE): | |
ca5ed022 | 139 | IE_NAME = 'hbo' |
71cdcb23 YCH |
140 | _VALID_URL = r'https?://(?:www\.)?hbo\.com/video/video\.html\?.*vid=(?P<id>[0-9]+)' |
141 | _TEST = { | |
142 | 'url': 'http://www.hbo.com/video/video.html?autoplay=true&g=u&vid=1437839', | |
ca5ed022 | 143 | 'md5': '2c6a6bc1222c7e91cb3334dad1746e5a', |
71cdcb23 YCH |
144 | 'info_dict': { |
145 | 'id': '1437839', | |
146 | 'ext': 'mp4', | |
147 | 'title': 'Ep. 64 Clip: Encryption', | |
ec85ded8 | 148 | 'thumbnail': r're:https?://.*\.jpg$', |
71cdcb23 YCH |
149 | 'duration': 1072, |
150 | } | |
151 | } | |
152 | ||
153 | def _real_extract(self, url): | |
154 | video_id = self._match_id(url) | |
155 | return self._extract_from_id(video_id) | |
156 | ||
157 | ||
158 | class HBOEpisodeIE(HBOBaseIE): | |
ca5ed022 RA |
159 | IE_NAME = 'hbo:episode' |
160 | _VALID_URL = r'https?://(?:www\.)?hbo\.com/(?P<path>(?!video)(?:(?:[^/]+/)+video|watch-free-episodes)/(?P<id>[0-9a-z-]+))(?:\.html)?' | |
71cdcb23 YCH |
161 | |
162 | _TESTS = [{ | |
163 | 'url': 'http://www.hbo.com/girls/episodes/5/52-i-love-you-baby/video/ep-52-inside-the-episode.html?autoplay=true', | |
ca5ed022 | 164 | 'md5': '61ead79b9c0dfa8d3d4b07ef4ac556fb', |
71cdcb23 YCH |
165 | 'info_dict': { |
166 | 'id': '1439518', | |
27b8d2ee | 167 | 'display_id': 'ep-52-inside-the-episode', |
71cdcb23 YCH |
168 | 'ext': 'mp4', |
169 | 'title': 'Ep. 52: Inside the Episode', | |
ec85ded8 | 170 | 'thumbnail': r're:https?://.*\.jpg$', |
71cdcb23 YCH |
171 | 'duration': 240, |
172 | }, | |
27b8d2ee YCH |
173 | }, { |
174 | 'url': 'http://www.hbo.com/game-of-thrones/about/video/season-5-invitation-to-the-set.html?autoplay=true', | |
175 | 'only_matching': True, | |
ca5ed022 RA |
176 | }, { |
177 | 'url': 'http://www.hbo.com/watch-free-episodes/last-week-tonight-with-john-oliver', | |
178 | 'only_matching': True, | |
71cdcb23 YCH |
179 | }] |
180 | ||
181 | def _real_extract(self, url): | |
ca5ed022 | 182 | path, display_id = re.match(self._VALID_URL, url).groups() |
71cdcb23 | 183 | |
ca5ed022 RA |
184 | content = self._download_json( |
185 | 'http://www.hbo.com/api/content/' + path, display_id)['content'] | |
71cdcb23 | 186 | |
ca5ed022 RA |
187 | video_id = compat_str((content.get('parsed', {}).get( |
188 | 'common:FullBleedVideo', {}) or content['selectedEpisode'])['videoId']) | |
71cdcb23 | 189 | |
27b8d2ee YCH |
190 | info_dict = self._extract_from_id(video_id) |
191 | info_dict['display_id'] = display_id | |
192 | ||
193 | return info_dict |