]>
Commit | Line | Data |
---|---|---|
80f772c2 | 1 | # coding: utf-8 |
b54a2da4 YCH |
2 | from __future__ import unicode_literals, division |
3 | ||
b9f5a412 S |
4 | import re |
5 | ||
80f772c2 | 6 | from .common import InfoExtractor |
7d34016f S |
7 | from ..compat import ( |
8 | compat_str, | |
9 | compat_HTTPError, | |
10 | ) | |
b9f5a412 S |
11 | from ..utils import ( |
12 | determine_ext, | |
13 | float_or_none, | |
14 | int_or_none, | |
15 | parse_age_limit, | |
16 | parse_duration, | |
7d34016f | 17 | ExtractorError |
b9f5a412 | 18 | ) |
80f772c2 | 19 | |
20 | ||
21 | class CrackleIE(InfoExtractor): | |
23b35a63 | 22 | _VALID_URL = r'(?:crackle:|https?://(?:(?:www|m)\.)?crackle\.com/(?:playlist/\d+/|(?:[^/]+/)+))(?P<id>\d+)' |
80f772c2 | 23 | _TEST = { |
7d34016f | 24 | # geo restricted to CA |
b9f5a412 | 25 | 'url': 'https://www.crackle.com/andromeda/2502343', |
80f772c2 | 26 | 'info_dict': { |
b9f5a412 | 27 | 'id': '2502343', |
80f772c2 | 28 | 'ext': 'mp4', |
b9f5a412 S |
29 | 'title': 'Under The Night', |
30 | 'description': 'md5:d2b8ca816579ae8a7bf28bfff8cefc8a', | |
31 | 'duration': 2583, | |
32 | 'view_count': int, | |
33 | 'average_rating': 0, | |
34 | 'age_limit': 14, | |
35 | 'genre': 'Action, Sci-Fi', | |
36 | 'creator': 'Allan Kroeker', | |
37 | 'artist': 'Keith Hamilton Cobb, Kevin Sorbo, Lisa Ryder, Lexa Doig, Robert Hewitt Wolfe', | |
38 | 'release_year': 2000, | |
39 | 'series': 'Andromeda', | |
40 | 'episode': 'Under The Night', | |
41 | 'season_number': 1, | |
42 | 'episode_number': 1, | |
80f772c2 | 43 | }, |
44 | 'params': { | |
45 | # m3u8 download | |
46 | 'skip_download': True, | |
47 | } | |
48 | } | |
49 | ||
80f772c2 | 50 | def _real_extract(self, url): |
51 | video_id = self._match_id(url) | |
b54a2da4 | 52 | |
7d34016f S |
53 | country_code = self._downloader.params.get('geo_bypass_country', None) |
54 | countries = [country_code] if country_code else ( | |
55 | 'US', 'AU', 'CA', 'AS', 'FM', 'GU', 'MP', 'PR', 'PW', 'MH', 'VI') | |
b9f5a412 | 56 | |
7d34016f | 57 | last_e = None |
b9f5a412 | 58 | |
7d34016f S |
59 | for country in countries: |
60 | try: | |
61 | media = self._download_json( | |
62 | 'https://web-api-us.crackle.com/Service.svc/details/media/%s/%s' | |
63 | % (video_id, country), video_id, | |
64 | 'Downloading media JSON as %s' % country, | |
65 | 'Unable to download media JSON', query={ | |
66 | 'disableProtocols': 'true', | |
67 | 'format': 'json' | |
68 | }) | |
69 | except ExtractorError as e: | |
70 | # 401 means geo restriction, trying next country | |
71 | if isinstance(e.cause, compat_HTTPError) and e.cause.code == 401: | |
72 | last_e = e | |
b9f5a412 | 73 | continue |
7d34016f S |
74 | raise |
75 | ||
76 | media_urls = media.get('MediaURLs') | |
77 | if not media_urls or not isinstance(media_urls, list): | |
78 | continue | |
79 | ||
80 | title = media['Title'] | |
81 | ||
82 | formats = [] | |
83 | for e in media['MediaURLs']: | |
84 | if e.get('UseDRM') is True: | |
b9f5a412 | 85 | continue |
7d34016f S |
86 | format_url = e.get('Path') |
87 | if not format_url or not isinstance(format_url, compat_str): | |
b9f5a412 | 88 | continue |
7d34016f S |
89 | ext = determine_ext(format_url) |
90 | if ext == 'm3u8': | |
91 | formats.extend(self._extract_m3u8_formats( | |
92 | format_url, video_id, 'mp4', entry_protocol='m3u8_native', | |
93 | m3u8_id='hls', fatal=False)) | |
94 | elif ext == 'mpd': | |
95 | formats.extend(self._extract_mpd_formats( | |
96 | format_url, video_id, mpd_id='dash', fatal=False)) | |
97 | self._sort_formats(formats) | |
98 | ||
99 | description = media.get('Description') | |
100 | duration = int_or_none(media.get( | |
101 | 'DurationInSeconds')) or parse_duration(media.get('Duration')) | |
102 | view_count = int_or_none(media.get('CountViews')) | |
103 | average_rating = float_or_none(media.get('UserRating')) | |
104 | age_limit = parse_age_limit(media.get('Rating')) | |
105 | genre = media.get('Genre') | |
106 | release_year = int_or_none(media.get('ReleaseYear')) | |
107 | creator = media.get('Directors') | |
108 | artist = media.get('Cast') | |
109 | ||
110 | if media.get('MediaTypeDisplayValue') == 'Full Episode': | |
111 | series = media.get('ShowName') | |
112 | episode = title | |
113 | season_number = int_or_none(media.get('Season')) | |
114 | episode_number = int_or_none(media.get('Episode')) | |
115 | else: | |
116 | series = episode = season_number = episode_number = None | |
117 | ||
118 | subtitles = {} | |
119 | cc_files = media.get('ClosedCaptionFiles') | |
120 | if isinstance(cc_files, list): | |
121 | for cc_file in cc_files: | |
122 | if not isinstance(cc_file, dict): | |
123 | continue | |
124 | cc_url = cc_file.get('Path') | |
125 | if not cc_url or not isinstance(cc_url, compat_str): | |
126 | continue | |
127 | lang = cc_file.get('Locale') or 'en' | |
128 | subtitles.setdefault(lang, []).append({'url': cc_url}) | |
129 | ||
130 | thumbnails = [] | |
131 | images = media.get('Images') | |
132 | if isinstance(images, list): | |
133 | for image_key, image_url in images.items(): | |
134 | mobj = re.search(r'Img_(\d+)[xX](\d+)', image_key) | |
135 | if not mobj: | |
136 | continue | |
137 | thumbnails.append({ | |
138 | 'url': image_url, | |
139 | 'width': int(mobj.group(1)), | |
140 | 'height': int(mobj.group(2)), | |
141 | }) | |
142 | ||
143 | return { | |
144 | 'id': video_id, | |
145 | 'title': title, | |
146 | 'description': description, | |
147 | 'duration': duration, | |
148 | 'view_count': view_count, | |
149 | 'average_rating': average_rating, | |
150 | 'age_limit': age_limit, | |
151 | 'genre': genre, | |
152 | 'creator': creator, | |
153 | 'artist': artist, | |
154 | 'release_year': release_year, | |
155 | 'series': series, | |
156 | 'episode': episode, | |
157 | 'season_number': season_number, | |
158 | 'episode_number': episode_number, | |
159 | 'thumbnails': thumbnails, | |
160 | 'subtitles': subtitles, | |
161 | 'formats': formats, | |
162 | } | |
163 | ||
164 | raise last_e |