From 358d9e29feccc14a0942d8c66c1a53f77fbec172 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Arnauts?= Date: Sun, 24 Jan 2021 11:44:45 +0100 Subject: [PATCH] Update --- resources/lib/addon.py | 2 +- resources/lib/modules/contextmenu.py | 8 ++-- resources/lib/modules/iptvsimple.py | 56 ++++++++++++++++++-------- resources/lib/modules/menu.py | 44 +++++++++++--------- resources/lib/modules/sources.py | 9 ++++- tests/test_integration.py | 11 ++--- tests/test_iptvsimple.py | 4 +- tests/test_sources.py | 60 ++++++++++++++++++++++++++++ 8 files changed, 145 insertions(+), 49 deletions(-) diff --git a/resources/lib/addon.py b/resources/lib/addon.py index 06f094a..48398a2 100644 --- a/resources/lib/addon.py +++ b/resources/lib/addon.py @@ -82,7 +82,7 @@ def disable_source(addon_id): def install(): """ Setup IPTV Simple """ from resources.lib.modules.menu import Menu - Menu().install() + Menu().show_install() @routing.route('/play') diff --git a/resources/lib/modules/contextmenu.py b/resources/lib/modules/contextmenu.py index b8db716..fdea31b 100644 --- a/resources/lib/modules/contextmenu.py +++ b/resources/lib/modules/contextmenu.py @@ -13,13 +13,13 @@ class ContextMenu: - """Helper class for PVR Context Menu handling""" + """ Helper class for PVR Context Menu handling (used in Kodi 18) """ def __init__(self): - """Initialise the Context Menu Module""" + """ Initialise object """ def play(self): - """ Play from Context Menu (used in Kodi 18) """ + """ Play from Context Menu """ stream = self.get_direct_uri() if stream is None: kodiutils.ok_dialog(message=kodiutils.localize(30706)) @@ -30,7 +30,7 @@ def play(self): @staticmethod def get_direct_uri(): - """Retrieve a direct URI from the selected ListItem.""" + """ Retrieve a direct URI from the selected ListItem. """ # We use a clever way / ugly hack (pick your choice) to hide the direct stream in Kodi 18. # Title [COLOR green]•[/COLOR][COLOR vod="plugin://plugin.video.example/play/whatever"][/COLOR] label = sys.listitem.getLabel() # pylint: disable=no-member diff --git a/resources/lib/modules/iptvsimple.py b/resources/lib/modules/iptvsimple.py index 1d1f040..3547e95 100644 --- a/resources/lib/modules/iptvsimple.py +++ b/resources/lib/modules/iptvsimple.py @@ -25,7 +25,42 @@ class IptvSimple: restart_required = False def __init__(self): - """Init""" + """Initialise object""" + + @classmethod + def _get_settings(cls): + """Return a dictionary with the required settings.""" + output_dir = kodiutils.addon_profile() + return { + 'm3uPathType': '0', # Local path + 'm3uPath': os.path.join(output_dir, IPTV_SIMPLE_PLAYLIST), + 'epgPathType': '0', # Local path + 'epgPath': os.path.join(output_dir, IPTV_SIMPLE_EPG), + 'epgCache': 'true', + 'epgTimeShift': '0', + 'logoPathType': '0', # Local path + 'logoPath': '/', + 'catchupEnabled': 'true', # Allow playback from the guide in Matrix + 'allChannelsCatchupMode': '1', # Allow to specify the vod mode per channel + 'catchupOnlyOnFinishedProgrammes': 'false', # Allow vod also on future programs + } + + @classmethod + def check(cls): + """Check if IPTV Simple is configured correctly.""" + try: + addon = kodiutils.get_addon(IPTV_SIMPLE_ID) + except Exception as exc: # pylint: disable=broad-except + _LOGGER.warning('Could not find IPTV Simple: %s', str(exc)) + return False + + # Validate IPTV Simple configuration + settings = cls._get_settings() + for key, value in settings.items(): + if value != addon.getSetting(key): + return False + + return True @classmethod def setup(cls): @@ -45,22 +80,9 @@ def setup(cls): cls._deactivate() # Configure IPTV Simple - output_dir = kodiutils.addon_profile() - - addon.setSetting('m3uPathType', '0') # Local path - addon.setSetting('m3uPath', os.path.join(output_dir, IPTV_SIMPLE_PLAYLIST)) - - addon.setSetting('epgPathType', '0') # Local path - addon.setSetting('epgPath', os.path.join(output_dir, IPTV_SIMPLE_EPG)) - addon.setSetting('epgCache', 'true') - addon.setSetting('epgTimeShift', '0') - - addon.setSetting('logoPathType', '0') # Local path - addon.setSetting('logoPath', '/') - - addon.setSetting('catchupEnabled', 'true') - addon.setSetting('allChannelsCatchupMode', '1') - addon.setSetting('catchupOnlyOnFinishedProgrammes', 'false') + settings = cls._get_settings() + for key, value in settings.items(): + addon.setSetting(key, value) # Activate IPTV Simple cls._activate() diff --git a/resources/lib/modules/menu.py b/resources/lib/modules/menu.py index 493b146..ee49b78 100644 --- a/resources/lib/modules/menu.py +++ b/resources/lib/modules/menu.py @@ -25,6 +25,15 @@ def show_mainmenu(): """ Show the main menu. """ listing = [] + if not IptvSimple.check(): + listing.append(TitleItem( + title=kodiutils.localize(30822), + path=kodiutils.url_for('install'), + art_dict=dict( + icon='DefaultAddonService.png', + ), + )) + listing.append(TitleItem( title=kodiutils.localize(30803), path=kodiutils.url_for('refresh'), @@ -33,37 +42,32 @@ def show_mainmenu(): ), )) - listing.append(TitleItem( - title='IPTV Manager Settings…', - path=kodiutils.url_for('show_settings'), - art_dict=dict( - icon='DefaultAddonService.png', - ), - info_dict=dict( - plot='IPTV Manager Settings…', - ), - )) + # listing.append(TitleItem( + # title='IPTV Manager Settings…', # TODO: translate + # path=kodiutils.url_for('show_settings'), + # art_dict=dict( + # icon='DefaultAddonService.png', + # ), + # info_dict=dict( + # plot='IPTV Manager Settings…', # TODO: translate + # ), + # )) listing.append(TitleItem( - title='Manage Sources…', + title='Manage Sources…', # TODO: translate path=kodiutils.url_for('show_sources'), art_dict=dict( icon='DefaultPlaylist.png', ), info_dict=dict( - plot='Manage Sources…', + plot='Manage Sources…', # TODO: translate ), )) kodiutils.show_listing(listing, sort=['unsorted']) @staticmethod - def show_settings(): - """ Show the sources menu. """ - kodiutils.open_settings() - - @staticmethod - def install(): + def show_install(): """ Setup IPTV Simple """ reply = kodiutils.yesno_dialog(message=kodiutils.localize(30700)) # Are you sure... if reply: @@ -72,7 +76,9 @@ def install(): else: kodiutils.ok_dialog(message=kodiutils.localize(30702)) # The configuration of IPTV Simple has failed! - # Open settings again + @staticmethod + def show_settings(): + """ Show the sources menu. """ kodiutils.open_settings() @staticmethod diff --git a/resources/lib/modules/sources.py b/resources/lib/modules/sources.py index 14e04fe..6486860 100644 --- a/resources/lib/modules/sources.py +++ b/resources/lib/modules/sources.py @@ -355,10 +355,15 @@ def delete(self): with open(os.path.join(kodiutils.addon_profile(), Sources.SOURCES_FILE), 'w') as fdesc: json.dump(sources, fdesc) - def get_channels(self): + @staticmethod + def get_channels(): """Get channel data from this source""" + + # TODO return [] - def get_epg(self): + @staticmethod + def get_epg(): """Get epg data from this source""" + # TODO return [] diff --git a/tests/test_integration.py b/tests/test_integration.py index 0a1a092..35bd0a4 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -6,6 +6,7 @@ from __future__ import absolute_import, division, print_function, unicode_literals import os +import sys import time import unittest from xml.etree import ElementTree as etree @@ -15,6 +16,7 @@ from xbmcgui import ListItem from resources.lib import kodiutils +from resources.lib.modules.contextmenu import ContextMenu from resources.lib.modules.sources import Sources @@ -22,7 +24,7 @@ class IntegrationTest(unittest.TestCase): """Integration Tests""" def test_refresh(self): - """Test the refreshing of data""" + """Test the refreshing of data.""" m3u_path = 'tests/home/userdata/addon_data/service.iptv.manager/playlist.m3u8' epg_path = 'tests/home/userdata/addon_data/service.iptv.manager/epg.xml' @@ -52,14 +54,13 @@ def test_refresh(self): self.assertIsNotNone(xml.find('./channel[@id="channel1.com"]')) self.assertIsNotNone(xml.find('./channel[@id="één.be"]')) - # Now, try playing something from the Guide - import sys + def test_play_from_guide(self): + """Play something from the guide.""" sys.listitem = ListItem(label='Example Show [COLOR green]•[/COLOR][COLOR vod="plugin://plugin.video.example/play/something"][/COLOR]', path='pvr://guide/0006/2020-05-23 11:35:00.epg') # Try to play it - from resources.lib.functions import play_from_contextmenu - play_from_contextmenu() + ContextMenu().play() # Check that something is playing player = xbmc.Player() diff --git a/tests/test_iptvsimple.py b/tests/test_iptvsimple.py index 72fcc2f..6fde622 100644 --- a/tests/test_iptvsimple.py +++ b/tests/test_iptvsimple.py @@ -15,7 +15,9 @@ class IptvSimpleTest(unittest.TestCase): def test_setup(self): """Test the setup of IPTV Simple (this will be mocked)""" - self.assertTrue(IptvSimple.setup()) + self.assertFalse(IptvSimple.check()) # Configuration will be incorrect + self.assertTrue(IptvSimple.setup()) # Setup configuration + self.assertTrue(IptvSimple.check()) # Configuration will be correct def test_restart(self): """Test the restart of IPTV Simple (this will be mocked)""" diff --git a/tests/test_sources.py b/tests/test_sources.py index 3088a11..8ce6ac6 100644 --- a/tests/test_sources.py +++ b/tests/test_sources.py @@ -8,9 +8,29 @@ import unittest from uuid import uuid4 +from mock import patch + from resources.lib import kodiutils from resources.lib.modules.sources import CustomSource, Sources +TEST_PLAYLIST = """ +#EXTM3U +#EXTINF:-1 tvg-name="Test 1" tvg-id="test1.com" tvg-logo="https://example.com/test1.png" tvg-chno="1" group-title="Test IPTV Addon" catchup="vod",Test 1 +plugin://plugin.video.test/play/live +""" + +TEST_EPG = """ + + + + + + Test 1 + Test 1 description + + +""" + class SourcesTest(unittest.TestCase): @@ -42,6 +62,46 @@ def test_create(self): self.assertIn(key, sources.keys()) self.assertEqual(sources.get(key).enabled, True) + def test_fetch(self): + + def mocked_requests_get(*args, **kwargs): + class MockResponse: + def __init__(self, json_data, status_code): + self.json_data = json_data + self.status_code = status_code + + def json(self): + return self.json_data + + if args[0].endsWith('m3u'): + return MockResponse(TEST_PLAYLIST, 200) + elif args[0].endsWith('xml'): + return MockResponse(TEST_EPG, 200) + + return MockResponse(None, 404) + + with patch('requests.get', side_effect=mocked_requests_get): + source = CustomSource(uuid=str(uuid4()), + name='Test Source', + enabled=False, + playlist_uri='https://example.com/playlist.m3u', + playlist_type=CustomSource.TYPE_URL, + epg_uri='https://example.com/playlist.m3u', + epg_type=CustomSource.TYPE_URL, + ) + + channels = source.get_channels() + + print(channels) + + + epg = source.get_epg() + + print(epg) + + exit() + + if __name__ == '__main__': unittest.main()