Update api (#114)

This commit is contained in:
mediaminister 2023-01-16 17:59:29 +01:00 committed by GitHub
parent 89f4b457df
commit 9cdebb6c7f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 51 additions and 31 deletions

View File

@ -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>')

View File

@ -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=''))

View File

@ -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:

View File

@ -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,50 +351,63 @@ 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
return ResolvedStream(
uuid=uuid,
url=data['manifestUrls']['dash'],
stream_type=STREAM_DASH,
)
# Unprotected HLS stream
return ResolvedStream(
uuid=uuid,
url=data['manifestUrls']['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 # Unprotected DASH stream
return ResolvedStream( return ResolvedStream(
uuid=uuid, uuid=uuid,
url=data['videoDash']['S'], url=ad_data['stream_manifest'],
stream_type=STREAM_DASH, stream_type=STREAM_DASH,
) )
# Unprotected HLS stream raise UnavailableException
return ResolvedStream(
uuid=uuid,
url=data['video']['S'],
stream_type=STREAM_HLS,
)
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.
@ -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')

View File

@ -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)