Implement C-SPAN Live information extractor

This commit introduces the `CSpanLiveIE` class to add support for C-SPAN
live streams. These streams are based on the `BrightcoveNewIE` class
which requires Adobe Pass MSO authentication.

In order to support this new information extractor, `BrightcoveNewIE`
had to be updated to support an optional Akamai token ('hdnts' query
parameter) in the final m3u8 URL used by Brightcove.
This commit is contained in:
Clay Freeman 2019-07-16 23:57:55 -05:00
parent 1824bfdcdf
commit bce2befab6
No known key found for this signature in database
GPG Key ID: E8023472A3663FDC
3 changed files with 47 additions and 4 deletions

View File

@ -12,6 +12,7 @@ from ..compat import (
compat_etree_fromstring, compat_etree_fromstring,
compat_parse_qs, compat_parse_qs,
compat_str, compat_str,
compat_urllib_parse_urlencode,
compat_urllib_parse_urlparse, compat_urllib_parse_urlparse,
compat_urlparse, compat_urlparse,
compat_xml_parse_error, compat_xml_parse_error,
@ -611,14 +612,14 @@ class BrightcoveNewIE(AdobePassIE):
return entries return entries
def _parse_brightcove_metadata(self, json_data, video_id, headers={}): def _parse_brightcove_metadata(self, json_data, video_id, headers={}, options={}):
title = json_data['name'].strip() title = json_data['name'].strip()
formats = [] formats = []
for source in json_data.get('sources', []): for source in json_data.get('sources', []):
container = source.get('container') container = source.get('container')
ext = mimetype2ext(source.get('type')) ext = mimetype2ext(source.get('type'))
src = source.get('src') src = self._preprocess_metadata_url(source.get('src'), options)
# https://support.brightcove.com/playback-api-video-fields-reference#key_systems_object # https://support.brightcove.com/playback-api-video-fields-reference#key_systems_object
if ext == 'ism' or container == 'WVM' or source.get('key_systems'): if ext == 'ism' or container == 'WVM' or source.get('key_systems'):
continue continue
@ -723,6 +724,17 @@ class BrightcoveNewIE(AdobePassIE):
'is_live': is_live, 'is_live': is_live,
} }
def _preprocess_metadata_url(self, url, options={}):
url = compat_urllib_parse_urlparse(url)._asdict()
query = dict(compat_parse_qs(url['query']))
if options.get('akamai_token') is not None:
query['hdnts'] = options.get('akamai_token')
url['query'] = compat_urllib_parse_urlencode(query)
return compat_urlparse.urlunparse(tuple(url.values()))
def _real_extract(self, url): def _real_extract(self, url):
url, smuggled_data = unsmuggle_url(url, {}) url, smuggled_data = unsmuggle_url(url, {})
self._initialize_geo_bypass({ self._initialize_geo_bypass({
@ -786,12 +798,16 @@ class BrightcoveNewIE(AdobePassIE):
'tveToken': tve_token, 'tveToken': tve_token,
}) })
options = {
'akamai_token': smuggled_data.get('akamai_token')
}
if content_type == 'playlist': if content_type == 'playlist':
return self.playlist_result( return self.playlist_result(
[self._parse_brightcove_metadata(vid, vid.get('id'), headers) [self._parse_brightcove_metadata(vid, vid.get('id'), headers=headers, options=options)
for vid in json_data.get('videos', []) if vid.get('id')], for vid in json_data.get('videos', []) if vid.get('id')],
json_data.get('id'), json_data.get('name'), json_data.get('id'), json_data.get('name'),
json_data.get('description')) json_data.get('description'))
return self._parse_brightcove_metadata( return self._parse_brightcove_metadata(
json_data, video_id, headers=headers) json_data, video_id, headers=headers, options=options)

View File

@ -0,0 +1,26 @@
# coding: utf-8
from __future__ import unicode_literals
from .brightcove import BrightcoveNewIE
from .common import InfoExtractor
from ..utils import smuggle_url
class CSpanLiveIE(InfoExtractor):
IE_NAME = 'cspanlive'
BRIGHTCOVE_URL_TEMPLATE = 'http://players.brightcove.net/3162030207001/2B2qWQJYYM_default/index.html?videoId=%s'
_VALID_URL = r'^https?://(?:www\.)?c-span\.org/networks'
def _real_extract(self, url):
webpage = self._download_webpage(url, 'stream')
akamai_token = self._html_search_regex(r'data-akamaitoken="([^"]+)"', webpage, 'akamai_token')
video_id = self._html_search_regex(r'data-bcid="([^"]+)"', webpage, 'video_id')
brightcove_url = smuggle_url(self.BRIGHTCOVE_URL_TEMPLATE % video_id, {
'akamai_token': akamai_token,
'source_url': url
})
return self.url_result(brightcove_url, ie=BrightcoveNewIE.ie_key(), video_id=video_id)

View File

@ -238,6 +238,7 @@ from .crunchyroll import (
CrunchyrollShowPlaylistIE CrunchyrollShowPlaylistIE
) )
from .cspan import CSpanIE from .cspan import CSpanIE
from .cspanlive import CSpanLiveIE
from .ctsnews import CtsNewsIE from .ctsnews import CtsNewsIE
from .ctvnews import CTVNewsIE from .ctvnews import CTVNewsIE
from .cultureunplugged import CultureUnpluggedIE from .cultureunplugged import CultureUnpluggedIE