Update api (#114)
This commit is contained in:
parent
89f4b457df
commit
9cdebb6c7f
@ -161,10 +161,13 @@ def play_epg(channel, timestamp):
|
|||||||
|
|
||||||
@routing.route('/play/catalog')
|
@routing.route('/play/catalog')
|
||||||
@routing.route('/play/catalog/<uuid>')
|
@routing.route('/play/catalog/<uuid>')
|
||||||
def play_catalog(uuid=None):
|
@routing.route('/play/catalog/<uuid>/<islongform>')
|
||||||
|
def play_catalog(uuid=None, islongform=False):
|
||||||
""" Play the requested item """
|
""" Play the requested item """
|
||||||
|
from ast import literal_eval
|
||||||
from resources.lib.modules.player import Player
|
from resources.lib.modules.player import Player
|
||||||
Player().play(uuid)
|
# Convert string to bool using literal_eval
|
||||||
|
Player().play(uuid, literal_eval(islongform))
|
||||||
|
|
||||||
|
|
||||||
@routing.route('/play/page/<page>')
|
@routing.route('/play/page/<page>')
|
||||||
|
@ -183,7 +183,7 @@ class Menu:
|
|||||||
|
|
||||||
if item.uuid:
|
if item.uuid:
|
||||||
# We have an UUID and can play this item directly
|
# We have an UUID and can play this item directly
|
||||||
path = kodiutils.url_for('play_catalog', uuid=item.uuid)
|
path = kodiutils.url_for('play_catalog', uuid=item.uuid, islongform=item.islongform)
|
||||||
else:
|
else:
|
||||||
# We don't have an UUID, and first need to fetch the video information from the page
|
# We don't have an UUID, and first need to fetch the video information from the page
|
||||||
path = kodiutils.url_for('play_from_page', page=quote(item.path, safe=''))
|
path = kodiutils.url_for('play_from_page', page=quote(item.path, safe=''))
|
||||||
|
@ -74,7 +74,7 @@ class Player:
|
|||||||
|
|
||||||
if episode.uuid:
|
if episode.uuid:
|
||||||
# Lookup the stream
|
# Lookup the stream
|
||||||
resolved_stream = self._resolve_stream(episode.uuid)
|
resolved_stream = self._resolve_stream(episode.uuid, episode.islongform)
|
||||||
_LOGGER.debug('Resolved stream: %s', resolved_stream)
|
_LOGGER.debug('Resolved stream: %s', resolved_stream)
|
||||||
|
|
||||||
if resolved_stream:
|
if resolved_stream:
|
||||||
@ -95,16 +95,17 @@ class Player:
|
|||||||
art_dict=titleitem.art_dict,
|
art_dict=titleitem.art_dict,
|
||||||
prop_dict=titleitem.prop_dict)
|
prop_dict=titleitem.prop_dict)
|
||||||
|
|
||||||
def play(self, uuid):
|
def play(self, uuid, islongform):
|
||||||
""" Play the requested item.
|
""" Play the requested item.
|
||||||
:type uuid: string
|
:type uuid: string
|
||||||
|
:type islongform: bool
|
||||||
"""
|
"""
|
||||||
if not uuid:
|
if not uuid:
|
||||||
kodiutils.ok_dialog(message=kodiutils.localize(30712)) # The video is unavailable...
|
kodiutils.ok_dialog(message=kodiutils.localize(30712)) # The video is unavailable...
|
||||||
return
|
return
|
||||||
|
|
||||||
# Lookup the stream
|
# Lookup the stream
|
||||||
resolved_stream = self._resolve_stream(uuid)
|
resolved_stream = self._resolve_stream(uuid, islongform)
|
||||||
if resolved_stream.license_url:
|
if resolved_stream.license_url:
|
||||||
# Generate license key
|
# Generate license key
|
||||||
license_key = self.create_license_key(resolved_stream.license_url, key_headers=dict(
|
license_key = self.create_license_key(resolved_stream.license_url, key_headers=dict(
|
||||||
@ -116,9 +117,10 @@ class Player:
|
|||||||
kodiutils.play(resolved_stream.url, resolved_stream.stream_type, license_key)
|
kodiutils.play(resolved_stream.url, resolved_stream.stream_type, license_key)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _resolve_stream(uuid):
|
def _resolve_stream(uuid, islongform):
|
||||||
""" Resolve the stream for the requested item
|
""" Resolve the stream for the requested item
|
||||||
:type uuid: string
|
:type uuid: string
|
||||||
|
:type islongform: bool
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# Check if we have credentials
|
# Check if we have credentials
|
||||||
@ -135,7 +137,7 @@ class Player:
|
|||||||
auth = AuthApi(kodiutils.get_setting('username'), kodiutils.get_setting('password'), kodiutils.get_tokens_path())
|
auth = AuthApi(kodiutils.get_setting('username'), kodiutils.get_setting('password'), kodiutils.get_tokens_path())
|
||||||
|
|
||||||
# Get stream information
|
# Get stream information
|
||||||
resolved_stream = ContentApi(auth).get_stream_by_uuid(uuid)
|
resolved_stream = ContentApi(auth).get_stream_by_uuid(uuid, islongform)
|
||||||
return resolved_stream
|
return resolved_stream
|
||||||
|
|
||||||
except (InvalidLoginException, AuthenticationException) as ex:
|
except (InvalidLoginException, AuthenticationException) as ex:
|
||||||
|
@ -109,7 +109,7 @@ class Episode:
|
|||||||
""" Defines an Episode. """
|
""" Defines an Episode. """
|
||||||
|
|
||||||
def __init__(self, uuid=None, nodeid=None, path=None, channel=None, program_title=None, title=None, description=None, thumb=None, duration=None,
|
def __init__(self, uuid=None, nodeid=None, path=None, channel=None, program_title=None, title=None, description=None, thumb=None, duration=None,
|
||||||
season=None, season_uuid=None, number=None, rating=None, aired=None, expiry=None, stream=None):
|
season=None, season_uuid=None, number=None, rating=None, aired=None, expiry=None, stream=None, islongform=False):
|
||||||
"""
|
"""
|
||||||
:type uuid: str
|
:type uuid: str
|
||||||
:type nodeid: str
|
:type nodeid: str
|
||||||
@ -127,6 +127,7 @@ class Episode:
|
|||||||
:type aired: datetime
|
:type aired: datetime
|
||||||
:type expiry: datetime
|
:type expiry: datetime
|
||||||
:type stream: string
|
:type stream: string
|
||||||
|
:type islongform: bool
|
||||||
"""
|
"""
|
||||||
self.uuid = uuid
|
self.uuid = uuid
|
||||||
self.nodeid = nodeid
|
self.nodeid = nodeid
|
||||||
@ -144,6 +145,7 @@ class Episode:
|
|||||||
self.aired = aired
|
self.aired = aired
|
||||||
self.expiry = expiry
|
self.expiry = expiry
|
||||||
self.stream = stream
|
self.stream = stream
|
||||||
|
self.islongform = islongform
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "%r" % self.__dict__
|
return "%r" % self.__dict__
|
||||||
@ -349,51 +351,64 @@ class ContentApi:
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_stream_by_uuid(self, uuid):
|
def get_stream_by_uuid(self, uuid, islongform):
|
||||||
""" Get the stream URL to use for this video.
|
""" Get the stream URL to use for this video.
|
||||||
:type uuid: str
|
:type uuid: str
|
||||||
|
:type islongform: bool
|
||||||
:rtype str
|
:rtype str
|
||||||
"""
|
"""
|
||||||
response = self._get_url(self.API_VIERVIJFZES + '/content/%s' % uuid, authentication=self._auth.get_token())
|
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())
|
||||||
data = json.loads(response)
|
data = json.loads(response)
|
||||||
|
|
||||||
if not data:
|
if not data:
|
||||||
raise UnavailableException
|
raise UnavailableException
|
||||||
|
|
||||||
if 'videoDash' in data:
|
if data.get('manifestUrls'):
|
||||||
|
|
||||||
if 'drmKey' in data:
|
if data.get('drmXml'):
|
||||||
# DRM protected stream
|
# DRM protected stream
|
||||||
# See https://docs.unified-streaming.com/documentation/drm/buydrm.html#setting-up-the-client
|
# See https://docs.unified-streaming.com/documentation/drm/buydrm.html#setting-up-the-client
|
||||||
drm_key = data['drmKey']['S']
|
|
||||||
|
|
||||||
_LOGGER.debug('Fetching Authentication XML with drm_key %s', drm_key)
|
|
||||||
response_drm = self._get_url(self.API_GOPLAY + '/video/xml/%s' % drm_key, authentication=self._auth.get_token())
|
|
||||||
data_drm = json.loads(response_drm)
|
|
||||||
|
|
||||||
# DRM protected DASH stream
|
# DRM protected DASH stream
|
||||||
return ResolvedStream(
|
return ResolvedStream(
|
||||||
uuid=uuid,
|
uuid=uuid,
|
||||||
url=data['videoDash']['S'],
|
url=data['manifestUrls']['dash'],
|
||||||
stream_type=STREAM_DASH,
|
stream_type=STREAM_DASH,
|
||||||
license_url='https://wv-keyos.licensekeyserver.com/',
|
license_url='https://wv-keyos.licensekeyserver.com/',
|
||||||
auth=data_drm.get('auth'),
|
auth=data['drmXml'],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if data.get('manifestUrls').get('dash'):
|
||||||
# Unprotected DASH stream
|
# Unprotected DASH stream
|
||||||
return ResolvedStream(
|
return ResolvedStream(
|
||||||
uuid=uuid,
|
uuid=uuid,
|
||||||
url=data['videoDash']['S'],
|
url=data['manifestUrls']['dash'],
|
||||||
stream_type=STREAM_DASH,
|
stream_type=STREAM_DASH,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Unprotected HLS stream
|
# Unprotected HLS stream
|
||||||
return ResolvedStream(
|
return ResolvedStream(
|
||||||
uuid=uuid,
|
uuid=uuid,
|
||||||
url=data['video']['S'],
|
url=data['manifestUrls']['hls'],
|
||||||
stream_type=STREAM_HLS,
|
stream_type=STREAM_HLS,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# No manifest url found, get manifest from Server-Side Ad Insertion service
|
||||||
|
if data.get('adType') == 'SSAI' and data.get('ssai'):
|
||||||
|
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=''))
|
||||||
|
|
||||||
|
# Unprotected DASH stream
|
||||||
|
return ResolvedStream(
|
||||||
|
uuid=uuid,
|
||||||
|
url=ad_data['stream_manifest'],
|
||||||
|
stream_type=STREAM_DASH,
|
||||||
|
)
|
||||||
|
|
||||||
|
raise UnavailableException
|
||||||
|
|
||||||
|
|
||||||
def get_program_tree(self, cache=CACHE_AUTO):
|
def get_program_tree(self, cache=CACHE_AUTO):
|
||||||
""" Get a content tree with information about all the programs.
|
""" Get a content tree with information about all the programs.
|
||||||
:type cache: str
|
:type cache: str
|
||||||
@ -675,7 +690,6 @@ class ContentApi:
|
|||||||
:type season_uuid: str
|
:type season_uuid: str
|
||||||
:rtype Episode
|
:rtype Episode
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if data.get('episodeNumber'):
|
if data.get('episodeNumber'):
|
||||||
episode_number = data.get('episodeNumber')
|
episode_number = data.get('episodeNumber')
|
||||||
else:
|
else:
|
||||||
@ -703,6 +717,7 @@ class ContentApi:
|
|||||||
expiry=datetime.fromtimestamp(int(data.get('unpublishDate'))) if data.get('unpublishDate') else None,
|
expiry=datetime.fromtimestamp(int(data.get('unpublishDate'))) if data.get('unpublishDate') else None,
|
||||||
rating=data.get('parentalRating'),
|
rating=data.get('parentalRating'),
|
||||||
stream=data.get('path'),
|
stream=data.get('path'),
|
||||||
|
islongform=data.get('isLongForm'),
|
||||||
)
|
)
|
||||||
return episode
|
return episode
|
||||||
|
|
||||||
@ -751,7 +766,7 @@ class ContentApi:
|
|||||||
else:
|
else:
|
||||||
response = self._session.post(url, params=params, json=data)
|
response = self._session.post(url, params=params, json=data)
|
||||||
|
|
||||||
if response.status_code != 200:
|
if response.status_code not in (200, 201):
|
||||||
_LOGGER.error(response.text)
|
_LOGGER.error(response.text)
|
||||||
raise Exception('Could not fetch data')
|
raise Exception('Could not fetch data')
|
||||||
|
|
||||||
|
@ -70,12 +70,12 @@ class TestApi(unittest.TestCase):
|
|||||||
self.assertIsInstance(program, Program)
|
self.assertIsInstance(program, Program)
|
||||||
|
|
||||||
episode = program.episodes[0]
|
episode = program.episodes[0]
|
||||||
resolved_stream = self._api.get_stream_by_uuid(episode.uuid)
|
resolved_stream = self._api.get_stream_by_uuid(episode.uuid, episode.islongform)
|
||||||
self.assertIsInstance(resolved_stream, ResolvedStream)
|
self.assertIsInstance(resolved_stream, ResolvedStream)
|
||||||
|
|
||||||
@unittest.skipUnless(kodiutils.get_setting('username') and kodiutils.get_setting('password'), 'Skipping since we have no credentials.')
|
@unittest.skipUnless(kodiutils.get_setting('username') and kodiutils.get_setting('password'), 'Skipping since we have no credentials.')
|
||||||
def test_get_drm_stream(self):
|
def test_get_drm_stream(self):
|
||||||
resolved_stream = self._api.get_stream_by_uuid('e7faa457-5768-4abd-bf4f-5a0e1055bbd3') # https://www.goplay.be/video/ncis-los-angeles/ncis-los-angeles-s13/ncis-los-angeles-s13-aflevering-1
|
resolved_stream = self._api.get_stream_by_uuid('e7faa457-5768-4abd-bf4f-5a0e1055bbd3', True) # https://www.goplay.be/video/ncis-los-angeles/ncis-los-angeles-s13/ncis-los-angeles-s13-aflevering-1
|
||||||
self.assertIsInstance(resolved_stream, ResolvedStream)
|
self.assertIsInstance(resolved_stream, ResolvedStream)
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user