| 
									
										
										
										
											2016-02-10 22:16:21 +01:00
										 |  |  | # coding: utf-8 | 
					
						
							| 
									
										
										
										
											2016-08-25 22:22:31 +08:00
										 |  |  | from __future__ import unicode_literals, division | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-23 23:53:18 +07:00
										 |  |  | import re | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-10 22:16:21 +01:00
										 |  |  | from .common import InfoExtractor | 
					
						
							| 
									
										
										
										
											2018-03-24 01:49:50 +07:00
										 |  |  | from ..compat import ( | 
					
						
							|  |  |  |     compat_str, | 
					
						
							|  |  |  |     compat_HTTPError, | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2018-03-23 23:53:18 +07:00
										 |  |  | from ..utils import ( | 
					
						
							|  |  |  |     determine_ext, | 
					
						
							|  |  |  |     float_or_none, | 
					
						
							|  |  |  |     int_or_none, | 
					
						
							|  |  |  |     parse_age_limit, | 
					
						
							|  |  |  |     parse_duration, | 
					
						
							| 
									
										
										
										
											2018-03-24 01:49:50 +07:00
										 |  |  |     ExtractorError | 
					
						
							| 
									
										
										
										
											2018-03-23 23:53:18 +07:00
										 |  |  | ) | 
					
						
							| 
									
										
										
										
											2016-02-10 22:16:21 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class CrackleIE(InfoExtractor): | 
					
						
							| 
									
										
										
										
											2017-01-24 16:55:07 +01:00
										 |  |  |     _VALID_URL = r'(?:crackle:|https?://(?:(?:www|m)\.)?crackle\.com/(?:playlist/\d+/|(?:[^/]+/)+))(?P<id>\d+)' | 
					
						
							| 
									
										
										
										
											2016-02-10 22:16:21 +01:00
										 |  |  |     _TEST = { | 
					
						
							| 
									
										
										
										
											2018-03-24 01:49:50 +07:00
										 |  |  |         # geo restricted to CA | 
					
						
							| 
									
										
										
										
											2018-03-23 23:53:18 +07:00
										 |  |  |         'url': 'https://www.crackle.com/andromeda/2502343', | 
					
						
							| 
									
										
										
										
											2016-02-10 22:16:21 +01:00
										 |  |  |         'info_dict': { | 
					
						
							| 
									
										
										
										
											2018-03-23 23:53:18 +07:00
										 |  |  |             'id': '2502343', | 
					
						
							| 
									
										
										
										
											2016-02-10 22:16:21 +01:00
										 |  |  |             'ext': 'mp4', | 
					
						
							| 
									
										
										
										
											2018-03-23 23:53:18 +07:00
										 |  |  |             'title': 'Under The Night', | 
					
						
							|  |  |  |             'description': 'md5:d2b8ca816579ae8a7bf28bfff8cefc8a', | 
					
						
							|  |  |  |             'duration': 2583, | 
					
						
							|  |  |  |             'view_count': int, | 
					
						
							|  |  |  |             'average_rating': 0, | 
					
						
							|  |  |  |             'age_limit': 14, | 
					
						
							|  |  |  |             'genre': 'Action, Sci-Fi', | 
					
						
							|  |  |  |             'creator': 'Allan Kroeker', | 
					
						
							|  |  |  |             'artist': 'Keith Hamilton Cobb, Kevin Sorbo, Lisa Ryder, Lexa Doig, Robert Hewitt Wolfe', | 
					
						
							|  |  |  |             'release_year': 2000, | 
					
						
							|  |  |  |             'series': 'Andromeda', | 
					
						
							|  |  |  |             'episode': 'Under The Night', | 
					
						
							|  |  |  |             'season_number': 1, | 
					
						
							|  |  |  |             'episode_number': 1, | 
					
						
							| 
									
										
										
										
											2016-02-10 22:16:21 +01:00
										 |  |  |         }, | 
					
						
							|  |  |  |         'params': { | 
					
						
							|  |  |  |             # m3u8 download | 
					
						
							|  |  |  |             'skip_download': True, | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _real_extract(self, url): | 
					
						
							|  |  |  |         video_id = self._match_id(url) | 
					
						
							| 
									
										
										
										
											2016-08-25 22:22:31 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-24 01:49:50 +07:00
										 |  |  |         country_code = self._downloader.params.get('geo_bypass_country', None) | 
					
						
							|  |  |  |         countries = [country_code] if country_code else ( | 
					
						
							|  |  |  |             'US', 'AU', 'CA', 'AS', 'FM', 'GU', 'MP', 'PR', 'PW', 'MH', 'VI') | 
					
						
							| 
									
										
										
										
											2018-03-23 23:53:18 +07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-24 01:49:50 +07:00
										 |  |  |         last_e = None | 
					
						
							| 
									
										
										
										
											2018-03-23 23:53:18 +07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-24 01:49:50 +07:00
										 |  |  |         for country in countries: | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 media = self._download_json( | 
					
						
							|  |  |  |                     'https://web-api-us.crackle.com/Service.svc/details/media/%s/%s' | 
					
						
							|  |  |  |                     % (video_id, country), video_id, | 
					
						
							|  |  |  |                     'Downloading media JSON as %s' % country, | 
					
						
							|  |  |  |                     'Unable to download media JSON', query={ | 
					
						
							|  |  |  |                         'disableProtocols': 'true', | 
					
						
							|  |  |  |                         'format': 'json' | 
					
						
							|  |  |  |                     }) | 
					
						
							|  |  |  |             except ExtractorError as e: | 
					
						
							|  |  |  |                 # 401 means geo restriction, trying next country | 
					
						
							|  |  |  |                 if isinstance(e.cause, compat_HTTPError) and e.cause.code == 401: | 
					
						
							|  |  |  |                     last_e = e | 
					
						
							| 
									
										
										
										
											2018-03-23 23:53:18 +07:00
										 |  |  |                     continue | 
					
						
							| 
									
										
										
										
											2018-03-24 01:49:50 +07:00
										 |  |  |                 raise | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             media_urls = media.get('MediaURLs') | 
					
						
							|  |  |  |             if not media_urls or not isinstance(media_urls, list): | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             title = media['Title'] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             formats = [] | 
					
						
							|  |  |  |             for e in media['MediaURLs']: | 
					
						
							|  |  |  |                 if e.get('UseDRM') is True: | 
					
						
							| 
									
										
										
										
											2018-03-23 23:53:18 +07:00
										 |  |  |                     continue | 
					
						
							| 
									
										
										
										
											2018-03-24 01:49:50 +07:00
										 |  |  |                 format_url = e.get('Path') | 
					
						
							|  |  |  |                 if not format_url or not isinstance(format_url, compat_str): | 
					
						
							| 
									
										
										
										
											2018-03-23 23:53:18 +07:00
										 |  |  |                     continue | 
					
						
							| 
									
										
										
										
											2018-03-24 01:49:50 +07:00
										 |  |  |                 ext = determine_ext(format_url) | 
					
						
							|  |  |  |                 if ext == 'm3u8': | 
					
						
							|  |  |  |                     formats.extend(self._extract_m3u8_formats( | 
					
						
							|  |  |  |                         format_url, video_id, 'mp4', entry_protocol='m3u8_native', | 
					
						
							|  |  |  |                         m3u8_id='hls', fatal=False)) | 
					
						
							|  |  |  |                 elif ext == 'mpd': | 
					
						
							|  |  |  |                     formats.extend(self._extract_mpd_formats( | 
					
						
							|  |  |  |                         format_url, video_id, mpd_id='dash', fatal=False)) | 
					
						
							|  |  |  |             self._sort_formats(formats) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             description = media.get('Description') | 
					
						
							|  |  |  |             duration = int_or_none(media.get( | 
					
						
							|  |  |  |                 'DurationInSeconds')) or parse_duration(media.get('Duration')) | 
					
						
							|  |  |  |             view_count = int_or_none(media.get('CountViews')) | 
					
						
							|  |  |  |             average_rating = float_or_none(media.get('UserRating')) | 
					
						
							|  |  |  |             age_limit = parse_age_limit(media.get('Rating')) | 
					
						
							|  |  |  |             genre = media.get('Genre') | 
					
						
							|  |  |  |             release_year = int_or_none(media.get('ReleaseYear')) | 
					
						
							|  |  |  |             creator = media.get('Directors') | 
					
						
							|  |  |  |             artist = media.get('Cast') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if media.get('MediaTypeDisplayValue') == 'Full Episode': | 
					
						
							|  |  |  |                 series = media.get('ShowName') | 
					
						
							|  |  |  |                 episode = title | 
					
						
							|  |  |  |                 season_number = int_or_none(media.get('Season')) | 
					
						
							|  |  |  |                 episode_number = int_or_none(media.get('Episode')) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 series = episode = season_number = episode_number = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             subtitles = {} | 
					
						
							|  |  |  |             cc_files = media.get('ClosedCaptionFiles') | 
					
						
							|  |  |  |             if isinstance(cc_files, list): | 
					
						
							|  |  |  |                 for cc_file in cc_files: | 
					
						
							|  |  |  |                     if not isinstance(cc_file, dict): | 
					
						
							|  |  |  |                         continue | 
					
						
							|  |  |  |                     cc_url = cc_file.get('Path') | 
					
						
							|  |  |  |                     if not cc_url or not isinstance(cc_url, compat_str): | 
					
						
							|  |  |  |                         continue | 
					
						
							|  |  |  |                     lang = cc_file.get('Locale') or 'en' | 
					
						
							|  |  |  |                     subtitles.setdefault(lang, []).append({'url': cc_url}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             thumbnails = [] | 
					
						
							|  |  |  |             images = media.get('Images') | 
					
						
							|  |  |  |             if isinstance(images, list): | 
					
						
							|  |  |  |                 for image_key, image_url in images.items(): | 
					
						
							|  |  |  |                     mobj = re.search(r'Img_(\d+)[xX](\d+)', image_key) | 
					
						
							|  |  |  |                     if not mobj: | 
					
						
							|  |  |  |                         continue | 
					
						
							|  |  |  |                     thumbnails.append({ | 
					
						
							|  |  |  |                         'url': image_url, | 
					
						
							|  |  |  |                         'width': int(mobj.group(1)), | 
					
						
							|  |  |  |                         'height': int(mobj.group(2)), | 
					
						
							|  |  |  |                     }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return { | 
					
						
							|  |  |  |                 'id': video_id, | 
					
						
							|  |  |  |                 'title': title, | 
					
						
							|  |  |  |                 'description': description, | 
					
						
							|  |  |  |                 'duration': duration, | 
					
						
							|  |  |  |                 'view_count': view_count, | 
					
						
							|  |  |  |                 'average_rating': average_rating, | 
					
						
							|  |  |  |                 'age_limit': age_limit, | 
					
						
							|  |  |  |                 'genre': genre, | 
					
						
							|  |  |  |                 'creator': creator, | 
					
						
							|  |  |  |                 'artist': artist, | 
					
						
							|  |  |  |                 'release_year': release_year, | 
					
						
							|  |  |  |                 'series': series, | 
					
						
							|  |  |  |                 'episode': episode, | 
					
						
							|  |  |  |                 'season_number': season_number, | 
					
						
							|  |  |  |                 'episode_number': episode_number, | 
					
						
							|  |  |  |                 'thumbnails': thumbnails, | 
					
						
							|  |  |  |                 'subtitles': subtitles, | 
					
						
							|  |  |  |                 'formats': formats, | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         raise last_e |