Add DRM support for all streams (#121)
This commit is contained in:
parent
dc446332ab
commit
c9ae78f83b
@ -12,11 +12,6 @@ from resources.lib.viervijfzes.auth import AuthApi
|
|||||||
from resources.lib.viervijfzes.aws.cognito_idp import AuthenticationException, InvalidLoginException
|
from resources.lib.viervijfzes.aws.cognito_idp import AuthenticationException, InvalidLoginException
|
||||||
from resources.lib.viervijfzes.content import CACHE_PREVENT, ContentApi, GeoblockedException, UnavailableException
|
from resources.lib.viervijfzes.content import CACHE_PREVENT, ContentApi, GeoblockedException, UnavailableException
|
||||||
|
|
||||||
try: # Python 3
|
|
||||||
from urllib.parse import quote, urlencode
|
|
||||||
except ImportError: # Python 2
|
|
||||||
from urllib import quote, urlencode
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@ -79,18 +74,9 @@ class Player:
|
|||||||
|
|
||||||
if resolved_stream:
|
if resolved_stream:
|
||||||
titleitem = Menu.generate_titleitem(episode)
|
titleitem = Menu.generate_titleitem(episode)
|
||||||
if resolved_stream.license_url:
|
|
||||||
# Generate license key
|
|
||||||
license_key = self.create_license_key(resolved_stream.license_url,
|
|
||||||
key_headers=dict(
|
|
||||||
customdata=resolved_stream.auth,
|
|
||||||
))
|
|
||||||
else:
|
|
||||||
license_key = None
|
|
||||||
|
|
||||||
kodiutils.play(resolved_stream.url,
|
kodiutils.play(resolved_stream.url,
|
||||||
resolved_stream.stream_type,
|
resolved_stream.stream_type,
|
||||||
license_key,
|
resolved_stream.license_key,
|
||||||
info_dict=titleitem.info_dict,
|
info_dict=titleitem.info_dict,
|
||||||
art_dict=titleitem.art_dict,
|
art_dict=titleitem.art_dict,
|
||||||
prop_dict=titleitem.prop_dict)
|
prop_dict=titleitem.prop_dict)
|
||||||
@ -106,15 +92,7 @@ class Player:
|
|||||||
|
|
||||||
# Lookup the stream
|
# Lookup the stream
|
||||||
resolved_stream = self._resolve_stream(uuid, islongform)
|
resolved_stream = self._resolve_stream(uuid, islongform)
|
||||||
if resolved_stream.license_url:
|
kodiutils.play(resolved_stream.url, resolved_stream.stream_type, resolved_stream.license_key)
|
||||||
# Generate license key
|
|
||||||
license_key = self.create_license_key(resolved_stream.license_url, key_headers=dict(
|
|
||||||
customdata=resolved_stream.auth,
|
|
||||||
))
|
|
||||||
else:
|
|
||||||
license_key = None
|
|
||||||
|
|
||||||
kodiutils.play(resolved_stream.url, resolved_stream.stream_type, license_key)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _resolve_stream(uuid, islongform):
|
def _resolve_stream(uuid, islongform):
|
||||||
@ -153,26 +131,3 @@ class Player:
|
|||||||
except UnavailableException:
|
except UnavailableException:
|
||||||
kodiutils.ok_dialog(message=kodiutils.localize(30712)) # The video is unavailable...
|
kodiutils.ok_dialog(message=kodiutils.localize(30712)) # The video is unavailable...
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def create_license_key(key_url, key_type='R', key_headers=None, key_value=None):
|
|
||||||
""" Create a license key string that we need for inputstream.adaptive.
|
|
||||||
|
|
||||||
:param str key_url:
|
|
||||||
:param str key_type:
|
|
||||||
:param dict[str, str] key_headers:
|
|
||||||
:param str key_value:
|
|
||||||
:rtype: str
|
|
||||||
"""
|
|
||||||
header = ''
|
|
||||||
if key_headers:
|
|
||||||
header = urlencode(key_headers)
|
|
||||||
|
|
||||||
if key_type in ('A', 'R', 'B'):
|
|
||||||
key_value = key_type + '{SSM}'
|
|
||||||
elif key_type == 'D':
|
|
||||||
if 'D{SSM}' not in key_value:
|
|
||||||
raise ValueError('Missing D{SSM} placeholder')
|
|
||||||
key_value = quote(key_value)
|
|
||||||
|
|
||||||
return '%s|%s|%s|' % (key_url, header, key_value)
|
|
||||||
|
@ -79,19 +79,17 @@ STREAM_DICT = {
|
|||||||
class ResolvedStream:
|
class ResolvedStream:
|
||||||
""" Defines a stream that we can play"""
|
""" Defines a stream that we can play"""
|
||||||
|
|
||||||
def __init__(self, uuid=None, url=None, stream_type=None, license_url=None, auth=None):
|
def __init__(self, uuid=None, url=None, stream_type=None, license_key=None):
|
||||||
"""
|
"""
|
||||||
:type uuid: str
|
:type uuid: str
|
||||||
:type url: str
|
:type url: str
|
||||||
:type stream_type: str
|
:type stream_type: str
|
||||||
:type license_url: str
|
:type license_key: str
|
||||||
:type auth: str
|
|
||||||
"""
|
"""
|
||||||
self.uuid = uuid
|
self.uuid = uuid
|
||||||
self.url = url
|
self.url = url
|
||||||
self.stream_type = stream_type
|
self.stream_type = stream_type
|
||||||
self.license_url = license_url
|
self.license_key = license_key
|
||||||
self.auth = auth
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "%r" % self.__dict__
|
return "%r" % self.__dict__
|
||||||
|
@ -351,10 +351,10 @@ class ContentApi:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def get_stream_by_uuid(self, uuid, islongform):
|
def get_stream_by_uuid(self, uuid, islongform):
|
||||||
""" Get the stream URL to use for this video.
|
""" Return a ResolvedStream for this video.
|
||||||
:type uuid: str
|
:type uuid: str
|
||||||
:type islongform: bool
|
:type islongform: bool
|
||||||
:rtype str
|
:rtype: ResolvedStream
|
||||||
"""
|
"""
|
||||||
mode = 'long-form' if islongform else 'short-form'
|
mode = 'long-form' if islongform else 'short-form'
|
||||||
response = self._get_url(self.API_GOPLAY + '/web/v1/videos/%s/%s' % (mode, uuid), authentication='Bearer %s' % self._auth.get_token())
|
response = self._get_url(self.API_GOPLAY + '/web/v1/videos/%s/%s' % (mode, uuid), authentication='Bearer %s' % self._auth.get_token())
|
||||||
@ -363,34 +363,35 @@ class ContentApi:
|
|||||||
if not data:
|
if not data:
|
||||||
raise UnavailableException
|
raise UnavailableException
|
||||||
|
|
||||||
|
# Get DRM license
|
||||||
|
license_key = None
|
||||||
|
if data.get('drmXml'):
|
||||||
|
# BuyDRM format
|
||||||
|
# See https://docs.unified-streaming.com/documentation/drm/buydrm.html#setting-up-the-client
|
||||||
|
|
||||||
|
# Generate license key
|
||||||
|
license_key = self.create_license_key('https://wv-keyos.licensekeyserver.com/', key_headers=dict(
|
||||||
|
customdata=data['drmXml'],
|
||||||
|
))
|
||||||
|
|
||||||
|
# Get manifest url
|
||||||
if data.get('manifestUrls'):
|
if data.get('manifestUrls'):
|
||||||
|
|
||||||
if data.get('drmXml'):
|
|
||||||
# DRM protected stream
|
|
||||||
# See https://docs.unified-streaming.com/documentation/drm/buydrm.html#setting-up-the-client
|
|
||||||
|
|
||||||
# DRM protected DASH stream
|
|
||||||
return ResolvedStream(
|
|
||||||
uuid=uuid,
|
|
||||||
url=data['manifestUrls']['dash'],
|
|
||||||
stream_type=STREAM_DASH,
|
|
||||||
license_url='https://wv-keyos.licensekeyserver.com/',
|
|
||||||
auth=data['drmXml'],
|
|
||||||
)
|
|
||||||
|
|
||||||
if data.get('manifestUrls').get('dash'):
|
if data.get('manifestUrls').get('dash'):
|
||||||
# Unprotected DASH stream
|
# DASH stream
|
||||||
return ResolvedStream(
|
return ResolvedStream(
|
||||||
uuid=uuid,
|
uuid=uuid,
|
||||||
url=data['manifestUrls']['dash'],
|
url=data['manifestUrls']['dash'],
|
||||||
stream_type=STREAM_DASH,
|
stream_type=STREAM_DASH,
|
||||||
|
license_key=license_key,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Unprotected HLS stream
|
# HLS stream
|
||||||
return ResolvedStream(
|
return ResolvedStream(
|
||||||
uuid=uuid,
|
uuid=uuid,
|
||||||
url=data['manifestUrls']['hls'],
|
url=data['manifestUrls']['hls'],
|
||||||
stream_type=STREAM_HLS,
|
stream_type=STREAM_HLS,
|
||||||
|
license_key=license_key,
|
||||||
)
|
)
|
||||||
|
|
||||||
# No manifest url found, get manifest from Server-Side Ad Insertion service
|
# No manifest url found, get manifest from Server-Side Ad Insertion service
|
||||||
@ -398,11 +399,12 @@ class ContentApi:
|
|||||||
url = 'https://pubads.g.doubleclick.net/ondemand/dash/content/%s/vid/%s/streams' % (data.get('ssai').get('contentSourceID'), data.get('ssai').get('videoID'))
|
url = 'https://pubads.g.doubleclick.net/ondemand/dash/content/%s/vid/%s/streams' % (data.get('ssai').get('contentSourceID'), data.get('ssai').get('videoID'))
|
||||||
ad_data = json.loads(self._post_url(url, data=''))
|
ad_data = json.loads(self._post_url(url, data=''))
|
||||||
|
|
||||||
# Unprotected DASH stream
|
# Server-Side Ad Insertion DASH stream
|
||||||
return ResolvedStream(
|
return ResolvedStream(
|
||||||
uuid=uuid,
|
uuid=uuid,
|
||||||
url=ad_data['stream_manifest'],
|
url=ad_data['stream_manifest'],
|
||||||
stream_type=STREAM_DASH,
|
stream_type=STREAM_DASH,
|
||||||
|
license_key=license_key,
|
||||||
)
|
)
|
||||||
|
|
||||||
raise UnavailableException
|
raise UnavailableException
|
||||||
@ -733,6 +735,34 @@ class ContentApi:
|
|||||||
)
|
)
|
||||||
return episode
|
return episode
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_license_key(key_url, key_type='R', key_headers=None, key_value='', response_value=''):
|
||||||
|
""" Create a license key string that we need for inputstream.adaptive.
|
||||||
|
:type key_url: str
|
||||||
|
:type key_type: str
|
||||||
|
:type key_headers: dict[str, str]
|
||||||
|
:type key_value: str
|
||||||
|
:type response_value: str
|
||||||
|
:rtype str
|
||||||
|
"""
|
||||||
|
try: # Python 3
|
||||||
|
from urllib.parse import quote, urlencode
|
||||||
|
except ImportError: # Python 2
|
||||||
|
from urllib import quote, urlencode
|
||||||
|
|
||||||
|
header = ''
|
||||||
|
if key_headers:
|
||||||
|
header = urlencode(key_headers)
|
||||||
|
|
||||||
|
if key_type in ('A', 'R', 'B'):
|
||||||
|
key_value = key_type + '{SSM}'
|
||||||
|
elif key_type == 'D':
|
||||||
|
if 'D{SSM}' not in key_value:
|
||||||
|
raise ValueError('Missing D{SSM} placeholder')
|
||||||
|
key_value = quote(key_value)
|
||||||
|
|
||||||
|
return '%s|%s|%s|%s' % (key_url, header, key_value, response_value)
|
||||||
|
|
||||||
def _get_url(self, url, params=None, authentication=None):
|
def _get_url(self, url, params=None, authentication=None):
|
||||||
""" Makes a GET request for the specified URL.
|
""" Makes a GET request for the specified URL.
|
||||||
:type url: str
|
:type url: str
|
||||||
|
Loading…
Reference in New Issue
Block a user