| 
									
										
										
										
											2015-12-25 01:59:56 +01:00
										 |  |  | # coding: utf-8 | 
					
						
							|  |  |  | from __future__ import unicode_literals | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-26 08:13:16 +01:00
										 |  |  | import hashlib | 
					
						
							|  |  |  | import hmac | 
					
						
							|  |  |  | import time | 
					
						
							| 
									
										
										
										
											2017-11-05 19:14:48 +07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-25 01:59:56 +01:00
										 |  |  | from .common import InfoExtractor | 
					
						
							| 
									
										
										
										
											2018-09-26 08:13:16 +01:00
										 |  |  | from ..compat import compat_HTTPError | 
					
						
							| 
									
										
										
										
											2015-12-25 01:59:56 +01:00
										 |  |  | from ..utils import ( | 
					
						
							|  |  |  |     determine_ext, | 
					
						
							| 
									
										
										
										
											2017-11-05 19:14:48 +07:00
										 |  |  |     ExtractorError, | 
					
						
							| 
									
										
										
										
											2015-12-25 01:59:56 +01:00
										 |  |  |     int_or_none, | 
					
						
							| 
									
										
										
										
											2018-10-02 06:07:06 +01:00
										 |  |  |     try_get, | 
					
						
							| 
									
										
										
										
											2015-12-25 01:59:56 +01:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-05 19:14:48 +07:00
										 |  |  | class HotStarBaseIE(InfoExtractor): | 
					
						
							| 
									
										
										
										
											2018-09-26 08:13:16 +01:00
										 |  |  |     _AKAMAI_ENCRYPTION_KEY = b'\x05\xfc\x1a\x01\xca\xc9\x4b\xc4\x12\xfc\x53\x12\x07\x75\xf9\xee' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _call_api(self, path, video_id, query_name='contentId'): | 
					
						
							|  |  |  |         st = int(time.time()) | 
					
						
							|  |  |  |         exp = st + 6000 | 
					
						
							|  |  |  |         auth = 'st=%d~exp=%d~acl=/*' % (st, exp) | 
					
						
							|  |  |  |         auth += '~hmac=' + hmac.new(self._AKAMAI_ENCRYPTION_KEY, auth.encode(), hashlib.sha256).hexdigest() | 
					
						
							|  |  |  |         response = self._download_json( | 
					
						
							|  |  |  |             'https://api.hotstar.com/' + path, | 
					
						
							|  |  |  |             video_id, headers={ | 
					
						
							|  |  |  |                 'hotstarauth': auth, | 
					
						
							|  |  |  |                 'x-country-code': 'IN', | 
					
						
							|  |  |  |                 'x-platform-code': 'JIO', | 
					
						
							|  |  |  |             }, query={ | 
					
						
							|  |  |  |                 query_name: video_id, | 
					
						
							|  |  |  |                 'tas': 10000, | 
					
						
							|  |  |  |             }) | 
					
						
							|  |  |  |         if response['statusCode'] != 'OK': | 
					
						
							|  |  |  |             raise ExtractorError( | 
					
						
							|  |  |  |                 response['body']['message'], expected=True) | 
					
						
							|  |  |  |         return response['body']['results'] | 
					
						
							| 
									
										
										
										
											2017-11-05 19:14:48 +07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class HotStarIE(HotStarBaseIE): | 
					
						
							| 
									
										
										
										
											2018-09-26 08:13:16 +01:00
										 |  |  |     IE_NAME = 'hotstar' | 
					
						
							| 
									
										
										
										
											2017-11-05 19:14:48 +07:00
										 |  |  |     _VALID_URL = r'https?://(?:www\.)?hotstar\.com/(?:.+?[/-])?(?P<id>\d{10})' | 
					
						
							| 
									
										
										
										
											2016-02-10 04:43:00 +06:00
										 |  |  |     _TESTS = [{ | 
					
						
							| 
									
										
										
										
											2018-11-30 00:48:15 +07:00
										 |  |  |         # contentData | 
					
						
							| 
									
										
										
										
											2018-09-26 08:13:16 +01:00
										 |  |  |         'url': 'https://www.hotstar.com/can-you-not-spread-rumours/1000076273', | 
					
						
							| 
									
										
										
										
											2015-12-25 01:59:56 +01:00
										 |  |  |         'info_dict': { | 
					
						
							|  |  |  |             'id': '1000076273', | 
					
						
							|  |  |  |             'ext': 'mp4', | 
					
						
							| 
									
										
										
										
											2018-09-26 08:13:16 +01:00
										 |  |  |             'title': 'Can You Not Spread Rumours?', | 
					
						
							| 
									
										
										
										
											2015-12-25 01:59:56 +01:00
										 |  |  |             'description': 'md5:c957d8868e9bc793ccb813691cc4c434', | 
					
						
							| 
									
										
										
										
											2018-09-26 08:13:16 +01:00
										 |  |  |             'timestamp': 1447248600, | 
					
						
							| 
									
										
										
										
											2015-12-25 01:59:56 +01:00
										 |  |  |             'upload_date': '20151111', | 
					
						
							|  |  |  |             'duration': 381, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         'params': { | 
					
						
							|  |  |  |             # m3u8 download | 
					
						
							|  |  |  |             'skip_download': True, | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-11-30 00:48:15 +07:00
										 |  |  |     }, { | 
					
						
							|  |  |  |         # contentDetail | 
					
						
							|  |  |  |         'url': 'https://www.hotstar.com/movies/radha-gopalam/1000057157', | 
					
						
							|  |  |  |         'only_matching': True, | 
					
						
							| 
									
										
										
										
											2016-02-10 04:43:00 +06:00
										 |  |  |     }, { | 
					
						
							|  |  |  |         'url': 'http://www.hotstar.com/sports/cricket/rajitha-sizzles-on-debut-with-329/2001477583', | 
					
						
							|  |  |  |         'only_matching': True, | 
					
						
							|  |  |  |     }, { | 
					
						
							|  |  |  |         'url': 'http://www.hotstar.com/1000000515', | 
					
						
							|  |  |  |         'only_matching': True, | 
					
						
							|  |  |  |     }] | 
					
						
							| 
									
										
										
										
											2018-09-26 08:13:16 +01:00
										 |  |  |     _GEO_BYPASS = False | 
					
						
							| 
									
										
										
										
											2015-12-25 01:59:56 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def _real_extract(self, url): | 
					
						
							|  |  |  |         video_id = self._match_id(url) | 
					
						
							| 
									
										
										
										
											2017-11-05 19:14:48 +07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-26 08:13:16 +01:00
										 |  |  |         webpage = self._download_webpage(url, video_id) | 
					
						
							|  |  |  |         app_state = self._parse_json(self._search_regex( | 
					
						
							|  |  |  |             r'<script>window\.APP_STATE\s*=\s*({.+?})</script>', | 
					
						
							|  |  |  |             webpage, 'app state'), video_id) | 
					
						
							| 
									
										
										
										
											2018-10-02 06:07:06 +01:00
										 |  |  |         video_data = {} | 
					
						
							| 
									
										
										
										
											2018-12-07 18:52:01 +01:00
										 |  |  |         getters = list( | 
					
						
							| 
									
										
										
										
											2018-11-30 00:48:15 +07:00
										 |  |  |             lambda x, k=k: x['initialState']['content%s' % k]['content'] | 
					
						
							|  |  |  |             for k in ('Data', 'Detail') | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2018-10-02 06:07:06 +01:00
										 |  |  |         for v in app_state.values(): | 
					
						
							| 
									
										
										
										
											2018-11-30 00:48:15 +07:00
										 |  |  |             content = try_get(v, getters, dict) | 
					
						
							| 
									
										
										
										
											2018-10-02 06:07:06 +01:00
										 |  |  |             if content and content.get('contentId') == video_id: | 
					
						
							|  |  |  |                 video_data = content | 
					
						
							| 
									
										
										
										
											2018-12-07 18:52:01 +01:00
										 |  |  |                 break | 
					
						
							| 
									
										
										
										
											2017-11-05 19:14:48 +07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-26 08:13:16 +01:00
										 |  |  |         title = video_data['title'] | 
					
						
							| 
									
										
										
										
											2017-02-12 17:24:45 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-26 08:13:16 +01:00
										 |  |  |         if video_data.get('drmProtected'): | 
					
						
							| 
									
										
										
										
											2017-02-12 17:24:45 +01:00
										 |  |  |             raise ExtractorError('This video is DRM protected.', expected=True) | 
					
						
							| 
									
										
										
										
											2015-12-25 01:59:56 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         formats = [] | 
					
						
							| 
									
										
										
										
											2018-09-26 08:13:16 +01:00
										 |  |  |         format_data = self._call_api('h/v1/play', video_id)['item'] | 
					
						
							|  |  |  |         format_url = format_data['playbackUrl'] | 
					
						
							|  |  |  |         ext = determine_ext(format_url) | 
					
						
							|  |  |  |         if ext == 'm3u8': | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 formats.extend(self._extract_m3u8_formats( | 
					
						
							|  |  |  |                     format_url, video_id, 'mp4', m3u8_id='hls')) | 
					
						
							|  |  |  |             except ExtractorError as e: | 
					
						
							|  |  |  |                 if isinstance(e.cause, compat_HTTPError) and e.cause.code == 403: | 
					
						
							|  |  |  |                     self.raise_geo_restricted(countries=['IN']) | 
					
						
							|  |  |  |                 raise | 
					
						
							|  |  |  |         elif ext == 'f4m': | 
					
						
							|  |  |  |             # produce broken files | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             formats.append({ | 
					
						
							|  |  |  |                 'url': format_url, | 
					
						
							|  |  |  |                 'width': int_or_none(format_data.get('width')), | 
					
						
							|  |  |  |                 'height': int_or_none(format_data.get('height')), | 
					
						
							|  |  |  |             }) | 
					
						
							| 
									
										
										
										
											2015-12-25 01:59:56 +01:00
										 |  |  |         self._sort_formats(formats) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return { | 
					
						
							|  |  |  |             'id': video_id, | 
					
						
							| 
									
										
										
										
											2017-02-12 17:24:45 +01:00
										 |  |  |             'title': title, | 
					
						
							| 
									
										
										
										
											2015-12-25 01:59:56 +01:00
										 |  |  |             'description': video_data.get('description'), | 
					
						
							|  |  |  |             'duration': int_or_none(video_data.get('duration')), | 
					
						
							| 
									
										
										
										
											2018-09-26 08:13:16 +01:00
										 |  |  |             'timestamp': int_or_none(video_data.get('broadcastDate') or video_data.get('startDate')), | 
					
						
							| 
									
										
										
										
											2015-12-25 01:59:56 +01:00
										 |  |  |             'formats': formats, | 
					
						
							| 
									
										
										
										
											2018-09-26 08:13:16 +01:00
										 |  |  |             'channel': video_data.get('channelName'), | 
					
						
							|  |  |  |             'channel_id': video_data.get('channelId'), | 
					
						
							|  |  |  |             'series': video_data.get('showName'), | 
					
						
							|  |  |  |             'season': video_data.get('seasonName'), | 
					
						
							|  |  |  |             'season_number': int_or_none(video_data.get('seasonNo')), | 
					
						
							|  |  |  |             'season_id': video_data.get('seasonId'), | 
					
						
							| 
									
										
										
										
											2017-02-12 17:24:45 +01:00
										 |  |  |             'episode': title, | 
					
						
							| 
									
										
										
										
											2018-09-26 08:13:16 +01:00
										 |  |  |             'episode_number': int_or_none(video_data.get('episodeNo')), | 
					
						
							| 
									
										
										
										
											2015-12-25 01:59:56 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2017-03-16 22:00:11 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-05 19:14:48 +07:00
										 |  |  | class HotStarPlaylistIE(HotStarBaseIE): | 
					
						
							| 
									
										
										
										
											2017-03-16 22:00:11 +05:30
										 |  |  |     IE_NAME = 'hotstar:playlist' | 
					
						
							| 
									
										
										
										
											2018-09-26 08:13:16 +01:00
										 |  |  |     _VALID_URL = r'https?://(?:www\.)?hotstar\.com/tv/[^/]+/s-\w+/list/[^/]+/t-(?P<id>\w+)' | 
					
						
							| 
									
										
										
										
											2017-03-16 22:00:11 +05:30
										 |  |  |     _TESTS = [{ | 
					
						
							| 
									
										
										
										
											2018-09-26 08:13:16 +01:00
										 |  |  |         'url': 'https://www.hotstar.com/tv/savdhaan-india/s-26/list/popular-clips/t-3_2_26', | 
					
						
							| 
									
										
										
										
											2017-03-16 22:00:11 +05:30
										 |  |  |         'info_dict': { | 
					
						
							| 
									
										
										
										
											2018-09-26 08:13:16 +01:00
										 |  |  |             'id': '3_2_26', | 
					
						
							| 
									
										
										
										
											2017-03-16 22:00:11 +05:30
										 |  |  |         }, | 
					
						
							| 
									
										
										
										
											2018-09-26 08:13:16 +01:00
										 |  |  |         'playlist_mincount': 20, | 
					
						
							| 
									
										
										
										
											2017-03-16 22:00:11 +05:30
										 |  |  |     }, { | 
					
						
							| 
									
										
										
										
											2018-09-26 08:13:16 +01:00
										 |  |  |         'url': 'https://www.hotstar.com/tv/savdhaan-india/s-26/list/extras/t-2480', | 
					
						
							| 
									
										
										
										
											2017-03-16 22:00:11 +05:30
										 |  |  |         'only_matching': True, | 
					
						
							|  |  |  |     }] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _real_extract(self, url): | 
					
						
							| 
									
										
										
										
											2018-09-26 08:13:16 +01:00
										 |  |  |         playlist_id = self._match_id(url) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         collection = self._call_api('o/v1/tray/find', playlist_id, 'uqId') | 
					
						
							| 
									
										
										
										
											2017-03-16 22:00:11 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  |         entries = [ | 
					
						
							| 
									
										
										
										
											2017-11-05 19:14:48 +07:00
										 |  |  |             self.url_result( | 
					
						
							| 
									
										
										
										
											2018-09-26 08:13:16 +01:00
										 |  |  |                 'https://www.hotstar.com/%s' % video['contentId'], | 
					
						
							| 
									
										
										
										
											2017-11-05 19:14:48 +07:00
										 |  |  |                 ie=HotStarIE.ie_key(), video_id=video['contentId']) | 
					
						
							| 
									
										
										
										
											2018-09-26 08:13:16 +01:00
										 |  |  |             for video in collection['assets']['items'] | 
					
						
							| 
									
										
										
										
											2017-11-05 19:14:48 +07:00
										 |  |  |             if video.get('contentId')] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return self.playlist_result(entries, playlist_id) |