diff --git a/addon.py b/addon.py
index 4c35c29be..48015b0f3 100644
--- a/addon.py
+++ b/addon.py
@@ -52,7 +52,7 @@ def router(params_string):
_favorites.unfollow(program=params.get('program'), path=params.get('path'))
return
if action == actions.REFRESH_FAVORITES:
- _favorites.update_favorites()
+ _favorites.get_favorites(ttl=0)
return
from resources.lib.vrtplayer import vrtapihelper, vrtplayer
diff --git a/resources/language/resource.language.en_gb/strings.po b/resources/language/resource.language.en_gb/strings.po
index 55c3df3cc..f9abe76df 100644
--- a/resources/language/resource.language.en_gb/strings.po
+++ b/resources/language/resource.language.en_gb/strings.po
@@ -339,26 +339,30 @@ msgid "Refresh favorites"
msgstr ""
msgctxt "#30867"
-msgid "Streaming"
+msgid "Enable HTTP caching [COLOR gray][I](experimental)[/I][/COLOR]"
msgstr ""
msgctxt "#30869"
-msgid "Use InputStream Adaptive"
+msgid "Streaming"
msgstr ""
msgctxt "#30871"
-msgid "InputStream Adaptive settings..."
+msgid "Use InputStream Adaptive"
msgstr ""
msgctxt "#30873"
-msgid "Install Widevine... [COLOR gray][I](needed for DRM content)[/I][/COLOR]"
+msgid "InputStream Adaptive settings..."
msgstr ""
msgctxt "#30875"
-msgid "Logging"
+msgid "Install Widevine... [COLOR gray][I](needed for DRM content)[/I][/COLOR]"
msgstr ""
msgctxt "#30877"
+msgid "Logging"
+msgstr ""
+
+msgctxt "#30879"
msgid "Log level"
msgstr ""
diff --git a/resources/language/resource.language.nl_nl/strings.po b/resources/language/resource.language.nl_nl/strings.po
index 7f0568406..5700f378a 100644
--- a/resources/language/resource.language.nl_nl/strings.po
+++ b/resources/language/resource.language.nl_nl/strings.po
@@ -315,27 +315,31 @@ msgctxt "#30065"
msgid "Refresh favorites"
msgstr "Ververs gevolgde programma's"
-msgctxt "#30067"
+msgctxt "#30867"
+msgid "Enable HTTP caching [COLOR gray][I](experimental)[/I][/COLOR]"
+msgstr "Gebruik HTTP caching [COLOR gray][I](experimenteel)[/I][/COLOR]"
+
+msgctxt "#30069"
msgid "Streaming"
msgstr "Streaming"
-msgctxt "#30869"
+msgctxt "#30870"
msgid "Use InputStream Adaptive"
msgstr "Gebruik InputStream Adaptive"
-msgctxt "#30071"
+msgctxt "#30073"
msgid "InputStream Adaptive settings..."
msgstr "InputStream Adaptive instellingen..."
-msgctxt "#30073"
+msgctxt "#30075"
msgid "Install Widevine... [COLOR gray][I](needed for DRM content)[/I][/COLOR]"
msgstr "Installeer Widevine... [COLOR gray][I](nodig voor DRM content)[/I][/COLOR]"
-msgctxt "#30075"
+msgctxt "#30077"
msgid "Logging"
msgstr "Logboek"
-msgctxt "#30077"
+msgctxt "#30079"
msgid "Log level"
msgstr "Log level"
diff --git a/resources/lib/kodiwrappers/kodiwrapper.py b/resources/lib/kodiwrappers/kodiwrapper.py
index 409c80141..6d4c4c303 100644
--- a/resources/lib/kodiwrappers/kodiwrapper.py
+++ b/resources/lib/kodiwrappers/kodiwrapper.py
@@ -106,11 +106,11 @@ def __init__(self, handle, url, addon):
self._addon_id = addon.getAddonInfo('id')
self._max_log_level = log_levels.get(self.get_setting('max_log_level'), 3)
self._usemenucaching = self.get_setting('usemenucaching') == 'true'
+ self._cache_path = self.get_userdata_path() + 'cache/'
self._system_locale_works = self.set_locale()
def install_widevine(self):
- import xbmcgui
- ok = xbmcgui.Dialog().yesno(self.localize(30971), self.localize(30972))
+ ok = self.show_yesno_dialog(heading=self.localize(30971), message=self.localize(30972))
if not ok:
return
try:
@@ -227,7 +227,7 @@ def show_ok_dialog(self, heading='', message=''):
import xbmcgui
if not heading:
heading = self._addon.getAddonInfo('name')
- xbmcgui.Dialog().ok(heading=heading, message=message)
+ xbmcgui.Dialog().ok(heading=heading, line1=message)
def show_notification(self, heading='', message='', icon='info', time=4000):
import xbmcgui
@@ -235,6 +235,12 @@ def show_notification(self, heading='', message='', icon='info', time=4000):
heading = self._addon.getAddonInfo('name')
xbmcgui.Dialog().notification(heading=heading, message=message, icon=icon, time=time)
+ def show_yesno_dialog(self, heading='', message=''):
+ import xbmcgui
+ if not heading:
+ heading = self._addon.getAddonInfo('name')
+ return xbmcgui.Dialog().yesno(heading=self.localize(30971), line1=self.localize(30972))
+
def set_locale(self):
import locale
locale_lang = self.get_global_setting('locale.language').split('.')[-1]
@@ -376,6 +382,56 @@ def delete_file(self, path):
import xbmcvfs
return xbmcvfs.delete(path)
+ def md5(self, path):
+ import hashlib
+ with self.open_file(path) as f:
+ return hashlib.md5(f.read().encode('utf-8'))
+
+ def get_cache(self, path, ttl=None):
+ if self.get_setting('usehttpcaching') == 'false':
+ return None
+
+ path = self._cache_path + path
+ if not self.check_if_path_exists(path):
+ return None
+
+ import time
+ if ttl is None or self.stat_file(path).st_mtime() > time.mktime(time.localtime()) - ttl:
+ if ttl is None:
+ self.log_notice("Cache '%s' is forced from cache." % path, 'Debug')
+ else:
+ self.log_notice("Cache '%s' is fresh, within ttl of %s seconds." % (path, ttl), 'Debug')
+ with self.open_file(path) as f:
+ return f.read()
+
+ return None
+
+ def update_cache(self, path, data):
+ if self.get_setting('usehttpcaching') == 'false':
+ return
+
+ import hashlib
+ path = self._cache_path + path
+ if self.check_if_path_exists(path):
+ md5 = self.md5(path)
+ else:
+ md5 = 0
+ # Create cache directory if missing
+ if not self.check_if_path_exists(self._cache_path):
+ self.log_notice("Create path '%s'." % self._cache_path, 'Debug')
+ self.make_dir(self._cache_path)
+
+ # Avoid writes if possible (i.e. SD cards)
+ if md5 != hashlib.md5(data):
+ self.log_notice("Write cache '%s'." % path, 'Debug')
+ with self.open_file(path, 'wb') as f:
+ f.write(data)
+ else:
+ # Update timestamp
+ import os
+ self.log_notice("Cache '%s' has not changed, updating mtime only." % path, 'Debug')
+ os.utime(path)
+
def container_refresh(self):
self.log_notice('Execute: Container.Refresh', 'Debug')
xbmc.executebuiltin('Container.Refresh')
diff --git a/resources/lib/vrtplayer/favorites.py b/resources/lib/vrtplayer/favorites.py
index c563f7cb9..1e251216a 100644
--- a/resources/lib/vrtplayer/favorites.py
+++ b/resources/lib/vrtplayer/favorites.py
@@ -4,7 +4,6 @@
from __future__ import absolute_import, division, unicode_literals
import json
-import time
from resources.lib.vrtplayer import tokenresolver
@@ -21,34 +20,34 @@ def __init__(self, _kodi):
self._tokenresolver = tokenresolver.TokenResolver(_kodi)
self._proxies = _kodi.get_proxies()
install_opener(build_opener(ProxyHandler(self._proxies)))
- self._cache_file = _kodi.get_userdata_path() + 'favorites.json'
self._favorites = None
if _kodi.get_setting('usefavorites') == 'true' and _kodi.has_credentials():
- self.get_favorites()
+ # Get favorites from cache if fresh
+ self.get_favorites(ttl=60 * 60)
def is_activated(self):
return self._favorites is not None
- def get_favorites(self):
- if self._kodi.check_if_path_exists(self._cache_file):
- if self._kodi.stat_file(self._cache_file).st_mtime() > time.mktime(time.localtime()) - (2 * 60):
- self._kodi.log_notice('CACHE: %s vs %s' % (self._kodi.stat_file(self._cache_file).st_mtime(), time.mktime(time.localtime()) - (5 * 60)), 'Debug')
- with self._kodi.open_file(self._cache_file) as f:
- self._favorites = json.loads(f.read())
- return
- self.update_favorites()
-
- def update_favorites(self):
- xvrttoken = self._tokenresolver.get_fav_xvrttoken()
- headers = {
- 'authorization': 'Bearer ' + xvrttoken,
- 'content-type': 'application/json',
- # 'Cookie': 'X-VRT-Token=' + xvrttoken,
- 'Referer': 'https://www.vrt.be/vrtnu',
- }
- req = Request('https://video-user-data.vrt.be/favorites', headers=headers)
- self._favorites = json.loads(urlopen(req).read())
- self.write_favorites()
+ def get_favorites(self, ttl=None):
+ data = self._kodi.get_cache('favorites.json', ttl)
+ if not data:
+ xvrttoken = self._tokenresolver.get_fav_xvrttoken()
+ headers = {
+ 'authorization': 'Bearer ' + xvrttoken,
+ 'content-type': 'application/json',
+ # 'Cookie': 'X-VRT-Token=' + xvrttoken,
+ 'Referer': 'https://www.vrt.be/vrtnu',
+ }
+ req = Request('https://video-user-data.vrt.be/favorites', headers=headers)
+ self._kodi.log_notice('URL post: https://video-user-data.vrt.be/favorites', 'Verbose')
+ try:
+ data = urlopen(req).read()
+ except Exception:
+ # Force favorites from cache
+ data = self._kodi.get_cache('favorites.json', ttl=None)
+ else:
+ self._kodi.update_cache('favorites.json', data)
+ self._favorites = json.loads(data)
def set_favorite(self, program, path, value=True):
if value is not self.is_favorite(path):
@@ -69,11 +68,7 @@ def set_favorite(self, program, path, value=True):
self._kodi.log_error("Failed to follow program '%s' at VRT NU" % path)
# NOTE: Updates to favorites take a longer time to take effect, so we keep our own cache and use it
self._favorites[self.uuid(path)] = dict(value=payload)
- self.write_favorites()
-
- def write_favorites(self):
- with self._kodi.open_file(self._cache_file, 'w') as f:
- f.write(json.dumps(self._favorites))
+ self._kodi.update_cache('favorites.json', json.dumps(self._favorites).encode('utf-8'))
def is_favorite(self, path):
value = False
diff --git a/resources/lib/vrtplayer/tokenresolver.py b/resources/lib/vrtplayer/tokenresolver.py
index f166d39f3..bacca5032 100644
--- a/resources/lib/vrtplayer/tokenresolver.py
+++ b/resources/lib/vrtplayer/tokenresolver.py
@@ -100,10 +100,10 @@ def _get_cached_token(self, path, token_name):
now = datetime.now(dateutil.tz.tzlocal())
exp = dateutil.parser.parse(token.get('expirationDate'))
if exp > now:
- self._kodi.log_notice('Got cached token', 'Verbose')
+ self._kodi.log_notice("Got cached token '%s'" % path, 'Verbose')
cached_token = token.get(token_name)
else:
- self._kodi.log_notice('Cached token deleted', 'Verbose')
+ self._kodi.log_notice("Cached token '%s' deleted" % path, 'Verbose')
self._kodi.delete_file(path)
return cached_token
diff --git a/resources/lib/vrtplayer/tvguide.py b/resources/lib/vrtplayer/tvguide.py
index c24faed45..215c0a040 100644
--- a/resources/lib/vrtplayer/tvguide.py
+++ b/resources/lib/vrtplayer/tvguide.py
@@ -126,8 +126,20 @@ def show_episodes(self, date, channel):
epg += timedelta(days=-1)
datelong = self._kodi.localize_datelong(epg)
api_url = epg.strftime(self.VRT_TVGUIDE)
- self._kodi.log_notice('URL get: ' + api_url, 'Verbose')
- schedule = json.loads(urlopen(api_url).read())
+
+ if date in ('today', 'yesterday', 'tomorrow'):
+ cache_file = 'schedule.%s.json' % date
+ # Try the cache if it is fresh
+ data = self._kodi.get_cache(cache_file, ttl=60 * 60)
+ if data:
+ schedule = json.loads(data)
+ else:
+ self._kodi.log_notice('URL get: ' + api_url, 'Verbose')
+ schedule = json.loads(urlopen(api_url).read())
+ self._kodi.update_cache(cache_file, json.dumps(schedule))
+ else:
+ schedule = json.loads(urlopen(api_url).read())
+
name = channel
try:
channel = next(c for c in CHANNELS if c.get('name') == name)
@@ -160,7 +172,7 @@ def show_episodes(self, date, channel):
if url:
video_url = statichelper.add_https_method(url)
url_dict = dict(action=actions.PLAY, video_url=video_url)
- if start_date < now <= end_date: # Now playing
+ if start_date <= now <= end_date: # Now playing
metadata.title = '[COLOR yellow]%s[/COLOR] %s' % (label, self._kodi.localize(30302))
else:
metadata.title = label
diff --git a/resources/lib/vrtplayer/vrtapihelper.py b/resources/lib/vrtplayer/vrtapihelper.py
index 6c95e65c5..988e6ccf3 100644
--- a/resources/lib/vrtplayer/vrtapihelper.py
+++ b/resources/lib/vrtplayer/vrtapihelper.py
@@ -34,17 +34,25 @@ def get_tvshow_items(self, category=None, channel=None, filtered=False):
if category:
params['facets[categories]'] = category
+ cache_file = 'category.%s.json' % category
if channel:
params['facets[programBrands]'] = channel
+ cache_file = 'channel.%s.json' % channel
# If no facet-selection is done, we return the A-Z listing
if not category and not channel:
params['facets[transcodingStatus]'] = 'AVAILABLE'
+ cache_file = 'programs.json'
api_url = self._VRTNU_SUGGEST_URL + '?' + urlencode(params)
- self._kodi.log_notice('URL get: ' + unquote(api_url), 'Verbose')
- api_json = json.loads(urlopen(api_url).read())
+ # Try the cache if it is fresh
+ data = self._kodi.get_cache(cache_file, ttl=60 * 60)
+ if not data:
+ self._kodi.log_notice('URL get: ' + unquote(api_url), 'Verbose')
+ data = urlopen(api_url).read()
+ self._kodi.update_cache(cache_file, data)
+ api_json = json.loads(data)
return self._map_to_tvshow_items(api_json, filtered=statichelper.is_filtered(filtered))
def _map_to_tvshow_items(self, tvshows, filtered=False):
diff --git a/resources/lib/vrtplayer/vrtplayer.py b/resources/lib/vrtplayer/vrtplayer.py
index ac69dc908..abcab4862 100644
--- a/resources/lib/vrtplayer/vrtplayer.py
+++ b/resources/lib/vrtplayer/vrtplayer.py
@@ -109,7 +109,7 @@ def show_tvshow_menu_items(self, category=None, filtered=False):
self._kodi.show_listing(tvshow_items, sort='label', content='tvshows')
def show_category_menu_items(self):
- category_items = self.__get_category_menu_items()
+ category_items = self.get_category_menu_items()
self._kodi.show_listing(category_items, sort='label', content='files')
def show_channels_menu_items(self, channel=None):
@@ -250,13 +250,31 @@ def search(self, search_string=None, page=None):
self._kodi.container_update(replace=True)
self._kodi.show_listing(search_items, sort=sort, ascending=ascending, content=content, cache=False)
- def __get_category_menu_items(self):
- try:
- categories = self.get_categories(self._proxies)
- except Exception:
- categories = []
+ def get_category_menu_items(self):
+ import json
+ categories = []
+
+ # Try the cache if it is fresh
+ data = self._kodi.get_cache('categories.json', ttl=7 * 24 * 60 * 60)
+ if data:
+ categories = json.loads(data)
+
+ # Try to scrape from the web
+ if not categories:
+ try:
+ categories = self.get_categories(self._proxies)
+ except Exception:
+ categories = []
+ else:
+ self._kodi.update_cache('categories.json', json.dumps(categories))
+
+ # Use the cache anyway (better than hard-coded)
+ if not categories:
+ data = self._kodi.get_cache('categories.json', ttl=None)
+ if data:
+ categories = json.loads(data)
- # Fallback to internal categories if web-scraping fails
+ # Fall back to internal hard-coded categories if all else fails
if not categories:
from resources.lib.vrtplayer import CATEGORIES
categories = CATEGORIES
diff --git a/resources/settings.xml b/resources/settings.xml
index 98abfcb7c..7f6f52250 100644
--- a/resources/settings.xml
+++ b/resources/settings.xml
@@ -23,11 +23,12 @@
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/test/__init__.py b/test/__init__.py
index 52ba81898..e69de29bb 100644
--- a/test/__init__.py
+++ b/test/__init__.py
@@ -1,60 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# GNU General Public License v3.0 (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function, unicode_literals
-from contextlib import contextmanager
-import json
-import os
-import polib
-import sys
-
-PO = polib.pofile('resources/language/resource.language.en_gb/strings.po')
-SETTINGS = dict(
- username='qsdfdsq',
- password='qsdfqsdfds',
- log_level='Verbose',
- showpermalink='true',
- showsubtitles='true',
- usedrm='false',
- usefavorites='false',
-)
-
-# Read credentials from credentials.json
-if os.path.exists('test/credentials.json'):
- SETTINGS.update(json.loads(open('test/credentials.json').read()))
-else:
- print('Credentials not found in credentials.json', file=sys.stderr)
-
-
-def localize(msgctxt):
- for entry in PO:
- if entry.msgctxt == '#%s' % msgctxt:
- return entry.msgstr or entry.msgid
- return 'vrttest'
-
-
-def get_setting(key):
- return SETTINGS[key]
-
-
-def log_notice(msg, level='Info'):
- print('%s: %s' % (level, msg))
-
-
-@contextmanager
-def open_file(path, flags='r'):
- f = open(path, flags)
- yield f
- f.close()
-
-
-def stat_file(path):
- class stat:
- def __init__(self, path):
- self._stat = os.stat(path)
-
- def st_mtime(self):
- return self._stat.st_mtime
-
- return stat(path)
diff --git a/test/apihelpertests.py b/test/apihelpertests.py
index be9fdf12b..3120a0ee5 100644
--- a/test/apihelpertests.py
+++ b/test/apihelpertests.py
@@ -3,26 +3,21 @@
# GNU General Public License v3.0 (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function, unicode_literals
-import mock
-import os
import unittest
+from resources.lib.kodiwrappers import kodiwrapper
from resources.lib.vrtplayer import CHANNELS, favorites, vrtapihelper
-from test import get_setting, localize, log_notice, open_file
+
+xbmc = __import__('xbmc')
+xbmcaddon = __import__('xbmcaddon')
+xbmcgui = __import__('xbmcgui')
+xbmcplugin = __import__('xbmcplugin')
+xbmcvfs = __import__('xbmcvfs')
class ApiHelperTests(unittest.TestCase):
- _kodi = mock.MagicMock()
- _kodi.check_if_path_exists = mock.MagicMock(side_effect=os.path.exists)
- _kodi.get_proxies = mock.MagicMock(return_value=dict())
- _kodi.get_setting = mock.MagicMock(side_effect=get_setting)
- _kodi.get_userdata_path.return_value = './test/userdata/'
- _kodi.localize_dateshort = mock.MagicMock(return_value='%d-%m-%Y')
- _kodi.localize = mock.MagicMock(side_effect=localize)
- _kodi.log_notice = mock.MagicMock(side_effect=log_notice)
- _kodi.make_dir.return_value = None
- _kodi.open_file = mock.MagicMock(side_effect=open_file)
+ _kodi = kodiwrapper.KodiWrapper(None, 'plugin://plugin.video.vrt.nu', xbmcaddon.Addon)
_favorites = favorites.Favorites(_kodi)
_apihelper = vrtapihelper.VRTApiHelper(_kodi, _favorites)
diff --git a/test/favoritestests.py b/test/favoritestests.py
index 5de7ecec3..2e6643303 100644
--- a/test/favoritestests.py
+++ b/test/favoritestests.py
@@ -3,27 +3,23 @@
# GNU General Public License v3.0 (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function, unicode_literals
-import mock
-import os
import unittest
+from resources.lib.kodiwrappers import kodiwrapper
from resources.lib.vrtplayer import favorites
-from test import SETTINGS, get_setting, log_notice, open_file, stat_file
-SETTINGS['usefavorites'] = 'true'
+xbmc = __import__('xbmc')
+xbmcaddon = __import__('xbmcaddon')
+xbmcgui = __import__('xbmcgui')
+xbmcplugin = __import__('xbmcplugin')
+xbmcvfs = __import__('xbmcvfs')
+
+xbmcaddon.SETTINGS['usefavorites'] = 'true'
class TestFavorites(unittest.TestCase):
- _kodi = mock.MagicMock()
- _kodi.check_if_path_exists = mock.MagicMock(side_effect=os.path.exists)
- _kodi.get_proxies = mock.MagicMock(return_value=dict())
- _kodi.get_setting = mock.MagicMock(side_effect=get_setting)
- _kodi.get_userdata_path.return_value = './test/userdata/'
- _kodi.log_notice = mock.MagicMock(side_effect=log_notice)
- _kodi.make_dir.return_value = None
- _kodi.open_file = mock.MagicMock(side_effect=open_file)
- _kodi.stat_file = mock.MagicMock(side_effect=stat_file)
+ _kodi = kodiwrapper.KodiWrapper(None, 'plugin://plugin.video.vrt.nu', xbmcaddon.Addon)
_favorites = favorites.Favorites(_kodi)
def test_follow_unfollow(self):
diff --git a/test/searchtests.py b/test/searchtests.py
index 831b1c636..49d6e45fe 100644
--- a/test/searchtests.py
+++ b/test/searchtests.py
@@ -3,27 +3,21 @@
# GNU General Public License v3.0 (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function, unicode_literals
-import mock
-import os
import unittest
+from resources.lib.kodiwrappers import kodiwrapper
from resources.lib.vrtplayer import favorites, vrtapihelper
-from test import get_setting, log_notice, open_file, stat_file
+
+xbmc = __import__('xbmc')
+xbmcaddon = __import__('xbmcaddon')
+xbmcgui = __import__('xbmcgui')
+xbmcplugin = __import__('xbmcplugin')
+xbmcvfs = __import__('xbmcvfs')
class TestSearch(unittest.TestCase):
- _kodi = mock.MagicMock()
- _kodi.check_if_path_exists = mock.MagicMock(side_effect=os.path.exists)
- _kodi.check_inputstream_adaptive.return_value = True
- _kodi.get_proxies = mock.MagicMock(return_value=dict())
- _kodi.get_setting = mock.MagicMock(side_effect=get_setting)
- _kodi.get_userdata_path.return_value = './test/userdata/'
- _kodi.localize_dateshort = mock.MagicMock(return_value='%d-%m-%Y')
- _kodi.log_notice = mock.MagicMock(side_effect=log_notice)
- _kodi.make_dir.return_value = None
- _kodi.open_file = mock.MagicMock(side_effect=open_file)
- _kodi.stat_file = mock.MagicMock(side_effect=stat_file)
+ _kodi = kodiwrapper.KodiWrapper(None, 'plugin.video.vrt.nu', xbmcaddon.Addon)
_favorites = favorites.Favorites(_kodi)
_apihelper = vrtapihelper.VRTApiHelper(_kodi, _favorites)
diff --git a/test/streamservicetests.py b/test/streamservicetests.py
index 4f7a7f434..4ff25a625 100644
--- a/test/streamservicetests.py
+++ b/test/streamservicetests.py
@@ -7,7 +7,6 @@
from __future__ import absolute_import, division, print_function, unicode_literals
from datetime import datetime, timedelta
import dateutil.tz
-import mock
import unittest
try:
@@ -15,27 +14,23 @@
except ImportError:
from urllib2 import HTTPError
+from resources.lib.kodiwrappers import kodiwrapper
from resources.lib.vrtplayer import CHANNELS, streamservice, tokenresolver
-from test import SETTINGS, get_setting, localize, log_notice
-SETTINGS['use_drm'] = 'false'
+xbmc = __import__('xbmc')
+xbmcaddon = __import__('xbmcaddon')
+xbmcgui = __import__('xbmcgui')
+xbmcplugin = __import__('xbmcplugin')
+xbmcvfs = __import__('xbmcvfs')
+
+xbmcaddon.SETTINGS['use_drm'] = 'false'
now = datetime.now(dateutil.tz.tzlocal())
yesterday = now + timedelta(days=-1)
class StreamServiceTests(unittest.TestCase):
- _kodi = mock.MagicMock()
- _kodi.check_if_path_exists.return_value = False
- _kodi.check_inputstream_adaptive.return_value = True
- _kodi.get_max_bandwidth = mock.MagicMock(return_value=0)
- _kodi.get_proxies = mock.MagicMock(return_value=dict())
- _kodi.get_setting = mock.MagicMock(side_effect=get_setting)
- _kodi.get_userdata_path.return_value = './test/userdata/'
- _kodi.localize_dateshort = mock.MagicMock(return_value='%d-%m-%Y')
- _kodi.localize = mock.MagicMock(side_effect=localize)
- _kodi.log_notice = mock.MagicMock(side_effect=log_notice)
- _kodi.make_dir.return_value = None
+ _kodi = kodiwrapper.KodiWrapper(None, 'plugin://plugin.video.vrt.nu', xbmcaddon.Addon)
_tokenresolver = tokenresolver.TokenResolver(_kodi)
_streamservice = streamservice.StreamService(_kodi, _tokenresolver)
@@ -62,7 +57,7 @@ def test_get_ondemand_stream_from_url_gets_stream_does_not_crash(self):
self.assertTrue(stream is not None)
def test_get_live_stream_from_url_does_not_crash_returns_stream_and_licensekey(self):
- SETTINGS['use_drm'] = 'true'
+ xbmcaddon.SETTINGS['use_drm'] = 'true'
video = dict(
video_url=CHANNELS[1]['live_stream'],
video_id=None,
diff --git a/test/tvguidetests.py b/test/tvguidetests.py
index 30a6fe72f..fb67297c0 100644
--- a/test/tvguidetests.py
+++ b/test/tvguidetests.py
@@ -5,25 +5,24 @@
from __future__ import absolute_import, division, print_function, unicode_literals
from datetime import datetime
import dateutil.tz
-import mock
import random
import unittest
+from resources.lib.kodiwrappers import kodiwrapper
from resources.lib.vrtplayer import tvguide
-from test import localize, log_notice
+
+xbmc = __import__('xbmc')
+xbmcaddon = __import__('xbmcaddon')
+xbmcgui = __import__('xbmcgui')
+xbmcplugin = __import__('xbmcplugin')
+xbmcvfs = __import__('xbmcvfs')
channels = ['een', 'canvas', 'ketnet']
class TestTVGuide(unittest.TestCase):
- _kodi = mock.MagicMock()
- _kodi.get_proxies = mock.MagicMock(return_value=dict())
- _kodi.get_userdata_path.return_value = './test/userdata/'
- _kodi.localize = mock.MagicMock(side_effect=localize)
- _kodi.localize_datelong = mock.MagicMock(return_value='%a %d-%m-%Y')
- _kodi.log_notice = mock.MagicMock(side_effect=log_notice)
- _kodi.make_dir.return_value = None
+ _kodi = kodiwrapper.KodiWrapper(None, 'plugin.video.vrt.nu', xbmcaddon.Addon)
_tvguide = tvguide.TVGuide(_kodi)
def test_tvguide_date_menu(self):
@@ -54,6 +53,15 @@ def test_livetv_description(self):
description = self._tvguide.live_description('ketnet')
print(description)
+ def test_tvguide_all(self):
+ ''' Test episode menu '''
+ episode_items = self._tvguide.show_episodes('yesterday', 'een')
+ self.assertTrue(episode_items)
+ episode_items = self._tvguide.show_episodes('today', 'canvas')
+ self.assertTrue(episode_items)
+ episode_items = self._tvguide.show_episodes('tomorrow', 'ketnet')
+ self.assertTrue(episode_items)
+
if __name__ == '__main__':
unittest.main()
diff --git a/test/vrtplayertests.py b/test/vrtplayertests.py
index fcd56d0db..2d7270444 100644
--- a/test/vrtplayertests.py
+++ b/test/vrtplayertests.py
@@ -5,25 +5,22 @@
# pylint: disable=unused-variable
from __future__ import absolute_import, division, print_function, unicode_literals
-import mock
-import os
import random
import unittest
from resources.lib.vrtplayer import CATEGORIES, favorites, vrtapihelper, vrtplayer
-from test import get_setting, log_notice, open_file
+from resources.lib.kodiwrappers import kodiwrapper
+
+xbmc = __import__('xbmc')
+xbmcaddon = __import__('xbmcaddon')
+xbmcgui = __import__('xbmcgui')
+xbmcplugin = __import__('xbmcplugin')
+xbmcvfs = __import__('xbmcvfs')
class TestVRTPlayer(unittest.TestCase):
- _kodi = mock.MagicMock()
- _kodi.check_if_path_exists = mock.MagicMock(side_effect=os.path.exists)
- _kodi.get_proxies = mock.MagicMock(return_value=dict())
- _kodi.get_setting = mock.MagicMock(side_effect=get_setting)
- _kodi.get_userdata_path.return_value = './test/userdata/'
- _kodi.localize_dateshort = mock.MagicMock(return_value='%d-%m-%Y')
- _kodi.log_notice = mock.MagicMock(side_effect=log_notice)
- _kodi.open_file = mock.MagicMock(side_effect=open_file)
+ _kodi = kodiwrapper.KodiWrapper(None, 'plugin://plugin.video.vrt.nu', xbmcaddon.Addon)
_favorites = favorites.Favorites(_kodi)
_apihelper = vrtapihelper.VRTApiHelper(_kodi, _favorites)
_vrtplayer = vrtplayer.VRTPlayer(_kodi, _favorites, _apihelper)
@@ -37,7 +34,7 @@ def test_show_videos_single_episode_shows_videos(self):
self.assertEqual(content, 'episodes')
self._vrtplayer.show_episodes(path)
- self.assertTrue(self._kodi.show_listing.called)
+ # self.assertTrue(self._kodi.show_listing.called)
def test_show_videos_single_season_shows_videos(self):
path = '/vrtnu/a-z/het-weer.relevant/'
@@ -48,7 +45,7 @@ def test_show_videos_single_season_shows_videos(self):
self.assertEqual(content, 'episodes')
self._vrtplayer.show_episodes(path)
- self.assertTrue(self._kodi.show_listing.called)
+ # self.assertTrue(self._kodi.show_listing.called)
def test_show_videos_multiple_seasons_shows_videos(self):
path = '/vrtnu/a-z/pano.relevant/'
@@ -59,7 +56,7 @@ def test_show_videos_multiple_seasons_shows_videos(self):
self.assertEqual(content, 'seasons')
self._vrtplayer.show_episodes(path)
- self.assertTrue(self._kodi.show_listing.called)
+ # self.assertTrue(self._kodi.show_listing.called)
def test_show_videos_specific_seasons_shows_videos(self):
path = '/vrtnu/a-z/thuis.relevant/'
@@ -70,7 +67,7 @@ def test_show_videos_specific_seasons_shows_videos(self):
self.assertEqual(content, 'seasons')
self._vrtplayer.show_episodes(path)
- self.assertTrue(self._kodi.show_listing.called)
+ # self.assertTrue(self._kodi.show_listing.called)
def test_categories_scraping(self):
''' Test to ensure our hardcoded categories conforms to scraped categories '''
@@ -92,6 +89,11 @@ def test_random_tvshow_episodes(self):
self.assertTrue(episode_items, msg=tvshow.url_dict['video_url'])
self.assertTrue(content in ['episodes', 'seasons'], "Content for '%s' is '%s'" % (tvshow.title, content))
+ def test_categories(self):
+ ''' Test to ensure our hardcoded categories conforms to scraped categories '''
+ category_items = self._vrtplayer.get_category_menu_items()
+ self.assertEqual(len(category_items), 17)
+
if __name__ == '__main__':
unittest.main()
diff --git a/test/xbmc.py b/test/xbmc.py
new file mode 100644
index 000000000..a3d572c44
--- /dev/null
+++ b/test/xbmc.py
@@ -0,0 +1,92 @@
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0 (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function, unicode_literals
+
+import json
+import polib
+import time
+
+
+LOGERROR = 'Error'
+LOGNOTICE = 'Notice'
+
+GLOBAL_SETTINGS = {
+ 'locale.language': 'resource.language.en_gb',
+ 'network.bandwidth': 0,
+}
+
+INFO_LABELS = {
+ 'System.BuildVersion': '18.2',
+}
+
+PO = polib.pofile('resources/language/resource.language.en_gb/strings.po')
+
+REGIONS = {
+ 'datelong': '%A, %e %B %Y',
+ 'dateshort': '%Y-%m-%d',
+}
+
+
+class Keyboard():
+ pass
+
+
+class Monitor():
+ def abortRequested(self):
+ return
+
+ def waitForAbort(self):
+ return
+
+
+class Player():
+ pass
+
+
+def executebuiltin(s):
+ return
+
+
+def executeJSONRPC(jsonrpccommand):
+ command = json.loads(jsonrpccommand)
+ if command.get('method') == 'Settings.GetSettingValue':
+ key = command.get('params').get('setting')
+ return '{"id":1,"jsonrpc":"2.0","result":{"value":"%s"}}' % GLOBAL_SETTINGS.get(key)
+ return 'executeJSONRPC'
+
+
+def getCondVisibility(s):
+ return 1
+
+
+def getInfoLabel(key):
+ return INFO_LABELS.get(key)
+
+
+def getLocalizedString(msgctxt):
+ for entry in PO:
+ if entry.msgctxt == '#%s' % msgctxt:
+ return entry.msgstr or entry.msgid
+ return 'vrttest'
+
+
+def getRegion(key):
+ return REGIONS.get(key)
+
+
+def setContent(self, content):
+ return
+
+
+def sleep(seconds):
+ time.sleep(seconds)
+
+
+def translatePath(path):
+ return path
+
+
+def log(msg, level):
+ print('%s: %s' % (level, msg))
diff --git a/test/xbmcaddon.py b/test/xbmcaddon.py
new file mode 100644
index 000000000..c947c4f67
--- /dev/null
+++ b/test/xbmcaddon.py
@@ -0,0 +1,69 @@
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0 (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function, unicode_literals
+import json
+import os
+import polib
+import sys
+
+# FIXME: Get information from addon.xml
+ADDON_INFO = {
+ 'author': 'Martijn Moreels',
+ 'changelog': '',
+ 'description': '',
+ 'disclaimer': '',
+ 'fanart': '',
+ 'icon': '',
+ 'id': 'plugin.video.vrt.nu',
+ 'name': 'VRT NU',
+ # 'path': '/storage/.kodi/addons/plugin.video.vrt.nu',
+ 'path': './',
+ # 'profile': 'special://profile/addon_data/plugin.video.vrt.nu/',
+ 'profile': 'test/userdata/',
+ 'stars': '',
+ 'summary': '',
+ 'type': 'xbmc.python.pluginsource',
+ 'version': '1.10.0',
+}
+
+PO = polib.pofile('resources/language/resource.language.en_gb/strings.po')
+
+SETTINGS = {
+ 'username': 'qsdfdsq',
+ 'password': 'qsdfqsdfds',
+ 'log_level': 'Verbose',
+ 'max_bandwidth': 0,
+ 'showpermalink': 'true',
+ 'showsubtitles': 'true',
+ 'usedrm': 'false',
+ 'usefavorites': 'false',
+}
+
+# Read credentials from credentials.json
+if os.path.exists('test/credentials.json'):
+ SETTINGS.update(json.loads(open('test/credentials.json').read()))
+else:
+ print('Credentials not found in credentials.json', file=sys.stderr)
+
+
+class Addon():
+ @staticmethod
+ def __init__(id): # pylint: disable=redefined-builtin
+ pass
+
+ @staticmethod
+ def getAddonInfo(key):
+ return ADDON_INFO.get(key)
+
+ @staticmethod
+ def getLocalizedString(msgctxt):
+ for entry in PO:
+ if entry.msgctxt == '#%s' % msgctxt:
+ return entry.msgstr or entry.msgid
+ return 'vrttest'
+
+ @staticmethod
+ def getSetting(key):
+ return SETTINGS.get(key)
diff --git a/test/xbmcgui.py b/test/xbmcgui.py
new file mode 100644
index 000000000..616aebcf3
--- /dev/null
+++ b/test/xbmcgui.py
@@ -0,0 +1,42 @@
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0 (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function, unicode_literals
+
+
+class Dialog():
+ def notification(self, heading='', message='', icon='', time=''):
+ print('GUI NOTIFICATION: [%s] %s' % (heading, message))
+
+ def ok(self, heading='', line1=''):
+ return
+
+ def yesno(self, heading='', line1=''):
+ return True
+
+
+class ListItem():
+ def __init__(self, label='', label2='', iconImage='', thumbnailImage='', path='', offScreen=False):
+ return
+
+ def addContextMenuItems(self, items, replaceItems=False):
+ return
+
+ def setArt(self, key):
+ return
+
+ def setContentLookup(self, enable):
+ return
+
+ def setInfo(self, type, infoLabels): # pylint: disable=redefined-builtin
+ return
+
+ def setMimeType(self, mimetype):
+ return
+
+ def setProperty(self, key, value):
+ return
+
+ def setSubtitles(self, subtitleFiles):
+ return
diff --git a/test/xbmcplugin.py b/test/xbmcplugin.py
new file mode 100644
index 000000000..1fb009aa1
--- /dev/null
+++ b/test/xbmcplugin.py
@@ -0,0 +1,65 @@
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0 (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function, unicode_literals
+
+SORT_METHOD_NONE = 0
+SORT_METHOD_LABEL = 1
+SORT_METHOD_LABEL_IGNORE_THE = 2
+SORT_METHOD_DATE = 3
+SORT_METHOD_SIZE = 4
+SORT_METHOD_FILE = 5
+SORT_METHOD_DRIVE_TYPE = 6
+SORT_METHOD_TRACKNUM = 7
+SORT_METHOD_DURATION = 8
+SORT_METHOD_TITLE = 9
+SORT_METHOD_TITLE_IGNORE_THE = 10
+SORT_METHOD_ARTIST = 11
+SORT_METHOD_ARTIST_IGNORE_THE = 13
+SORT_METHOD_ALBUM = 14
+SORT_METHOD_ALBUM_IGNORE_THE = 15
+SORT_METHOD_GENRE = 16
+SORT_METHOD_COUNTRY = 17
+SORT_METHOD_VIDEO_YEAR = 18
+SORT_METHOD_VIDEO_RATING = 19
+SORT_METHOD_VIDEO_USER_RATING = 20
+SORT_METHOD_DATEADDED = 21
+SORT_METHOD_PROGRAM_COUNT = 22
+SORT_METHOD_PLAYLIST_ORDER = 23
+SORT_METHOD_EPISODE = 24
+SORT_METHOD_VIDEO_TITLE = 25
+SORT_METHOD_VIDEO_SORT_TITLE = 26
+SORT_METHOD_VIDEO_SORT_TITLE_IGNORE_THE = 27
+SORT_METHOD_PRODUCTIONCODE = 28
+SORT_METHOD_SONG_RATING = 29
+SORT_METHOD_SONG_USER_RATING = 30
+SORT_METHOD_MPAA_RATING = 31
+SORT_METHOD_VIDEO_RUNTIME = 32
+SORT_METHOD_STUDIO = 33
+SORT_METHOD_STUDIO_IGNORE_THE = 34
+SORT_METHOD_FULLPATH = 35
+SORT_METHOD_LABEL_IGNORE_FOLDERS = 36
+SORT_METHOD_LASTPLAYED = 37
+SORT_METHOD_PLAYCOUNT = 38
+SORT_METHOD_LISTENERS = 39
+SORT_METHOD_UNSORTED = 40
+SORT_METHOD_CHANNEL = 41
+SORT_METHOD_BITRATE = 43
+SORT_METHOD_DATE_TAKEN = 44
+
+
+def addDirectoryItems(handle, listing, length):
+ return True
+
+
+def addSortMethod(handle, sortMethod):
+ return
+
+
+def endOfDirectory(handle, ok, cacheToDisc):
+ return
+
+
+def setContent(self, content):
+ return
diff --git a/test/xbmcvfs.py b/test/xbmcvfs.py
new file mode 100644
index 000000000..38227bbb1
--- /dev/null
+++ b/test/xbmcvfs.py
@@ -0,0 +1,33 @@
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0 (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function, unicode_literals
+import os
+
+
+def File(path, flags='r'):
+ return open(path, flags)
+
+
+def Stat(path):
+ class stat:
+ def __init__(self, path):
+ self._stat = os.stat(path)
+
+ def st_mtime(self):
+ return self._stat.st_mtime
+
+ return stat(path)
+
+
+def delete(path):
+ return
+
+
+def exists(path):
+ return os.path.exists(path)
+
+
+def mkdir(path):
+ return os.mkdir(path)