]>
Commit | Line | Data |
---|---|---|
933a5b37 JMF |
1 | from __future__ import unicode_literals |
2 | ||
933a5b37 | 3 | from .common import InfoExtractor |
5e1912cf | 4 | from ..compat import ( |
8f5c0218 | 5 | compat_urllib_parse, |
26669ea3 | 6 | compat_parse_qs, |
7 | compat_urllib_parse_urlparse, | |
8 | compat_urlparse, | |
5e1912cf PH |
9 | ) |
10 | from ..utils import ( | |
57b8d84c | 11 | ExtractorError, |
26669ea3 | 12 | parse_duration, |
13 | replace_extension, | |
933a5b37 JMF |
14 | ) |
15 | ||
16 | ||
17 | class FiveMinIE(InfoExtractor): | |
18 | IE_NAME = '5min' | |
19 | _VALID_URL = r'''(?x) | |
5e1912cf | 20 | (?:https?://[^/]*?5min\.com/Scripts/PlayerSeed\.js\?(?:.*?&)?playList=| |
d5c69f1d | 21 | https?://(?:(?:massively|www)\.)?joystiq\.com/video/| |
933a5b37 JMF |
22 | 5min:) |
23 | (?P<id>\d+) | |
24 | ''' | |
25 | ||
8f5c0218 JMF |
26 | _TESTS = [ |
27 | { | |
28 | # From http://www.engadget.com/2013/11/15/ipad-mini-retina-display-review/ | |
29 | 'url': 'http://pshared.5min.com/Scripts/PlayerSeed.js?sid=281&width=560&height=345&playList=518013791', | |
30 | 'md5': '4f7b0b79bf1a470e5004f7112385941d', | |
31 | 'info_dict': { | |
32 | 'id': '518013791', | |
33 | 'ext': 'mp4', | |
34 | 'title': 'iPad Mini with Retina Display Review', | |
26669ea3 | 35 | 'duration': 177, |
8f5c0218 | 36 | }, |
933a5b37 | 37 | }, |
8f5c0218 JMF |
38 | { |
39 | # From http://on.aol.com/video/how-to-make-a-next-level-fruit-salad-518086247 | |
40 | 'url': '5min:518086247', | |
41 | 'md5': 'e539a9dd682c288ef5a498898009f69e', | |
42 | 'info_dict': { | |
43 | 'id': '518086247', | |
44 | 'ext': 'mp4', | |
45 | 'title': 'How to Make a Next-Level Fruit Salad', | |
26669ea3 | 46 | 'duration': 184, |
8f5c0218 JMF |
47 | }, |
48 | }, | |
49 | ] | |
26669ea3 | 50 | _ERRORS = { |
51 | 'ErrorVideoNotExist': 'We\'re sorry, but the video you are trying to watch does not exist.', | |
52 | 'ErrorVideoNoLongerAvailable': 'We\'re sorry, but the video you are trying to watch is no longer available.', | |
53 | 'ErrorVideoRejected': 'We\'re sorry, but the video you are trying to watch has been removed.', | |
54 | 'ErrorVideoUserNotGeo': 'We\'re sorry, but the video you are trying to watch cannot be viewed from your current location.', | |
55 | 'ErrorVideoLibraryRestriction': 'We\'re sorry, but the video you are trying to watch is currently unavailable for viewing at this domain.', | |
56 | 'ErrorExposurePermission': 'We\'re sorry, but the video you are trying to watch is currently unavailable for viewing at this domain.', | |
57 | } | |
58 | _QUALITIES = { | |
59 | 1: { | |
60 | 'width': 640, | |
61 | 'height': 360, | |
62 | }, | |
63 | 2: { | |
64 | 'width': 854, | |
65 | 'height': 480, | |
66 | }, | |
67 | 4: { | |
68 | 'width': 1280, | |
69 | 'height': 720, | |
70 | }, | |
71 | 8: { | |
72 | 'width': 1920, | |
73 | 'height': 1080, | |
74 | }, | |
75 | 16: { | |
76 | 'width': 640, | |
77 | 'height': 360, | |
78 | }, | |
79 | 32: { | |
80 | 'width': 854, | |
81 | 'height': 480, | |
82 | }, | |
83 | 64: { | |
84 | 'width': 1280, | |
85 | 'height': 720, | |
86 | }, | |
87 | 128: { | |
88 | 'width': 640, | |
89 | 'height': 360, | |
90 | }, | |
91 | } | |
933a5b37 | 92 | |
933a5b37 | 93 | def _real_extract(self, url): |
5e1912cf | 94 | video_id = self._match_id(url) |
8f5c0218 JMF |
95 | embed_url = 'https://embed.5min.com/playerseed/?playList=%s' % video_id |
96 | embed_page = self._download_webpage(embed_url, video_id, | |
9e1a5b84 | 97 | 'Downloading embed page') |
8f5c0218 JMF |
98 | sid = self._search_regex(r'sid=(\d+)', embed_page, 'sid') |
99 | query = compat_urllib_parse.urlencode({ | |
100 | 'func': 'GetResults', | |
101 | 'playlist': video_id, | |
102 | 'sid': sid, | |
103 | 'isPlayerSeed': 'true', | |
104 | 'url': embed_url, | |
105 | }) | |
57b8d84c | 106 | response = self._download_json( |
8f5c0218 | 107 | 'https://syn.5min.com/handlers/SenseHandler.ashx?' + query, |
57b8d84c JMF |
108 | video_id) |
109 | if not response['success']: | |
26669ea3 | 110 | raise ExtractorError( |
111 | '%s said: %s' % ( | |
112 | self.IE_NAME, | |
113 | self._ERRORS.get(response['errorMessage'], response['errorMessage'])), | |
114 | expected=True) | |
57b8d84c | 115 | info = response['binding'][0] |
933a5b37 | 116 | |
933a5b37 | 117 | formats = [] |
26669ea3 | 118 | parsed_video_url = compat_urllib_parse_urlparse(compat_parse_qs( |
119 | compat_urllib_parse_urlparse(info['EmbededURL']).query)['videoUrl'][0]) | |
120 | for rendition in info['Renditions']: | |
121 | if rendition['RenditionType'] == 'm3u8': | |
122 | formats.extend(self._extract_m3u8_formats(rendition['Url'], video_id, m3u8_id='hls')) | |
123 | elif rendition['RenditionType'] == 'aac': | |
124 | continue | |
125 | else: | |
126 | rendition_url = compat_urlparse.urlunparse(parsed_video_url._replace(path=replace_extension(parsed_video_url.path.replace('//', '/%s/' % rendition['ID']), rendition['RenditionType']))) | |
127 | quality = self._QUALITIES.get(rendition['ID'], {}) | |
933a5b37 | 128 | formats.append({ |
26669ea3 | 129 | 'format_id': '%s-%d' % (rendition['RenditionType'], rendition['ID']), |
130 | 'url': rendition_url, | |
131 | 'width': quality.get('width'), | |
132 | 'height': quality.get('height'), | |
933a5b37 | 133 | }) |
26669ea3 | 134 | self._sort_formats(formats) |
933a5b37 JMF |
135 | |
136 | return { | |
137 | 'id': video_id, | |
138 | 'title': info['Title'], | |
26669ea3 | 139 | 'thumbnail': info.get('ThumbURL'), |
140 | 'duration': parse_duration(info.get('Duration')), | |
933a5b37 JMF |
141 | 'formats': formats, |
142 | } |