[hotstar] Move to API v2 (closes #20931)

This commit is contained in:
Sergey M․ 2019-05-04 03:11:25 +07:00
parent e0dde1d8e2
commit 2533f5b691
No known key found for this signature in database
GPG Key ID: 2C393E0F18A9236D

View File

@ -4,40 +4,59 @@
import hashlib import hashlib
import hmac import hmac
import time import time
import uuid
from .common import InfoExtractor from .common import InfoExtractor
from ..compat import compat_HTTPError from ..compat import (
compat_HTTPError,
compat_str,
)
from ..utils import ( from ..utils import (
determine_ext, determine_ext,
ExtractorError, ExtractorError,
int_or_none, int_or_none,
str_or_none,
try_get, try_get,
url_or_none,
) )
class HotStarBaseIE(InfoExtractor): class HotStarBaseIE(InfoExtractor):
_AKAMAI_ENCRYPTION_KEY = b'\x05\xfc\x1a\x01\xca\xc9\x4b\xc4\x12\xfc\x53\x12\x07\x75\xf9\xee' _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'): def _call_api_impl(self, path, video_id, query):
st = int(time.time()) st = int(time.time())
exp = st + 6000 exp = st + 6000
auth = 'st=%d~exp=%d~acl=/*' % (st, exp) auth = 'st=%d~exp=%d~acl=/*' % (st, exp)
auth += '~hmac=' + hmac.new(self._AKAMAI_ENCRYPTION_KEY, auth.encode(), hashlib.sha256).hexdigest() auth += '~hmac=' + hmac.new(self._AKAMAI_ENCRYPTION_KEY, auth.encode(), hashlib.sha256).hexdigest()
response = self._download_json( response = self._download_json(
'https://api.hotstar.com/' + path, 'https://api.hotstar.com/' + path, video_id, headers={
video_id, headers={
'hotstarauth': auth, 'hotstarauth': auth,
'x-country-code': 'IN', 'x-country-code': 'IN',
'x-platform-code': 'JIO', 'x-platform-code': 'JIO',
}, query={ }, query=query)
query_name: video_id,
'tas': 10000,
})
if response['statusCode'] != 'OK': if response['statusCode'] != 'OK':
raise ExtractorError( raise ExtractorError(
response['body']['message'], expected=True) response['body']['message'], expected=True)
return response['body']['results'] return response['body']['results']
def _call_api(self, path, video_id, query_name='contentId'):
return self._call_api_impl(path, video_id, {
query_name: video_id,
'tas': 10000,
})
def _call_api_v2(self, path, video_id):
return self._call_api_impl(
'%s/in/contents/%s' % (path, video_id), video_id, {
'desiredConfig': 'encryption:plain;ladder:phone,tv;package:hls,dash',
'client': 'mweb',
'clientVersion': '6.18.0',
'deviceId': compat_str(uuid.uuid4()),
'osName': 'Windows',
'osVersion': '10',
})
class HotStarIE(HotStarBaseIE): class HotStarIE(HotStarBaseIE):
IE_NAME = 'hotstar' IE_NAME = 'hotstar'
@ -68,6 +87,10 @@ class HotStarIE(HotStarBaseIE):
}, { }, {
'url': 'http://www.hotstar.com/1000000515', 'url': 'http://www.hotstar.com/1000000515',
'only_matching': True, 'only_matching': True,
}, {
# only available via api v2
'url': 'https://www.hotstar.com/tv/ek-bhram-sarvagun-sampanna/s-2116/janhvi-targets-suman/1000234847',
'only_matching': True,
}] }]
_GEO_BYPASS = False _GEO_BYPASS = False
@ -95,26 +118,40 @@ def _real_extract(self, url):
raise ExtractorError('This video is DRM protected.', expected=True) raise ExtractorError('This video is DRM protected.', expected=True)
formats = [] formats = []
format_data = self._call_api('h/v1/play', video_id)['item'] geo_restricted = False
format_url = format_data['playbackUrl'] playback_sets = self._call_api_v2('h/v2/play', video_id)['playBackSets']
for playback_set in playback_sets:
if not isinstance(playback_set, dict):
continue
format_url = url_or_none(playback_set.get('playbackUrl'))
if not format_url:
continue
tags = str_or_none(playback_set.get('tagsCombination')) or ''
if tags and 'encryption:plain' not in tags:
continue
ext = determine_ext(format_url) ext = determine_ext(format_url)
if ext == 'm3u8':
try: try:
if 'package:hls' in tags or ext == 'm3u8':
formats.extend(self._extract_m3u8_formats( formats.extend(self._extract_m3u8_formats(
format_url, video_id, 'mp4', m3u8_id='hls')) format_url, video_id, 'mp4', m3u8_id='hls'))
except ExtractorError as e: elif 'package:dash' in tags or ext == 'mpd':
if isinstance(e.cause, compat_HTTPError) and e.cause.code == 403: formats.extend(self._extract_mpd_formats(
self.raise_geo_restricted(countries=['IN']) format_url, video_id, mpd_id='dash'))
raise
elif ext == 'f4m': elif ext == 'f4m':
# produce broken files # produce broken files
pass pass
else: else:
formats.append({ formats.append({
'url': format_url, 'url': format_url,
'width': int_or_none(format_data.get('width')), 'width': int_or_none(playback_set.get('width')),
'height': int_or_none(format_data.get('height')), 'height': int_or_none(playback_set.get('height')),
}) })
except ExtractorError as e:
if isinstance(e.cause, compat_HTTPError) and e.cause.code == 403:
geo_restricted = True
continue
if not formats and geo_restricted:
self.raise_geo_restricted(countries=['IN'])
self._sort_formats(formats) self._sort_formats(formats)
return { return {