diff --git a/resources/lib/kodiutils.py b/resources/lib/kodiutils.py index 156b8a2..67d9091 100644 --- a/resources/lib/kodiutils.py +++ b/resources/lib/kodiutils.py @@ -191,25 +191,36 @@ def show_listing(title_items, category=None, sort=None, content=None, cache=True def play(stream, title=None, art_dict=None, info_dict=None, prop_dict=None): """Play the given stream""" from resources.lib.addon import routing + playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO) + playlist.clear() + + 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) + + # Setup Inputstream Adaptive + if kodi_version_major() >= 19: + play_item.setProperty('inputstream', 'inputstream.adaptive') + else: + play_item.setProperty('inputstreamaddon', 'inputstream.adaptive') + play_item.setProperty('inputstream.adaptive.manifest_type', 'hls') + play_item.setMimeType('application/vnd.apple.mpegurl') + play_item.setContentLookup(False) - 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) - - # Setup Inputstream Adaptive - if kodi_version_major() >= 19: - play_item.setProperty('inputstream', 'inputstream.adaptive') - else: - play_item.setProperty('inputstreamaddon', 'inputstream.adaptive') - play_item.setProperty('inputstream.adaptive.manifest_type', 'hls') - play_item.setMimeType('application/vnd.apple.mpegurl') - 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=play_item) + 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 5feeea5..c390f6b 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. @@ -60,6 +64,8 @@ def play_from_page(self, channel, path): _LOGGER.debug('Resolved stream: %s', resolved_stream) if resolved_stream: + ad_streams.append(resolved_stream) + resolved_stream = ad_streams titleitem = Menu.generate_titleitem(episode) kodiutils.play(resolved_stream, info_dict=titleitem.info_dict, art_dict=titleitem.art_dict, prop_dict=titleitem.prop_dict) diff --git a/resources/lib/viervijfzes/content.py b/resources/lib/viervijfzes/content.py index fd97279..d744927 100644 --- a/resources/lib/viervijfzes/content.py +++ b/resources/lib/viervijfzes/content.py @@ -97,10 +97,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 @@ -119,6 +120,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 @@ -376,6 +378,69 @@ 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).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) + ad_streams = [item.text for item in tree.findall('.//MediaFile[@delivery="streaming"]') if item.text.endswith('.m3u8')] + return ad_streams + @staticmethod def _extract_programs(html, channel): """ Extract Programs from HTML code """ @@ -536,6 +601,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'), diff --git a/tests/xbmc.py b/tests/xbmc.py index 88e31d5..db7d7bc 100644 --- a/tests/xbmc.py +++ b/tests/xbmc.py @@ -45,6 +45,9 @@ GLOBAL_SETTINGS = global_settings() PO = import_language(language=GLOBAL_SETTINGS.get('locale.language')) +PLAYLIST_MUSIC = 0 +PLAYLIST_VIDEO = 1 + def to_unicode(text, encoding='utf-8'): """ Force text to unicode """ @@ -135,6 +138,26 @@ def getPlayingFile(self): return '' +class PlayList(object): # pylint: disable=useless-object-inheritance + """ A stub implementation of the xbmc PlayList class """ + + def __init__(self, playList): + """ A stub constructor for the xbmc PlayList class """ + + def getposition(self): + """ A stub implementation for the xbmc PlayList class getposition() method """ + return 0 + + def add(self, url, listitem=None, index=-1): + """ A stub implementation for the xbmc PlayList class add() method """ + + def clear(self): + """ A stub implementation for the xbmc PlayList class clear() method """ + + def size(self): + """ A stub implementation for the xbmc PlayList class size() method """ + + class VideoInfoTag: """ A stub implementation of the xbmc VideoInfoTag class """