2 from __future__ 
import unicode_literals
 
  12 from .common 
import InfoExtractor
 
  13 from ..compat 
import ( 
  14     compat_urllib_parse_urlencode
, 
  23 class VRVBaseIE(InfoExtractor
): 
  28     def _call_api(self
, path
, video_id
, note
, data
=None): 
  29         base_url 
= self
._API
_DOMAIN 
+ '/core/' + path
 
  30         encoded_query 
= compat_urllib_parse_urlencode({ 
  31             'oauth_consumer_key': self
._API
_PARAMS
['oAuthKey'], 
  32             'oauth_nonce': ''.join([random
.choice(string
.ascii_letters
) for _ 
in range(32)]), 
  33             'oauth_signature_method': 'HMAC-SHA1', 
  34             'oauth_timestamp': int(time
.time()), 
  35             'oauth_version': '1.0', 
  37         headers 
= self
.geo_verification_headers() 
  39             data 
= json
.dumps(data
).encode() 
  40             headers
['Content-Type'] = 'application/json' 
  41         method 
= 'POST' if data 
else 'GET' 
  42         base_string 
= '&'.join([method
, compat_urllib_parse
.quote(base_url
, ''), compat_urllib_parse
.quote(encoded_query
, '')]) 
  43         oauth_signature 
= base64
.b64encode(hmac
.new( 
  44             (self
._API
_PARAMS
['oAuthSecret'] + '&').encode('ascii'), 
  45             base_string
.encode(), hashlib
.sha1
).digest()).decode() 
  46         encoded_query 
+= '&oauth_signature=' + compat_urllib_parse
.quote(oauth_signature
, '') 
  47         return self
._download
_json
( 
  48             '?'.join([base_url
, encoded_query
]), video_id
, 
  49             note
='Downloading %s JSON metadata' % note
, headers
=headers
, data
=data
) 
  51     def _call_cms(self
, path
, video_id
, note
): 
  52         if not self
._CMS
_SIGNING
: 
  53             self
._CMS
_SIGNING 
= self
._call
_api
('index', video_id
, 'CMS Signing')['cms_signing'] 
  54         return self
._download
_json
( 
  55             self
._API
_DOMAIN 
+ path
, video_id
, query
=self
._CMS
_SIGNING
, 
  56             note
='Downloading %s JSON metadata' % note
, headers
=self
.geo_verification_headers()) 
  58     def _set_api_params(self
, webpage
, video_id
): 
  59         if not self
._API
_PARAMS
: 
  60             self
._API
_PARAMS 
= self
._parse
_json
(self
._search
_regex
( 
  61                 r
'window\.__APP_CONFIG__\s*=\s*({.+?})</script>', 
  62                 webpage
, 'api config'), video_id
)['cxApiParams'] 
  63             self
._API
_DOMAIN 
= self
._API
_PARAMS
.get('apiDomain', 'https://api.vrv.co') 
  65     def _get_cms_resource(self
, resource_key
, video_id
): 
  66         return self
._call
_api
( 
  67             'cms_resource', video_id
, 'resource path', data
={ 
  68                 'resource_key': resource_key
, 
  69             })['__links__']['cms_resource']['href'] 
  72 class VRVIE(VRVBaseIE
): 
  74     _VALID_URL 
= r
'https?://(?:www\.)?vrv\.co/watch/(?P<id>[A-Z0-9]+)' 
  76         'url': 'https://vrv.co/watch/GR9PNZ396/Hidden-America-with-Jonah-Ray:BOSTON-WHERE-THE-PAST-IS-THE-PRESENT', 
  80             'title': 'BOSTON: WHERE THE PAST IS THE PRESENT', 
  81             'description': 'md5:4ec8844ac262ca2df9e67c0983c6b83f', 
  82             'uploader_id': 'seeso', 
  86             'skip_download': True, 
  90     def _real_extract(self
, url
): 
  91         video_id 
= self
._match
_id
(url
) 
  92         webpage 
= self
._download
_webpage
( 
  94             headers
=self
.geo_verification_headers()) 
  95         media_resource 
= self
._parse
_json
(self
._search
_regex
( 
  96             r
'window\.__INITIAL_STATE__\s*=\s*({.+?})</script>', 
  97             webpage
, 'inital state'), video_id
).get('watch', {}).get('mediaResource') or {} 
  99         video_data 
= media_resource
.get('json') 
 101             self
._set
_api
_params
(webpage
, video_id
) 
 102             episode_path 
= self
._get
_cms
_resource
( 
 103                 'cms:/episodes/' + video_id
, video_id
) 
 104             video_data 
= self
._call
_cms
(episode_path
, video_id
, 'video') 
 105         title 
= video_data
['title'] 
 107         streams_json 
= media_resource
.get('streams', {}).get('json', {}) 
 109             self
._set
_api
_params
(webpage
, video_id
) 
 110             streams_path 
= video_data
['__links__']['streams']['href'] 
 111             streams_json 
= self
._call
_cms
(streams_path
, video_id
, 'streams') 
 113         audio_locale 
= streams_json
.get('audio_locale') 
 115         for stream_type
, streams 
in streams_json
.get('streams', {}).items(): 
 116             if stream_type 
in ('adaptive_hls', 'adaptive_dash'): 
 117                 for stream 
in streams
.values(): 
 118                     stream_url 
= stream
.get('url') 
 121                     stream_id 
= stream
.get('hardsub_locale') or audio_locale
 
 122                     format_id 
= '%s-%s' % (stream_type
.split('_')[1], stream_id
) 
 123                     if stream_type 
== 'adaptive_hls': 
 124                         adaptive_formats 
= self
._extract
_m
3u8_formats
( 
 125                             stream_url
, video_id
, 'mp4', m3u8_id
=format_id
, 
 126                             note
='Downloading %s m3u8 information' % stream_id
, 
 129                         adaptive_formats 
= self
._extract
_mpd
_formats
( 
 130                             stream_url
, video_id
, mpd_id
=format_id
, 
 131                             note
='Downloading %s MPD information' % stream_id
, 
 134                         for f 
in adaptive_formats
: 
 135                             if f
.get('acodec') != 'none': 
 136                                 f
['language'] = audio_locale
 
 137                     formats
.extend(adaptive_formats
) 
 138         self
._sort
_formats
(formats
) 
 141         for subtitle 
in streams_json
.get('subtitles', {}).values(): 
 142             subtitle_url 
= subtitle
.get('url') 
 145             subtitles
.setdefault(subtitle
.get('locale', 'en-US'), []).append({ 
 147                 'ext': subtitle
.get('format', 'ass'), 
 151         for thumbnail 
in video_data
.get('images', {}).get('thumbnails', []): 
 152             thumbnail_url 
= thumbnail
.get('source') 
 153             if not thumbnail_url
: 
 156                 'url': thumbnail_url
, 
 157                 'width': int_or_none(thumbnail
.get('width')), 
 158                 'height': int_or_none(thumbnail
.get('height')), 
 165             'subtitles': subtitles
, 
 166             'thumbnails': thumbnails
, 
 167             'description': video_data
.get('description'), 
 168             'duration': float_or_none(video_data
.get('duration_ms'), 1000), 
 169             'uploader_id': video_data
.get('channel_id'), 
 170             'series': video_data
.get('series_title'), 
 171             'season': video_data
.get('season_title'), 
 172             'season_number': int_or_none(video_data
.get('season_number')), 
 173             'season_id': video_data
.get('season_id'), 
 175             'episode_number': int_or_none(video_data
.get('episode_number')), 
 176             'episode_id': video_data
.get('production_episode_id'), 
 180 class VRVSeriesIE(VRVBaseIE
): 
 181     IE_NAME 
= 'vrv:series' 
 182     _VALID_URL 
= r
'https?://(?:www\.)?vrv\.co/series/(?P<id>[A-Z0-9]+)' 
 184         'url': 'https://vrv.co/series/G68VXG3G6/The-Perfect-Insider', 
 188         'playlist_mincount': 11, 
 191     def _real_extract(self
, url
): 
 192         series_id 
= self
._match
_id
(url
) 
 193         webpage 
= self
._download
_webpage
( 
 195             headers
=self
.geo_verification_headers()) 
 197         self
._set
_api
_params
(webpage
, series_id
) 
 198         seasons_path 
= self
._get
_cms
_resource
( 
 199             'cms:/seasons?series_id=' + series_id
, series_id
) 
 200         seasons_data 
= self
._call
_cms
(seasons_path
, series_id
, 'seasons') 
 203         for season 
in seasons_data
.get('items', []): 
 204             episodes_path 
= season
['__links__']['season/episodes']['href'] 
 205             episodes 
= self
._call
_cms
(episodes_path
, series_id
, 'episodes') 
 206             for episode 
in episodes
.get('items', []): 
 207                 episode_id 
= episode
['id'] 
 208                 entries
.append(self
.url_result( 
 209                     'https://vrv.co/watch/' + episode_id
, 
 210                     'VRV', episode_id
, episode
.get('title'))) 
 212         return self
.playlist_result(entries
, series_id
)