From 44d944ec40cdff9b5ec66ab8ba39d00b58a1a6dd Mon Sep 17 00:00:00 2001 From: mediaminister Date: Wed, 11 Nov 2020 10:48:53 +0100 Subject: [PATCH] Play advertisements --- resources/lib/kodiutils.py | 80 +++++++++++++++++----------- resources/lib/modules/player.py | 7 ++- resources/lib/viervijfzes/content.py | 74 ++++++++++++++++++++++++- 3 files changed, 126 insertions(+), 35 deletions(-) diff --git a/resources/lib/kodiutils.py b/resources/lib/kodiutils.py index 60f30a2..42356ef 100644 --- a/resources/lib/kodiutils.py +++ b/resources/lib/kodiutils.py @@ -193,40 +193,56 @@ def show_listing(title_items, category=None, sort=None, content=None, cache=True xbmcplugin.endOfDirectory(routing.handle, succeeded, cacheToDisc=cache) -def play(stream, stream_type=STREAM_HLS, license_key=None, title=None, art_dict=None, info_dict=None, prop_dict=None, stream_dict=None): +def play(stream, stream_type=STREAM_HLS, license_key=None, title=None, art_dict=None, info_dict=None, prop_dict=None, stream_dict=None, ads_list=None): """Play the given stream""" from resources.lib.addon import routing - - play_item = xbmcgui.ListItem(label=title, path=stream) - if art_dict: - play_item.setArt(art_dict) - if info_dict: - play_item.setInfo(type='video', infoLabels=info_dict) - if prop_dict: - play_item.setProperties(prop_dict) - if stream_dict: - play_item.addStreamInfo('video', stream_dict) - - # Setup Inputstream Adaptive - if kodi_version_major() >= 19: - play_item.setProperty('inputstream', 'inputstream.adaptive') - else: - play_item.setProperty('inputstreamaddon', 'inputstream.adaptive') - - if stream_type == STREAM_HLS: - play_item.setProperty('inputstream.adaptive.manifest_type', 'hls') - play_item.setMimeType('application/vnd.apple.mpegurl') - - elif stream_type == STREAM_DASH: - play_item.setProperty('inputstream.adaptive.manifest_type', 'mpd') - play_item.setMimeType('application/dash+xml') - if license_key: - play_item.setProperty('inputstream.adaptive.license_type', 'com.widevine.alpha') - play_item.setProperty('inputstream.adaptive.license_key', license_key) - - play_item.setContentLookup(False) - - xbmcplugin.setResolvedUrl(routing.handle, True, listitem=play_item) + playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO) + playlist.clear() + + if ads_list: + ads_list.append(stream) + stream = ads_list + + if not isinstance(stream, list): + stream = [stream] + + for i, url in enumerate(stream): + play_item = xbmcgui.ListItem(label=title, path=url) + if art_dict: + play_item.setArt(art_dict) + if info_dict: + play_item.setInfo(type='video', infoLabels=info_dict) + if prop_dict: + play_item.setProperties(prop_dict) + if stream_dict: + play_item.addStreamInfo('video', stream_dict) + + if not url.endswith('.mp4'): + # Setup Inputstream Adaptive + if kodi_version_major() >= 19: + play_item.setProperty('inputstream', 'inputstream.adaptive') + else: + play_item.setProperty('inputstreamaddon', 'inputstream.adaptive') + + if stream_type == STREAM_HLS: + play_item.setProperty('inputstream.adaptive.manifest_type', 'hls') + play_item.setMimeType('application/vnd.apple.mpegurl') + + elif stream_type == STREAM_DASH: + play_item.setProperty('inputstream.adaptive.manifest_type', 'mpd') + play_item.setMimeType('application/dash+xml') + if license_key: + play_item.setProperty('inputstream.adaptive.license_type', 'com.widevine.alpha') + play_item.setProperty('inputstream.adaptive.license_key', license_key) + + play_item.setContentLookup(False) + + if i == 0: + first_item = play_item + + playlist.add(url=url, listitem=play_item, index=i) + + xbmcplugin.setResolvedUrl(routing.handle, True, listitem=first_item) def get_search_string(heading='', message=''): diff --git a/resources/lib/modules/player.py b/resources/lib/modules/player.py index b111607..6ce3ecb 100644 --- a/resources/lib/modules/player.py +++ b/resources/lib/modules/player.py @@ -48,6 +48,10 @@ def play_from_page(self, channel, path): kodiutils.ok_dialog(message=kodiutils.localize(30712)) return + # Get advertisements + ad_streams = self._api.get_ad_streams(episode.channel, episode.program_title, path, episode.uuid, episode.video_type, kodiutils.get_setting('username')) + _LOGGER.info('Advertisements: %s', ad_streams) + if episode.stream: # We already have a resolved stream. Nice! # We don't need credentials for these streams. @@ -78,7 +82,8 @@ def play_from_page(self, channel, path): license_key, info_dict=titleitem.info_dict, art_dict=titleitem.art_dict, - prop_dict=titleitem.prop_dict) + prop_dict=titleitem.prop_dict, + ads_list=ad_streams) def play(self, uuid): """ Play the requested item. diff --git a/resources/lib/viervijfzes/content.py b/resources/lib/viervijfzes/content.py index 12fbfcb..8a0ed98 100644 --- a/resources/lib/viervijfzes/content.py +++ b/resources/lib/viervijfzes/content.py @@ -98,10 +98,11 @@ def __repr__(self): class Episode: """ Defines an Episode. """ - def __init__(self, uuid=None, nodeid=None, path=None, channel=None, program_title=None, title=None, description=None, cover=None, background=None, - duration=None, season=None, season_uuid=None, number=None, rating=None, aired=None, expiry=None, stream=None): + def __init__(self, uuid=None, video_type=None, nodeid=None, path=None, channel=None, program_title=None, title=None, description=None, cover=None, + background=None, duration=None, season=None, season_uuid=None, number=None, rating=None, aired=None, expiry=None, stream=None): """ :type uuid: str + :type video_type: str :type nodeid: str :type path: str :type channel: str @@ -120,6 +121,7 @@ def __init__(self, uuid=None, nodeid=None, path=None, channel=None, program_titl :type stream: string """ self.uuid = uuid + self.video_type = video_type self.nodeid = nodeid self.path = path self.channel = channel @@ -401,6 +403,73 @@ def get_categories(self, channel): return categories + def get_weather(self, channel): + """ Get a weather dictionary. + :type channel: str + :rtype dict + """ + response = self._get_url(self.SITE_APIS[channel] + '/weather', authentication=True) + weather = json.loads(response) + return weather + + def get_ad_streams(self, channel, program, path, uuid, video_type, username): + """ Get a list of advertisement stream URLs to use for this video. + :type channel: str + :type path: str + :rtype list + """ + ad_streams = [] + ad_url = 'https://pubads.g.doubleclick.net/gampad/ads' + weather = self.get_weather(channel) + channel_info = dict( + vier=dict(cmsid='2493239', network_id='21797861328'), + vijf=dict(cmsid='2493512', network_id='21797861328'), + zes=dict(cmsid='2496240', network_id='21797861328') + ) + network_id = channel_info.get(channel).get('network_id') + from unicodedata import normalize + program = normalize('NFD', program).replace(' ', '-') + program = re.sub(r'[^A-Za-z0-9-]+', '', program).lower() + from hashlib import sha1 + ppid = sha1(username.encode('utf-8')).hexdigest() + if program: + iu_id = '/{}/{}/{}/{}'.format(network_id, channel, 'programmas', program) + else: + iu_id = '/{}/{}/'.format(network_id, channel) + params = dict(ad_rule=1, + cmsid=channel_info.get(channel).get('cmsid'), + correlator=int(round(time.time() * 1000)), + sbs_weather_cond=weather.get('summary'), + sbs_weather_temp=weather.get('temperature'), + description_url=path, + env='vp', + gdfp_req=1, + impl='s', + iu=iu_id, + output='vast', + pp='SBSNoDash', + ppid=ppid, + sz='640x360', + unviewed_position_start=1, + url=path, + vid=uuid, + video_type=video_type) + + xml = self._get_url(ad_url, params) + import xml.etree.ElementTree as ET + tree = ET.fromstring(xml) + for item in tree: + if item.tag == 'Preroll': + url = item.find('Ad').text + xml = self._get_url(url) + tree = ET.fromstring(xml) + for adv in tree.findall('.//Ad'): + for stream in adv.findall('.//MediaFile'): + if stream.get('type') == 'video/mp4' and stream.get('width') == '1920': + ad_streams.append(stream.text) + break + return ad_streams + @staticmethod def _extract_programs(html, channel): """ Extract Programs from HTML code """ @@ -561,6 +630,7 @@ def _parse_episode_data(data, season_uuid=None): episode = Episode( uuid=data.get('videoUuid'), + video_type=data.get('type', {}), nodeid=data.get('pageInfo', {}).get('nodeId'), path=data.get('link').lstrip('/'), channel=data.get('pageInfo', {}).get('site'),