From bc558611a5a3dbb2d8141bd5fab83088872c6213 Mon Sep 17 00:00:00 2001 From: Dag Wieers Date: Tue, 30 Apr 2019 02:23:21 +0200 Subject: [PATCH] Include list of channels A central database of all channels is very useful, and opens up the possibility to fall back when web scraping fails. It also makes it possible to start adding audio streams, or studio translations. --- addon.py | 5 + addon.xml | 2 +- resources/lib/kodiwrappers/kodiwrapper.py | 15 +- resources/lib/vrtplayer/__init__.py | 188 +++++++++++++++++++++- resources/lib/vrtplayer/actions.py | 1 + resources/lib/vrtplayer/vrtplayer.py | 30 ++++ test/vrtplayertests.py | 1 + 7 files changed, 238 insertions(+), 4 deletions(-) diff --git a/addon.py b/addon.py index 0d03d1c6..4661fb2e 100644 --- a/addon.py +++ b/addon.py @@ -29,6 +29,7 @@ def router(params_string): api_helper = vrtapihelper.VRTApiHelper(kodi_wrapper) vrt_player = vrtplayer.VRTPlayer(addon.getAddonInfo('path'), kodi_wrapper, stream_service, api_helper) params = dict(parse_qsl(params_string)) + content_type = params.get('content_type') action = params.get('action') if action == actions.LISTING_AZ_TVSHOWS: vrt_player.show_tvshow_menu_items(path=None) @@ -45,6 +46,10 @@ def router(params_string): tv_guide.show_tvguide(params) elif action == actions.PLAY: vrt_player.play(params) + elif action == actions.PLAY_RADIO: + vrt_player.play_radio(params) + elif content_type == 'audio': + vrt_player.show_radio_menu_items() else: vrt_player.show_main_menu_items() diff --git a/addon.xml b/addon.xml index d41388ed..683ee780 100644 --- a/addon.xml +++ b/addon.xml @@ -15,7 +15,7 @@ - video + video audio diff --git a/resources/lib/kodiwrappers/kodiwrapper.py b/resources/lib/kodiwrappers/kodiwrapper.py index 3b314be9..64d22dc5 100644 --- a/resources/lib/kodiwrappers/kodiwrapper.py +++ b/resources/lib/kodiwrappers/kodiwrapper.py @@ -50,7 +50,7 @@ def __init__(self, handle, url, addon): self._addon = addon self._addon_id = addon.getAddonInfo('id') - def show_listing(self, list_items, sort='unsorted', ascending=True, content_type='episodes', cache=True): + def show_listing(self, list_items, sort='unsorted', ascending=True, content_type='episodes', list_type='video', cache=True): listing = [] xbmcplugin.setContent(self._handle, content=content_type) @@ -90,7 +90,7 @@ def show_listing(self, list_items, sort='unsorted', ascending=True, content_type list_item.setArt(title_item.art_dict) if title_item.video_dict: - list_item.setInfo(type='video', infoLabels=title_item.video_dict) + list_item.setInfo(type=list_type, infoLabels=title_item.video_dict) listing.append((url, list_item, not title_item.is_playable)) @@ -121,6 +121,17 @@ def play(self, video): xbmc.sleep(100) xbmc.Player().showSubtitles(subtitles_visible) + def play_radio(self, stream): + play_item = xbmcgui.ListItem(path=stream) + if stream.endswith('.mpd'): + play_item.setProperty('inputstreamaddon', 'inputstream.adaptive') + play_item.setProperty('inputstream.adaptive.manifest_type', 'mpd') + play_item.setMimeType('application/dash+xml') + play_item.setContentLookup(False) + xbmcplugin.setResolvedUrl(self._handle, True, listitem=play_item) + while not xbmc.Player().isPlaying() and not xbmc.Monitor().abortRequested(): + xbmc.sleep(100) + def show_ok_dialog(self, title, message): xbmcgui.Dialog().ok(self._addon.getAddonInfo('name'), title, message) diff --git a/resources/lib/vrtplayer/__init__.py b/resources/lib/vrtplayer/__init__.py index 9901b0f1..18e87338 100644 --- a/resources/lib/vrtplayer/__init__.py +++ b/resources/lib/vrtplayer/__init__.py @@ -30,7 +30,9 @@ id='O8', type='tv', name='Eén', + tagline='', studio='Een', + website='https://een.be/', live_stream='https://www.vrt.be/vrtnu/kanalen/een/', live_stream_id='vualto_een_geo', ), @@ -38,7 +40,9 @@ id='1H', type='tv', name='Canvas', + tagline='', studio='Canvas', + website='https://canvas.be/', live_stream='https://www.vrt.be/vrtnu/kanalen/canvas/', live_stream_id='vualto_canvas_geo', ), @@ -46,7 +50,9 @@ id='O9', type='tv', name='Ketnet', + tagline='', studio='Ketnet', + website='https://ketnet.be/', live_stream='https://www.vrt.be/vrtnu/kanalen/ketnet/', live_stream_id='vualto_ketnet_geo', ), @@ -54,14 +60,24 @@ id='1H', type='tv', name='Ketnet Junior', + tagline='', studio='Ketnet Junior', + website='https://ketnet.be/', ), 'sporza': dict( id='12', type='radio+tv', name='Sporza', + tagline='Kristalheldere sportverslaggeving', studio='Sporza', live_stream_id='vualto_sporza_geo', + website='https://sporza.be/', + live_stream='https://live-aka.vrtcdn.be/groupa/live/bf2f7c79-1d77-4cdc-80e8-47ae024f30ba/live.isml/.mpd', + hls_128='https://live-radio-cf-vrt.akamaized.net/groupc/live/a1211b31-541b-43ce-b6e2-489d9a8995ad/live.isml/.m3u8', + mpeg_dash_128='https://live-radio-cf-vrt.akamaized.net/groupc/live/a1211b31-541b-43ce-b6e2-489d9a8995ad/live.isml/.mpd', + mp3_128='http://icecast.vrtcdn.be/sporza-high.mp3', + mp3_64='http://icecast.vrtcdn.be/sporza-mid.mp3', + aac_128='http://icecast.vrtcdn.be/sporza.aac', ), 'vrtnxt': dict( id='', @@ -73,42 +89,212 @@ id='11', type='radio', name='Radio 1', + tagline='Altijd Benieuwd', studio='Radio 1', + website='https://radio1.be/', + hls_128='https://live-radio-cf-vrt.akamaized.net/groupb/live/47303075-8243-434b-8199-2e62cf4dd97a/live.isml/.m3u8', + mpeg_dash_128='https://live-radio-cf-vrt.akamaized.net/groupb/live/47303075-8243-434b-8199-2e62cf4dd97a/live.isml/.mpd', + mp3_128='http://icecast.vrtcdn.be/radio1-high.mp3', + mp3_64='http://icecast.vrtcdn.be/radio1-mid.mp3', + aac_128='http://icecast.vrtcdn.be/radio1.aac', + ), + 'radio2-antwerpen': dict( + id='21', + type='radio', + name='Radio 2 Antwerpen', + tagline='De grootste familie', + studio='Radio 2', + website='https://radio2.be/', + hls_128='https://live-radio-cf-vrt.akamaized.net/groupb/live/033d312d-31f7-400a-b81a-61195f0b79c5/live.isml/.m3u8', + mpeg_dash_128='https://live-radio-cf-vrt.akamaized.net/groupb/live/033d312d-31f7-400a-b81a-61195f0b79c5/live.isml/.mpd', + mp3_128='http://icecast.vrtcdn.be/ra2ant-high.mp3', + mp3_64='http://icecast.vrtcdn.be/ra2ant-mid.mp3', + aac_128='http://icecast.vrtcdn.be/ra2ant.aac', + ), + 'radio2-vlaams-brabant': dict( + id='22', + type='radio', + name='Radio 2 Vlaams-Brabant', + tagline='De grootste familie', + studio='Radio 2', + website='https://radio2.be/', + hls_128='https://live-radio-cf-vrt.akamaized.net/groupc/live/1e08f370-1f20-4807-aaa3-051c7f0d8359/live.isml/.m3u8', + mpeg_dash_128='https://live-radio-cf-vrt.akamaized.net/groupc/live/1e08f370-1f20-4807-aaa3-051c7f0d8359/live.isml/.mpd', + mp3_128='http://icecast.vrtcdn.be/ra2vlb-high.mp3', + mp3_64='http://icecast.vrtcdn.be/ra2vlb-mid.mp3', + aac_128='http://icecast.vrtcdn.be/ra2vlb.aac', + ), + 'radio2-limburg': dict( + id='23', + type='radio', + name='Radio 2 Limburg', + tagline='De grootste familie', + studio='Radio 2', + website='https://radio2.be/', + hls_128='https://live-radio-cf-vrt.akamaized.net/groupb/live/d9c49923-b49f-4ab3-8532-4e9bd850b4e2/live.isml/.m3u8', + mpeg_dash_128='https://live-radio-cf-vrt.akamaized.net/groupb/live/d9c49923-b49f-4ab3-8532-4e9bd850b4e2/live.isml/.mpd', + mp3_128='http://icecast.vrtcdn.be/ra2lim-high.mp3', + mp3_64='http://icecast.vrtcdn.be/ra2lim-mid.mp3', + aac_128='http://icecast.vrtcdn.be/ra2lim.aac', ), 'radio2': dict( id='24', type='radio', - name='Radio 2', + name='Radio 2 Oost-Vlaanderen', + tagline='De grootste familie', studio='Radio 2', + website='https://radio2.be/', + hls_128='https://live-radio-cf-vrt.akamaized.net/groupb/live/93a8a402-9008-4a97-b473-bc107be7524d/live.isml/.m3u8', + mpeg_dash_128='https://live-radio-cf-vrt.akamaized.net/groupb/live/93a8a402-9008-4a97-b473-bc107be7524d/live.isml/.mpd', + mp3_128='http://icecast.vrtcdn.be/ra2ovl-high.mp3', + mp3_64='http://icecast.vrtcdn.be/ra2ovl-mid.mp3', + aac_128='http://icecast.vrtcdn.be/ra2ovl.aac', + ), + 'radio2-west-vlaanderen': dict( + id='25', + type='radio', + name='Radio 2 West-Vlaanderen', + tagline='De grootste familie', + studio='Radio 2', + website='https://radio2.be/', + hls_128='https://live-radio-cf-vrt.akamaized.net/groupc/live/604e4a0e-22e8-4f99-ad5e-4f62d27dfec4/live.isml/.m3u8', + mpeg_dash_128='https://live-radio-cf-vrt.akamaized.net/groupc/live/604e4a0e-22e8-4f99-ad5e-4f62d27dfec4/live.isml/.mpd', + mp3_128='http://icecast.vrtcdn.be/ra2wvl-high.mp3', + mp3_64='http://icecast.vrtcdn.be/ra2wvl-mid.mp3', + aac_128='http://icecast.vrtcdn.be/ra2wvl.aac', ), 'klara': dict( id='31', type='radio', + tagline='Blijf verwonderd', name='Klara', studio='Klara', + website='https://klara.be/', + hls_128='https://live-radio-cf-vrt.akamaized.net/groupa/live/a9f36fda-cb3c-4b4e-9405-a5bba55654c0/live.isml/.m3u8', + mpeg_dash_128='https://live-radio-cf-vrt.akamaized.net/groupa/live/a9f36fda-cb3c-4b4e-9405-a5bba55654c0/live.isml/.mpd', + mp3_128='http://icecast.vrtcdn.be/klara-high.mp3', + mp3_64='http://icecast.vrtcdn.be/klara-mid.mp3', + aac_128='http://icecast.vrtcdn.be/klara.aac', + ), + 'klara-continuo': dict( + id='32', + type='radio', + tagline='Non-stop klassieke muziek', + name='Klara Continuo', + studio='Klara Continuo', + website='https://klara.be/', + hls_128='https://live-radio-cf-vrt.akamaized.net/groupa/live/0d06dbbe-92d4-4cfe-a0b3-ccc6b7a32ec4/live.isml/.m3u8', + mpeg_dash_128='https://live-radio-cf-vrt.akamaized.net/groupa/live/0d06dbbe-92d4-4cfe-a0b3-ccc6b7a32ec4/live.isml/.mpd', + mp3_128='http://icecast.vrtcdn.be/klaracontinuo-high.mp3', + mp3_64='http://icecast.vrtcdn.be/klaracontinuo-mid.mp3', + aac_128='http://icecast.vrtcdn.be/klaracontinuo.aac', ), 'stubru': dict( id='41', type='radio+tv', name='Studio Brussel', + tagline='Life is Music', studio='Studio Brussel', + website='https://stubru.be/', # live_stream='https://stubru.be/live', live_stream_id='vualto_stubru', + hls_128='https://live-radio-cf-vrt.akamaized.net/groupc/live/f404f0f3-3917-40fd-80b6-a152761072fe/live.isml/.m3u8', + mpeg_dash_128='https://live-radio-cf-vrt.akamaized.net/groupc/live/f404f0f3-3917-40fd-80b6-a152761072fe/live.isml/.mpd', + mp3_128='http://icecast.vrtcdn.be/stubru-high.mp3', + mp3_64='http://icecast.vrtcdn.be/stubru-mid.mp3', + aac_128='http://icecast.vrtcdn.be/stubru.aac', + ), + 'stubru-tijdloze': dict( + id='44', + type='radio', + name='Studio Brussel, De Tijdloze', + tagline='Altijd en overal de beste Tijdloze muziek', + studio='Studio Brussel', + website='https://stubru.be/', + hls_128='https://live-radio-cf-vrt.akamaized.net/groupc/live/582109ca-1e71-4330-93fc-e9affee94d7d/live.isml/.m3u8', + mpeg_dash_128='https://live-radio-cf-vrt.akamaized.net/groupc/live/582109ca-1e71-4330-93fc-e9affee94d7d/live.isml/.mpd', + mp3_128='http://icecast.vrtcdn.be/stubru_tijdloze-high.mp3', + mp3_64='http://icecast.vrtcdn.be/stubru_tijdloze-mid.mp3', + aac_128='http://icecast.vrtcdn.be/stubru_tijdloze.aac', ), 'mnm': dict( id='55', type='radio+tv', name='MNM', + tagline='Music and More', studio='MNM', + website='https://mnm.be/', # live_stream='https://mnm.be/kijk/live', live_stream_id='vualto_mnm', + hls_128='https://live-radio-cf-vrt.akamaized.net/groupa/live/68dc3b80-040e-4a75-a394-72f3bb7aff9a/live.isml/.m3u8', + mpeg_dash_128='https://live-radio-cf-vrt.akamaized.net/groupa/live/68dc3b80-040e-4a75-a394-72f3bb7aff9a/live.isml/.mpd', + mp3_128='http://icecast.vrtcdn.be/mnm-high.mp3', + mp3_64='http://icecast.vrtcdn.be/mnm-mid.mp3', + aac_128='http://icecast.vrtcdn.be/mnm.aac', + ), + 'mnm-hits': dict( + id='56', + type='radio', + name='MNM Hits', + tagline='Music and More - The Hits', + studio='MNM', + website='https://mnm.be/', + hls_128='https://live-radio-cf-vrt.akamaized.net/groupb/live/35dd91de-0352-4865-8632-17e5af8dc6ba/live.isml/.m3u8', + mpeg_dash_128='https://live-radio-cf-vrt.akamaized.net/groupb/live/35dd91de-0352-4865-8632-17e5af8dc6ba/live.isml/.mpd', + mp3_128='http://icecast.vrtcdn.be/mnm_hits-high.mp3', + mp3_64='http://icecast.vrtcdn.be/mnm_hits-mid.mp3', + aac_128='http://icecast.vrtcdn.be/mnm_hits.aac', + ), + 'mnm-urbanice': dict( + id='57', + type='radio', + name='MNM UrbaNice', + tagline='De Online Urban Stream van MNM', + studio='MNM', + website='https://mnm.be/', + hls_128='https://live-radio-cf-vrt.akamaized.net/groupa/live/da0b681c-73db-4c9e-af32-7921591d3fbd/live.isml/.m3u8', + mpeg_dash_128='https://live-radio-cf-vrt.akamaized.net/groupa/live/da0b681c-73db-4c9e-af32-7921591d3fbd/live.isml/.mpd', + mp3_128='http://icecast.vrtcdn.be/mnm_urb-high.mp3', + mp3_64='http://icecast.vrtcdn.be/mnm_urb-mid.mp3', + aac_128='http://icecast.vrtcdn.be/mnm_urb.aac', + ), + 'ketnet-hits': dict( + id='O3', + type='radio', + name='Ketnet Hits', + tagline='De hipste, de coolste én de plezantste hits op een rijtje', + studio='Ketnet', + website='https://ketnet.be/', + hls_128='https://live-radio-cf-vrt.akamaized.net/groupa/live/014a9eea-af85-4da6-aab2-c472ca8d0149/live.isml/.m3u8', + mpeg_dash_128='https://live-radio-cf-vrt.akamaized.net/groupa/live/014a9eea-af85-4da6-aab2-c472ca8d0149/live.isml/.mpd', + mp3_128='http://icecast.vrtcdn.be/ketnetradio-high.mp3', + mp3_64='http://icecast.vrtcdn.be/ketnetradio-mid.mp3', + aac_128='http://icecast.vrtcdn.be/ketnetradio.aac', ), 'vrtnws': dict( id='13', type='radio+tv', name='VRT NWS', + tagline='Ieder moment het meest recente nieuws', studio='VRT NWS', live_stream_id='vualto_nieuws', # live_stream_id='vualto_journaal', + website='https://www.vrtnieuws.be/', + hls_128='https://ondemand-radio-cf-vrt.akamaized.net/audioonly/content/fixed/11_11niws-snip_hi.mp4/.m3u8', + mpeg_dash_128='https://ondemand-radio-cf-vrt.akamaized.net/audioonly/content/fixed/11_11niws-snip_hi.mp4/.mpd', + mp3_128='https://progressive-audio.lwc.vrtcdn.be/content/fixed/11_11niws-snip_hi.mp3', + ), + 'vrt-event': dict( + id='71', + type='radio', + name='VRT Event', + tagline='', + studio='VRT', + website='https://vrt.be/', + hls_128='https://live-radio-cf-vrt.akamaized.net/groupa/live/779d53fc-9472-4fe8-b62a-1d38c5878c60/live.isml/.m3u8', + mpeg_dash_128='https://live-radio-cf-vrt.akamaized.net/groupa/live/779d53fc-9472-4fe8-b62a-1d38c5878c60/live.isml/.mpd', + mp3_128='http://icecast.vrtcdn.be/vrtevent-high.mp3', + mp3_64='http://icecast.vrtcdn.be/vrtevent-mid.mp3', + aac_128='http://icecast.vrtcdn.be/vrtevent.aac', ), } diff --git a/resources/lib/vrtplayer/actions.py b/resources/lib/vrtplayer/actions.py index 96ec0ee9..84ba3b5a 100644 --- a/resources/lib/vrtplayer/actions.py +++ b/resources/lib/vrtplayer/actions.py @@ -14,3 +14,4 @@ LISTING_TVGUIDE = 'listingtvguide' PLAY = 'play' +PLAY_RADIO = 'play_radio' diff --git a/resources/lib/vrtplayer/vrtplayer.py b/resources/lib/vrtplayer/vrtplayer.py index 08ca5aa8..fce67239 100644 --- a/resources/lib/vrtplayer/vrtplayer.py +++ b/resources/lib/vrtplayer/vrtplayer.py @@ -119,6 +119,29 @@ def show_livestream_items(self): self._kodi_wrapper.show_listing(livestream_items, sort='unsorted', content_type='videos', cache=False) + def show_radio_menu_items(self): + radio_items = [] + for channel in CHANNELS: + if 'radio' not in CHANNELS[channel].get('type'): + continue + if CHANNELS[channel].get('tagline'): + label = '%(name)s [I][COLOR blue]-- %(tagline)s[/COLOR][/I]' % CHANNELS[channel] + else: + label = CHANNELS[channel].get('name') + radio_items.append(helperobjects.TitleItem( + title=label, + # Only MP3 support includes song information + url_dict=dict(action=actions.PLAY_RADIO, radio_stream=CHANNELS[channel].get('mp3_128')), + is_playable=True, + art_dict=dict(thumb='DefaultAddonMusic.png', icon='DefaultAddonMusic.png', fanart='DefaultAddonMusic.png'), + video_dict=dict( + plot='[B]%(name)s[/B]\n[I]%(tagline)s[/I]\n\n[COLOR yellow]%(website)s[/COLOR]' % CHANNELS[channel], + mediatype='music', + ), + )) + + self._kodi_wrapper.show_listing(radio_items, sort='label', content_type='music', list_type='video') + def show_episodes(self, path): episode_items, sort, ascending = self._api_helper.get_episode_items(path) self._kodi_wrapper.show_listing(episode_items, sort=sort, ascending=ascending, content_type='episodes', cache=False) @@ -128,6 +151,13 @@ def play(self, params): if stream is not None: self._kodi_wrapper.play(stream) + def play_radio(self, params): + if 'channel' in params: + stream = CHANNELS[params.get('channel')].get('mp3_128') + else: + stream = params.get('radio_stream') + self._kodi_wrapper.play_radio(stream) + def __get_media(self, file_name): return os.path.join(self._addon_path, 'resources', 'media', file_name) diff --git a/test/vrtplayertests.py b/test/vrtplayertests.py index 1a8fee8b..3cfc7d7d 100644 --- a/test/vrtplayertests.py +++ b/test/vrtplayertests.py @@ -87,6 +87,7 @@ def test_categories_scraping(self): ''' Test to ensure our hardcoded categories conforms to scraped categories ''' # Remove thumbnails from scraped categories first categories = [dict(id=c['id'], name=c['name']) for c in vrtplayer.get_categories()] + self.assertTrue(categories) self.assertEqual(categories, CATEGORIES) def test_random_tvshow_episodes(self):