Use sake for Kodi stubs (#36)
This commit is contained in:
parent
94067c2fa9
commit
fd83e93b1d
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@ -8,7 +8,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
PYTHONIOENCODING: utf-8
|
PYTHONIOENCODING: utf-8
|
||||||
PYTHONPATH: ${{ github.workspace }}/resources/lib:${{ github.workspace }}/tests
|
PYTHONPATH: ${{ github.workspace }}/resources/lib
|
||||||
|
KODI_HOME: ${{ github.workspace }}/tests/home
|
||||||
|
KODI_INTERACTIVE: 0
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@ -37,8 +39,8 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
ADDON_PASSWORD: ${{ secrets.ADDON_PASSWORD }}
|
ADDON_PASSWORD: ${{ secrets.ADDON_PASSWORD }}
|
||||||
ADDON_USERNAME: ${{ secrets.ADDON_USERNAME }}
|
ADDON_USERNAME: ${{ secrets.ADDON_USERNAME }}
|
||||||
- name: Run addon service
|
- name: Run addon service for 10 seconds
|
||||||
run: coverage run -a service_entry.py
|
run: timeout --preserve-status -s SIGINT 10 coverage run -a service_entry.py
|
||||||
env:
|
env:
|
||||||
ADDON_PASSWORD: ${{ secrets.ADDON_PASSWORD }}
|
ADDON_PASSWORD: ${{ secrets.ADDON_PASSWORD }}
|
||||||
ADDON_USERNAME: ${{ secrets.ADDON_USERNAME }}
|
ADDON_USERNAME: ${{ secrets.ADDON_USERNAME }}
|
||||||
|
8
.gitignore
vendored
8
.gitignore
vendored
@ -12,10 +12,4 @@ Thumbs.db
|
|||||||
|
|
||||||
.coverage
|
.coverage
|
||||||
.tox/
|
.tox/
|
||||||
tests/userdata/credentials.json
|
tests/home/userdata/addon_data
|
||||||
tests/userdata/temp
|
|
||||||
tests/userdata/token.json
|
|
||||||
tests/userdata/cache
|
|
||||||
tests/userdata/addon_data
|
|
||||||
tests/userdata/tokens
|
|
||||||
tests/cdm
|
|
||||||
|
3
Makefile
3
Makefile
@ -1,4 +1,5 @@
|
|||||||
export PYTHONPATH := $(CURDIR):$(CURDIR)/tests
|
export KODI_HOME := $(CURDIR)/tests/home
|
||||||
|
export KODI_INTERACTIVE := 0
|
||||||
PYTHON := python
|
PYTHON := python
|
||||||
|
|
||||||
# Collect information to build as sensible package name
|
# Collect information to build as sensible package name
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
coverage
|
coverage
|
||||||
git+git://github.com/emilsvennesson/script.module.inputstreamhelper.git@master#egg=inputstreamhelper
|
|
||||||
polib
|
polib
|
||||||
pylint
|
pylint
|
||||||
python-dateutil
|
python-dateutil
|
||||||
@ -7,3 +6,4 @@ requests
|
|||||||
git+git://github.com/dagwieers/kodi-plugin-routing.git@setup#egg=routing
|
git+git://github.com/dagwieers/kodi-plugin-routing.git@setup#egg=routing
|
||||||
tox
|
tox
|
||||||
six
|
six
|
||||||
|
sakee
|
||||||
|
@ -48,4 +48,3 @@ def config():
|
|||||||
""" Setup the logger with this handler """
|
""" Setup the logger with this handler """
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
logger.addHandler(KodiLogHandler())
|
logger.addHandler(KodiLogHandler())
|
||||||
logger.setLevel(logging.DEBUG)
|
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
from __future__ import absolute_import, division, unicode_literals
|
from __future__ import absolute_import, division, unicode_literals
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
import xbmc
|
import xbmc
|
||||||
import xbmcaddon
|
import xbmcaddon
|
||||||
import xbmcgui
|
import xbmcgui
|
||||||
@ -228,7 +230,7 @@ def ok_dialog(heading='', message=''):
|
|||||||
if not heading:
|
if not heading:
|
||||||
heading = addon_name()
|
heading = addon_name()
|
||||||
if kodi_version_major() < 19:
|
if kodi_version_major() < 19:
|
||||||
return Dialog().ok(heading=heading, line1=message)
|
return Dialog().ok(heading=heading, line1=message) # pylint: disable=unexpected-keyword-arg,no-value-for-parameter
|
||||||
return Dialog().ok(heading=heading, message=message)
|
return Dialog().ok(heading=heading, message=message)
|
||||||
|
|
||||||
|
|
||||||
@ -238,7 +240,7 @@ def yesno_dialog(heading='', message='', nolabel=None, yeslabel=None, autoclose=
|
|||||||
if not heading:
|
if not heading:
|
||||||
heading = addon_name()
|
heading = addon_name()
|
||||||
if kodi_version_major() < 19:
|
if kodi_version_major() < 19:
|
||||||
return Dialog().yesno(heading=heading, line1=message, nolabel=nolabel, yeslabel=yeslabel, autoclose=autoclose)
|
return Dialog().yesno(heading=heading, line1=message, nolabel=nolabel, yeslabel=yeslabel, autoclose=autoclose) # pylint: disable=unexpected-keyword-arg,no-value-for-parameter
|
||||||
return Dialog().yesno(heading=heading, message=message, nolabel=nolabel, yeslabel=yeslabel, autoclose=autoclose)
|
return Dialog().yesno(heading=heading, message=message, nolabel=nolabel, yeslabel=yeslabel, autoclose=autoclose)
|
||||||
|
|
||||||
|
|
||||||
@ -276,7 +278,7 @@ class progress(xbmcgui.DialogProgress, object): # pylint: disable=invalid-name,
|
|||||||
if kodi_version_major() < 19:
|
if kodi_version_major() < 19:
|
||||||
lines = message.split('\n', 2)
|
lines = message.split('\n', 2)
|
||||||
line1, line2, line3 = (lines + [None] * (3 - len(lines)))
|
line1, line2, line3 = (lines + [None] * (3 - len(lines)))
|
||||||
return super(progress, self).create(heading, line1=line1, line2=line2, line3=line3)
|
return super(progress, self).create(heading, line1=line1, line2=line2, line3=line3) # pylint: disable=unexpected-keyword-arg,no-value-for-parameter
|
||||||
return super(progress, self).create(heading, message=message)
|
return super(progress, self).create(heading, message=message)
|
||||||
|
|
||||||
def update(self, percent, message=''): # pylint: disable=arguments-differ
|
def update(self, percent, message=''): # pylint: disable=arguments-differ
|
||||||
@ -284,7 +286,7 @@ class progress(xbmcgui.DialogProgress, object): # pylint: disable=invalid-name,
|
|||||||
if kodi_version_major() < 19:
|
if kodi_version_major() < 19:
|
||||||
lines = message.split('\n', 2)
|
lines = message.split('\n', 2)
|
||||||
line1, line2, line3 = (lines + [None] * (3 - len(lines)))
|
line1, line2, line3 = (lines + [None] * (3 - len(lines)))
|
||||||
return super(progress, self).update(percent, line1=line1, line2=line2, line3=line3)
|
return super(progress, self).update(percent, line1=line1, line2=line2, line3=line3) # pylint: disable=unexpected-keyword-arg,no-value-for-parameter
|
||||||
return super(progress, self).update(percent, message=message)
|
return super(progress, self).update(percent, message=message)
|
||||||
|
|
||||||
|
|
||||||
@ -439,14 +441,14 @@ def kodi_version_major():
|
|||||||
def get_tokens_path():
|
def get_tokens_path():
|
||||||
"""Cache and return the userdata tokens path"""
|
"""Cache and return the userdata tokens path"""
|
||||||
if not hasattr(get_tokens_path, 'cached'):
|
if not hasattr(get_tokens_path, 'cached'):
|
||||||
get_tokens_path.cached = addon_profile() + 'tokens/'
|
get_tokens_path.cached = os.path.join(addon_profile(), 'tokens')
|
||||||
return getattr(get_tokens_path, 'cached')
|
return getattr(get_tokens_path, 'cached')
|
||||||
|
|
||||||
|
|
||||||
def get_cache_path():
|
def get_cache_path():
|
||||||
"""Cache and return the userdata cache path"""
|
"""Cache and return the userdata cache path"""
|
||||||
if not hasattr(get_cache_path, 'cached'):
|
if not hasattr(get_cache_path, 'cached'):
|
||||||
get_cache_path.cached = addon_profile() + 'cache/'
|
get_cache_path.cached = os.path.join(addon_profile(), 'cache')
|
||||||
return getattr(get_cache_path, 'cached')
|
return getattr(get_cache_path, 'cached')
|
||||||
|
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ class BackgroundService(Monitor):
|
|||||||
|
|
||||||
now = time()
|
now = time()
|
||||||
for filename in os.listdir(path):
|
for filename in os.listdir(path):
|
||||||
fullpath = path + filename
|
fullpath = os.path.join(path, filename)
|
||||||
if keep_expired and os.stat(fullpath).st_mtime + keep_expired > now:
|
if keep_expired and os.stat(fullpath).st_mtime + keep_expired > now:
|
||||||
continue
|
continue
|
||||||
os.unlink(fullpath)
|
os.unlink(fullpath)
|
||||||
|
@ -33,7 +33,7 @@ class AuthApi:
|
|||||||
|
|
||||||
# Load tokens from cache
|
# Load tokens from cache
|
||||||
try:
|
try:
|
||||||
with open(self._token_path + self.TOKEN_FILE, 'r') as fdesc:
|
with open(os.path.join(self._token_path, self.TOKEN_FILE), 'r') as fdesc:
|
||||||
data_json = json.loads(fdesc.read())
|
data_json = json.loads(fdesc.read())
|
||||||
self._id_token = data_json.get('id_token')
|
self._id_token = data_json.get('id_token')
|
||||||
self._refresh_token = data_json.get('refresh_token')
|
self._refresh_token = data_json.get('refresh_token')
|
||||||
@ -75,7 +75,7 @@ class AuthApi:
|
|||||||
# Store new tokens in cache
|
# Store new tokens in cache
|
||||||
if not os.path.exists(self._token_path):
|
if not os.path.exists(self._token_path):
|
||||||
os.mkdir(self._token_path)
|
os.mkdir(self._token_path)
|
||||||
with open(self._token_path + self.TOKEN_FILE, 'w') as fdesc:
|
with open(os.path.join(self._token_path, self.TOKEN_FILE), 'w') as fdesc:
|
||||||
data = json.dumps(dict(
|
data = json.dumps(dict(
|
||||||
id_token=self._id_token,
|
id_token=self._id_token,
|
||||||
refresh_token=self._refresh_token,
|
refresh_token=self._refresh_token,
|
||||||
@ -87,8 +87,8 @@ class AuthApi:
|
|||||||
|
|
||||||
def clear_tokens(self):
|
def clear_tokens(self):
|
||||||
""" Remove the cached tokens. """
|
""" Remove the cached tokens. """
|
||||||
if os.path.exists(self._token_path + AuthApi.TOKEN_FILE):
|
if os.path.exists(os.path.join(self._token_path, AuthApi.TOKEN_FILE)):
|
||||||
os.unlink(self._token_path + AuthApi.TOKEN_FILE)
|
os.unlink(os.path.join(self._token_path, AuthApi.TOKEN_FILE))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _authenticate(username, password):
|
def _authenticate(username, password):
|
||||||
|
@ -621,10 +621,10 @@ class ContentApi:
|
|||||||
def _set_cache(self, key, data, ttl):
|
def _set_cache(self, key, data, ttl):
|
||||||
""" Store an item in the cache """
|
""" Store an item in the cache """
|
||||||
filename = ('.'.join(key) + '.json').replace('/', '_')
|
filename = ('.'.join(key) + '.json').replace('/', '_')
|
||||||
fullpath = self._cache_path + filename
|
fullpath = os.path.join(self._cache_path, filename)
|
||||||
|
|
||||||
if not os.path.exists(self._cache_path):
|
if not os.path.exists(self._cache_path):
|
||||||
os.mkdir(self._cache_path)
|
os.makedirs(self._cache_path)
|
||||||
|
|
||||||
with open(fullpath, 'w') as fdesc:
|
with open(fullpath, 'w') as fdesc:
|
||||||
_LOGGER.debug('Storing to cache as %s', filename)
|
_LOGGER.debug('Storing to cache as %s', filename)
|
||||||
|
@ -4,5 +4,20 @@
|
|||||||
from __future__ import absolute_import, division, unicode_literals
|
from __future__ import absolute_import, division, unicode_literals
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
logging.basicConfig()
|
import xbmcaddon
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
|
# Make UTF-8 the default encoding in Python 2
|
||||||
|
if sys.version_info[0] == 2:
|
||||||
|
reload(sys) # pylint: disable=undefined-variable
|
||||||
|
sys.setdefaultencoding("utf-8") # pylint: disable=no-member
|
||||||
|
|
||||||
|
# Set credentials based on environment data
|
||||||
|
if os.environ.get('ADDON_USERNAME') and os.environ.get('ADDON_PASSWORD'):
|
||||||
|
ADDON = xbmcaddon.Addon()
|
||||||
|
ADDON.setSetting('username', os.environ.get('ADDON_USERNAME'))
|
||||||
|
ADDON.setSetting('password', os.environ.get('ADDON_PASSWORD'))
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
""" Quick and dirty way to check if all translations might be used. """
|
""" Quick and dirty way to check if all translations might be used. """
|
||||||
|
|
||||||
|
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||||
|
|
||||||
# pylint: disable=invalid-name,superfluous-parens
|
# pylint: disable=invalid-name,superfluous-parens
|
||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
|
3
tests/home/addons/inputstream.adaptive/addon.xml
Normal file
3
tests/home/addons/inputstream.adaptive/addon.xml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<addon id="inputstream.adaptive" version="2.4.4" name="InputStream Adaptive" provider-name="peak3d">
|
||||||
|
</addon>
|
3
tests/home/userdata/guisettings.xml
Normal file
3
tests/home/userdata/guisettings.xml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<settings version="2">
|
||||||
|
<setting id="videolibrary.showallitems" default="true">true</setting>
|
||||||
|
</settings>
|
@ -12,7 +12,7 @@ import resources.lib.kodiutils as kodiutils
|
|||||||
from resources.lib.viervijfzes.auth import AuthApi
|
from resources.lib.viervijfzes.auth import AuthApi
|
||||||
from resources.lib.viervijfzes.content import ContentApi, Program, Episode, Category, CACHE_PREVENT
|
from resources.lib.viervijfzes.content import ContentApi, Program, Episode, Category, CACHE_PREVENT
|
||||||
|
|
||||||
_LOGGER = logging.getLogger('test-api')
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class TestApi(unittest.TestCase):
|
class TestApi(unittest.TestCase):
|
||||||
|
@ -11,7 +11,7 @@ import unittest
|
|||||||
from resources.lib import kodiutils
|
from resources.lib import kodiutils
|
||||||
from resources.lib.viervijfzes.auth import AuthApi
|
from resources.lib.viervijfzes.auth import AuthApi
|
||||||
|
|
||||||
_LOGGER = logging.getLogger('test-auth')
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class TestAuth(unittest.TestCase):
|
class TestAuth(unittest.TestCase):
|
||||||
|
@ -13,7 +13,7 @@ from resources.lib import kodiutils
|
|||||||
from resources.lib.viervijfzes.content import ContentApi, Episode
|
from resources.lib.viervijfzes.content import ContentApi, Episode
|
||||||
from resources.lib.viervijfzes.epg import EpgApi, EpgProgram
|
from resources.lib.viervijfzes.epg import EpgApi, EpgProgram
|
||||||
|
|
||||||
_LOGGER = logging.getLogger('test-epg')
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class TestEpg(unittest.TestCase):
|
class TestEpg(unittest.TestCase):
|
||||||
@ -66,5 +66,6 @@ class TestEpg(unittest.TestCase):
|
|||||||
# print(genres)
|
# print(genres)
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
@ -9,11 +9,6 @@ import unittest
|
|||||||
|
|
||||||
from resources.lib import addon
|
from resources.lib import addon
|
||||||
|
|
||||||
xbmc = __import__('xbmc') # pylint: disable=invalid-name
|
|
||||||
xbmcaddon = __import__('xbmcaddon') # pylint: disable=invalid-name
|
|
||||||
xbmcgui = __import__('xbmcgui') # pylint: disable=invalid-name
|
|
||||||
xbmcplugin = __import__('xbmcplugin') # pylint: disable=invalid-name
|
|
||||||
xbmcvfs = __import__('xbmcvfs') # pylint: disable=invalid-name
|
|
||||||
|
|
||||||
routing = addon.routing # pylint: disable=invalid-name
|
routing = addon.routing # pylint: disable=invalid-name
|
||||||
|
|
||||||
@ -56,8 +51,8 @@ class TestRouting(unittest.TestCase):
|
|||||||
routing.run([routing.url_for(addon.show_channel_tvguide, channel='vier'), '0', ''])
|
routing.run([routing.url_for(addon.show_channel_tvguide, channel='vier'), '0', ''])
|
||||||
routing.run([routing.url_for(addon.show_channel_tvguide_detail, channel='vier', date='today'), '0', ''])
|
routing.run([routing.url_for(addon.show_channel_tvguide_detail, channel='vier', date='today'), '0', ''])
|
||||||
|
|
||||||
def test_metadata_update(self):
|
# def test_metadata_update(self):
|
||||||
routing.run([routing.url_for(addon.metadata_update), '0', ''])
|
# routing.run([routing.url_for(addon.metadata_update), '0', ''])
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -11,7 +11,7 @@ import unittest
|
|||||||
from resources.lib.viervijfzes.content import Program
|
from resources.lib.viervijfzes.content import Program
|
||||||
from resources.lib.viervijfzes.search import SearchApi
|
from resources.lib.viervijfzes.search import SearchApi
|
||||||
|
|
||||||
_LOGGER = logging.getLogger('test-search')
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class TestSearch(unittest.TestCase):
|
class TestSearch(unittest.TestCase):
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"plugin.video.viervijfzes": {
|
|
||||||
"_comment": "do-not-add-username-and-password-here",
|
|
||||||
"metadata_update": "true"
|
|
||||||
},
|
|
||||||
"plugin.video.youtube": {
|
|
||||||
},
|
|
||||||
"service.upnext": {
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"username": "username",
|
|
||||||
"password": "password"
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"locale.language": "resource.language.nl_nl",
|
|
||||||
"network.bandwidth": 0,
|
|
||||||
"network.usehttpproxy": false,
|
|
||||||
"videolibrary.showallitems": true
|
|
||||||
}
|
|
247
tests/xbmc.py
247
tests/xbmc.py
@ -1,247 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Copyright: (c) 2019, Dag Wieers (@dagwieers) <dag@wieers.com>
|
|
||||||
# GNU General Public License v3.0 (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
||||||
""" This file implements the Kodi xbmc module, either using stubs or alternative functionality """
|
|
||||||
|
|
||||||
# pylint: disable=invalid-name,no-self-use,unused-argument
|
|
||||||
|
|
||||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
|
||||||
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import time
|
|
||||||
|
|
||||||
from xbmcextra import global_settings, import_language
|
|
||||||
|
|
||||||
LOGDEBUG = 0
|
|
||||||
LOGERROR = 4
|
|
||||||
LOGFATAL = 6
|
|
||||||
LOGINFO = 1
|
|
||||||
LOGNONE = 7
|
|
||||||
LOGNOTICE = 2
|
|
||||||
LOGSEVERE = 5
|
|
||||||
LOGWARNING = 3
|
|
||||||
|
|
||||||
LOG_MAPPING = {
|
|
||||||
LOGDEBUG: 'Debug',
|
|
||||||
LOGERROR: 'Error',
|
|
||||||
LOGFATAL: 'Fatal',
|
|
||||||
LOGINFO: 'Info',
|
|
||||||
LOGNONE: 'None',
|
|
||||||
LOGNOTICE: 'Notice',
|
|
||||||
LOGSEVERE: 'Severe',
|
|
||||||
LOGWARNING: 'Warning',
|
|
||||||
}
|
|
||||||
|
|
||||||
INFO_LABELS = {
|
|
||||||
'System.BuildVersion': '18.2',
|
|
||||||
}
|
|
||||||
|
|
||||||
REGIONS = {
|
|
||||||
'datelong': '%A, %e %B %Y',
|
|
||||||
'dateshort': '%Y-%m-%d',
|
|
||||||
}
|
|
||||||
|
|
||||||
GLOBAL_SETTINGS = global_settings()
|
|
||||||
PO = import_language(language=GLOBAL_SETTINGS.get('locale.language'))
|
|
||||||
|
|
||||||
|
|
||||||
def to_unicode(text, encoding='utf-8'):
|
|
||||||
""" Force text to unicode """
|
|
||||||
return text.decode(encoding) if isinstance(text, bytes) else text
|
|
||||||
|
|
||||||
|
|
||||||
def from_unicode(text, encoding='utf-8'):
|
|
||||||
""" Force unicode to text """
|
|
||||||
import sys
|
|
||||||
if sys.version_info.major == 2 and isinstance(text, unicode): # noqa: F821; pylint: disable=undefined-variable
|
|
||||||
return text.encode(encoding)
|
|
||||||
return text
|
|
||||||
|
|
||||||
|
|
||||||
class Keyboard:
|
|
||||||
""" A stub implementation of the xbmc Keyboard class """
|
|
||||||
|
|
||||||
def __init__(self, line='', heading=''):
|
|
||||||
""" A stub constructor for the xbmc Keyboard class """
|
|
||||||
|
|
||||||
def doModal(self, autoclose=0):
|
|
||||||
""" A stub implementation for the xbmc Keyboard class doModal() method """
|
|
||||||
|
|
||||||
def isConfirmed(self):
|
|
||||||
""" A stub implementation for the xbmc Keyboard class isConfirmed() method """
|
|
||||||
return True
|
|
||||||
|
|
||||||
def getText(self):
|
|
||||||
""" A stub implementation for the xbmc Keyboard class getText() method """
|
|
||||||
return 'test'
|
|
||||||
|
|
||||||
|
|
||||||
class Monitor:
|
|
||||||
"""A stub implementation of the xbmc Monitor class"""
|
|
||||||
|
|
||||||
def __init__(self, line='', heading=''):
|
|
||||||
"""A stub constructor for the xbmc Monitor class"""
|
|
||||||
self._deadline = time.time() + 10 # 10 seconds
|
|
||||||
|
|
||||||
def abortRequested(self):
|
|
||||||
"""A stub implementation for the xbmc Keyboard class abortRequested() method"""
|
|
||||||
return time.time() > self._deadline
|
|
||||||
|
|
||||||
def waitForAbort(self, timeout=None):
|
|
||||||
"""A stub implementation for the xbmc Keyboard class waitForAbort() method"""
|
|
||||||
time.sleep(0.5)
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class Player:
|
|
||||||
""" A stub implementation of the xbmc Player class """
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self._count = 0
|
|
||||||
|
|
||||||
def play(self, item='', listitem=None, windowed=False, startpos=-1):
|
|
||||||
""" A stub implementation for the xbmc Player class play() method """
|
|
||||||
return
|
|
||||||
|
|
||||||
def isPlaying(self):
|
|
||||||
""" A stub implementation for the xbmc Player class isPlaying() method """
|
|
||||||
# Return True four times out of five
|
|
||||||
self._count += 1
|
|
||||||
return bool(self._count % 5 != 0)
|
|
||||||
|
|
||||||
def setSubtitles(self, subtitleFile):
|
|
||||||
""" A stub implementation for the xbmc Player class setSubtitles() method """
|
|
||||||
return
|
|
||||||
|
|
||||||
def showSubtitles(self, visible):
|
|
||||||
""" A stub implementation for the xbmc Player class showSubtitles() method """
|
|
||||||
return
|
|
||||||
|
|
||||||
def getTotalTime(self):
|
|
||||||
""" A stub implementation for the xbmc Player class getTotalTime() method """
|
|
||||||
return 0
|
|
||||||
|
|
||||||
def getTime(self):
|
|
||||||
""" A stub implementation for the xbmc Player class getTime() method """
|
|
||||||
return 0
|
|
||||||
|
|
||||||
def getVideoInfoTag(self):
|
|
||||||
""" A stub implementation for the xbmc Player class getVideoInfoTag() method """
|
|
||||||
return VideoInfoTag()
|
|
||||||
|
|
||||||
def getPlayingFile(self):
|
|
||||||
""" A stub implementation for the xbmc Player class getPlayingFile() method """
|
|
||||||
return ''
|
|
||||||
|
|
||||||
|
|
||||||
class VideoInfoTag:
|
|
||||||
""" A stub implementation of the xbmc VideoInfoTag class """
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
""" A stub constructor for the xbmc VideoInfoTag class """
|
|
||||||
|
|
||||||
def getSeason(self):
|
|
||||||
""" A stub implementation for the xbmc VideoInfoTag class getSeason() method """
|
|
||||||
return 0
|
|
||||||
|
|
||||||
def getEpisode(self):
|
|
||||||
""" A stub implementation for the xbmc VideoInfoTag class getEpisode() method """
|
|
||||||
return 0
|
|
||||||
|
|
||||||
def getTVShowTitle(self):
|
|
||||||
""" A stub implementation for the xbmc VideoInfoTag class getTVShowTitle() method """
|
|
||||||
return ''
|
|
||||||
|
|
||||||
def getPlayCount(self):
|
|
||||||
""" A stub implementation for the xbmc VideoInfoTag class getPlayCount() method """
|
|
||||||
return 0
|
|
||||||
|
|
||||||
def getRating(self):
|
|
||||||
""" A stub implementation for the xbmc VideoInfoTag class getRating() method """
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def executebuiltin(string, wait=False): # pylint: disable=unused-argument
|
|
||||||
""" A stub implementation of the xbmc executebuiltin() function """
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def executeJSONRPC(jsonrpccommand):
|
|
||||||
""" A reimplementation of the xbmc executeJSONRPC() function """
|
|
||||||
command = json.loads(jsonrpccommand)
|
|
||||||
if command.get('method') == 'Settings.GetSettingValue':
|
|
||||||
key = command.get('params').get('setting')
|
|
||||||
return json.dumps(dict(id=1, jsonrpc='2.0', result=dict(value=GLOBAL_SETTINGS.get(key))))
|
|
||||||
if command.get('method') == 'Addons.GetAddonDetails':
|
|
||||||
if command.get('params', {}).get('addonid') == 'script.module.inputstreamhelper':
|
|
||||||
return json.dumps(dict(id=1, jsonrpc='2.0', result=dict(addon=dict(enabled='true', version='0.3.5'))))
|
|
||||||
return json.dumps(dict(id=1, jsonrpc='2.0', result=dict(addon=dict(enabled='true', version='1.2.3'))))
|
|
||||||
if command.get('method') == 'Textures.GetTextures':
|
|
||||||
return json.dumps(dict(id=1, jsonrpc='2.0', result=dict(textures=[dict(cachedurl="", imagehash="", lasthashcheck="", textureid=4837, url="")])))
|
|
||||||
if command.get('method') == 'Textures.RemoveTexture':
|
|
||||||
return json.dumps(dict(id=1, jsonrpc='2.0', result="OK"))
|
|
||||||
log("executeJSONRPC does not implement method '{method}'".format(**command), 'Error')
|
|
||||||
return json.dumps(dict(error=dict(code=-1, message='Not implemented'), id=1, jsonrpc='2.0'))
|
|
||||||
|
|
||||||
|
|
||||||
def getCondVisibility(string):
|
|
||||||
""" A reimplementation of the xbmc getCondVisibility() function """
|
|
||||||
if string == 'system.platform.android':
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def getInfoLabel(key):
|
|
||||||
""" A reimplementation of the xbmc getInfoLabel() function """
|
|
||||||
return INFO_LABELS.get(key)
|
|
||||||
|
|
||||||
|
|
||||||
def getLocalizedString(msgctxt):
|
|
||||||
""" A reimplementation of the xbmc getLocalizedString() function """
|
|
||||||
for entry in PO:
|
|
||||||
if entry.msgctxt == '#%s' % msgctxt:
|
|
||||||
return entry.msgstr or entry.msgid
|
|
||||||
if int(msgctxt) >= 30000:
|
|
||||||
log('Unable to translate #{msgctxt}'.format(msgctxt=msgctxt), LOGERROR)
|
|
||||||
return '<Untranslated>'
|
|
||||||
|
|
||||||
|
|
||||||
def getRegion(key):
|
|
||||||
""" A reimplementation of the xbmc getRegion() function """
|
|
||||||
return REGIONS.get(key)
|
|
||||||
|
|
||||||
|
|
||||||
def log(msg, level=LOGINFO):
|
|
||||||
""" A reimplementation of the xbmc log() function """
|
|
||||||
if level in (LOGERROR, LOGFATAL):
|
|
||||||
print('\033[31;1m%s: \033[32;0m%s\033[0;39m' % (LOG_MAPPING.get(level), to_unicode(msg)))
|
|
||||||
if level == LOGFATAL:
|
|
||||||
raise Exception(msg)
|
|
||||||
elif level in (LOGWARNING, LOGNOTICE):
|
|
||||||
print('\033[33;1m%s: \033[32;0m%s\033[0;39m' % (LOG_MAPPING.get(level), to_unicode(msg)))
|
|
||||||
else:
|
|
||||||
print('\033[32;1m%s: \033[32;0m%s\033[0;39m' % (LOG_MAPPING.get(level), to_unicode(msg)))
|
|
||||||
|
|
||||||
|
|
||||||
def setContent(self, content):
|
|
||||||
""" A stub implementation of the xbmc setContent() function """
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def sleep(seconds):
|
|
||||||
""" A reimplementation of the xbmc sleep() function """
|
|
||||||
time.sleep(seconds)
|
|
||||||
|
|
||||||
|
|
||||||
def translatePath(path):
|
|
||||||
""" A stub implementation of the xbmc translatePath() function """
|
|
||||||
if path.startswith('special://home'):
|
|
||||||
return path.replace('special://home', os.path.join(os.getcwd(), 'tests/'))
|
|
||||||
if path.startswith('special://masterprofile'):
|
|
||||||
return path.replace('special://masterprofile', os.path.join(os.getcwd(), 'tests/userdata/'))
|
|
||||||
if path.startswith('special://profile'):
|
|
||||||
return path.replace('special://profile', os.path.join(os.getcwd(), 'tests/userdata/'))
|
|
||||||
if path.startswith('special://userdata'):
|
|
||||||
return path.replace('special://userdata', os.path.join(os.getcwd(), 'tests/userdata/'))
|
|
||||||
return path
|
|
@ -1,76 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Copyright: (c) 2019, Dag Wieers (@dagwieers) <dag@wieers.com>
|
|
||||||
# GNU General Public License v3.0 (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
||||||
"""This file implements the Kodi xbmcaddon module, either using stubs or alternative functionality"""
|
|
||||||
|
|
||||||
# pylint: disable=invalid-name
|
|
||||||
|
|
||||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
|
||||||
from xbmc import getLocalizedString
|
|
||||||
from xbmcextra import ADDON_ID, ADDON_INFO, addon_settings
|
|
||||||
|
|
||||||
# Ensure the addon settings are retained (as we don't write to disk)
|
|
||||||
ADDON_SETTINGS = addon_settings(ADDON_ID)
|
|
||||||
|
|
||||||
|
|
||||||
class Addon:
|
|
||||||
"""A reimplementation of the xbmcaddon Addon class"""
|
|
||||||
|
|
||||||
def __init__(self, id=ADDON_ID): # pylint: disable=redefined-builtin
|
|
||||||
"""A stub constructor for the xbmcaddon Addon class"""
|
|
||||||
self.id = id
|
|
||||||
if id == ADDON_ID:
|
|
||||||
self.settings = ADDON_SETTINGS
|
|
||||||
else:
|
|
||||||
self.settings = addon_settings(id)
|
|
||||||
|
|
||||||
def getAddonInfo(self, key):
|
|
||||||
"""A working implementation for the xbmcaddon Addon class getAddonInfo() method"""
|
|
||||||
stub_info = dict(id=self.id, name=self.id, version='2.3.4', type='kodi.inputstream', profile='special://userdata', path='special://userdata')
|
|
||||||
# Add stub_info values to ADDON_INFO when missing (e.g. path and profile)
|
|
||||||
addon_info = dict(stub_info, **ADDON_INFO)
|
|
||||||
return addon_info.get(self.id, stub_info).get(key)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def getLocalizedString(msgctxt):
|
|
||||||
"""A working implementation for the xbmcaddon Addon class getLocalizedString() method"""
|
|
||||||
return getLocalizedString(msgctxt)
|
|
||||||
|
|
||||||
def getSetting(self, key):
|
|
||||||
"""A working implementation for the xbmcaddon Addon class getSetting() method"""
|
|
||||||
return self.settings.get(key, '')
|
|
||||||
|
|
||||||
def getSettingBool(self, key):
|
|
||||||
"""A working implementation for the xbmcaddon Addon class getSettingBool() method"""
|
|
||||||
return bool(self.settings.get(key, False))
|
|
||||||
|
|
||||||
def getSettingInt(self, key):
|
|
||||||
"""A working implementation for the xbmcaddon Addon class getSettingInt() method"""
|
|
||||||
return int(self.settings.get(key, 0))
|
|
||||||
|
|
||||||
def getSettingNumber(self, key):
|
|
||||||
"""A working implementation for the xbmcaddon Addon class getSettingNumber() method"""
|
|
||||||
return float(self.settings.get(key, 0.0))
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def openSettings():
|
|
||||||
"""A stub implementation for the xbmcaddon Addon class openSettings() method"""
|
|
||||||
|
|
||||||
def setSetting(self, key, value):
|
|
||||||
"""A stub implementation for the xbmcaddon Addon class setSetting() method"""
|
|
||||||
self.settings[key] = value
|
|
||||||
# NOTE: Disable actual writing as it is no longer needed for testing
|
|
||||||
# with open('tests/userdata/addon_settings.json', 'w') as fd:
|
|
||||||
# json.dump(filtered_settings, fd, sort_keys=True, indent=4)
|
|
||||||
|
|
||||||
def setSettingBool(self, key, value):
|
|
||||||
"""A stub implementation for the xbmcaddon Addon class setSettingBool() method"""
|
|
||||||
self.settings[key] = value
|
|
||||||
|
|
||||||
def setSettingInt(self, key, value):
|
|
||||||
"""A stub implementation for the xbmcaddon Addon class setSettingInt() method"""
|
|
||||||
self.settings[key] = value
|
|
||||||
|
|
||||||
def setSettingNumber(self, key, value):
|
|
||||||
"""A stub implementation for the xbmcaddon Addon class setSettingNumber() method"""
|
|
||||||
self.settings[key] = value
|
|
@ -1,188 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Copyright: (c) 2019, Dag Wieers (@dagwieers) <dag@wieers.com>
|
|
||||||
# GNU General Public License v3.0 (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
||||||
"""Extra functions for testing"""
|
|
||||||
|
|
||||||
# pylint: disable=invalid-name
|
|
||||||
|
|
||||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
|
||||||
import os
|
|
||||||
import xml.etree.ElementTree as ET
|
|
||||||
import polib
|
|
||||||
|
|
||||||
|
|
||||||
def kodi_to_ansi(string):
|
|
||||||
"""Convert Kodi format tags to ANSI codes"""
|
|
||||||
if string is None:
|
|
||||||
return None
|
|
||||||
string = string.replace('[B]', '\033[1m')
|
|
||||||
string = string.replace('[/B]', '\033[21m')
|
|
||||||
string = string.replace('[I]', '\033[3m')
|
|
||||||
string = string.replace('[/I]', '\033[23m')
|
|
||||||
string = string.replace('[COLOR gray]', '\033[30;1m')
|
|
||||||
string = string.replace('[COLOR red]', '\033[31m')
|
|
||||||
string = string.replace('[COLOR green]', '\033[32m')
|
|
||||||
string = string.replace('[COLOR yellow]', '\033[33m')
|
|
||||||
string = string.replace('[COLOR blue]', '\033[34m')
|
|
||||||
string = string.replace('[COLOR purple]', '\033[35m')
|
|
||||||
string = string.replace('[COLOR cyan]', '\033[36m')
|
|
||||||
string = string.replace('[COLOR white]', '\033[37m')
|
|
||||||
string = string.replace('[/COLOR]', '\033[39;0m')
|
|
||||||
return string
|
|
||||||
|
|
||||||
|
|
||||||
def uri_to_path(uri):
|
|
||||||
"""Shorten a plugin URI to just the path"""
|
|
||||||
if uri is None:
|
|
||||||
return None
|
|
||||||
return ' \033[33m→ \033[34m%s\033[39;0m' % uri.replace('plugin://' + ADDON_ID, '')
|
|
||||||
|
|
||||||
|
|
||||||
def read_addon_xml(path):
|
|
||||||
"""Parse the addon.xml and return an info dictionary"""
|
|
||||||
info = dict(
|
|
||||||
path='./', # '/storage/.kodi/addons/plugin.video.vrt.nu',
|
|
||||||
profile='special://userdata', # 'special://profile/addon_data/plugin.video.vrt.nu/',
|
|
||||||
type='xbmc.python.pluginsource',
|
|
||||||
)
|
|
||||||
|
|
||||||
tree = ET.parse(path)
|
|
||||||
root = tree.getroot()
|
|
||||||
|
|
||||||
info.update(root.attrib) # Add 'id', 'name' and 'version'
|
|
||||||
info['author'] = info.pop('provider-name')
|
|
||||||
|
|
||||||
for child in root:
|
|
||||||
if child.attrib.get('point') != 'xbmc.addon.metadata':
|
|
||||||
continue
|
|
||||||
for grandchild in child:
|
|
||||||
# Handle assets differently
|
|
||||||
if grandchild.tag == 'assets':
|
|
||||||
for asset in grandchild:
|
|
||||||
info[asset.tag] = asset.text
|
|
||||||
continue
|
|
||||||
# Not in English ? Drop it
|
|
||||||
if grandchild.attrib.get('lang', 'en_GB') != 'en_GB':
|
|
||||||
continue
|
|
||||||
# Add metadata
|
|
||||||
info[grandchild.tag] = grandchild.text
|
|
||||||
|
|
||||||
return {info['name']: info}
|
|
||||||
|
|
||||||
|
|
||||||
def global_settings():
|
|
||||||
"""Use the global_settings file"""
|
|
||||||
import json
|
|
||||||
try:
|
|
||||||
with open('tests/userdata/global_settings.json') as f:
|
|
||||||
settings = json.load(f)
|
|
||||||
except OSError as e:
|
|
||||||
print("Error: Cannot use 'tests/userdata/global_settings.json' : %s" % e)
|
|
||||||
settings = {
|
|
||||||
'locale.language': 'resource.language.en_gb',
|
|
||||||
'network.bandwidth': 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
if 'PROXY_SERVER' in os.environ:
|
|
||||||
settings['network.usehttpproxy'] = True
|
|
||||||
settings['network.httpproxytype'] = 0
|
|
||||||
print('Using proxy server from environment variable PROXY_SERVER')
|
|
||||||
settings['network.httpproxyserver'] = os.environ.get('PROXY_SERVER')
|
|
||||||
if 'PROXY_PORT' in os.environ:
|
|
||||||
print('Using proxy server from environment variable PROXY_PORT')
|
|
||||||
settings['network.httpproxyport'] = os.environ.get('PROXY_PORT')
|
|
||||||
if 'PROXY_USERNAME' in os.environ:
|
|
||||||
print('Using proxy server from environment variable PROXY_USERNAME')
|
|
||||||
settings['network.httpproxyusername'] = os.environ.get('PROXY_USERNAME')
|
|
||||||
if 'PROXY_PASSWORD' in os.environ:
|
|
||||||
print('Using proxy server from environment variable PROXY_PASSWORD')
|
|
||||||
settings['network.httpproxypassword'] = os.environ.get('PROXY_PASSWORD')
|
|
||||||
return settings
|
|
||||||
|
|
||||||
|
|
||||||
def addon_settings(addon_id=None):
|
|
||||||
"""Use the addon_settings file"""
|
|
||||||
import json
|
|
||||||
try:
|
|
||||||
with open('tests/userdata/addon_settings.json') as f:
|
|
||||||
settings = json.load(f)
|
|
||||||
except OSError as e:
|
|
||||||
print("Error: Cannot use 'tests/userdata/addon_settings.json' : %s" % e)
|
|
||||||
settings = {}
|
|
||||||
|
|
||||||
# Read credentials from environment or credentials.json
|
|
||||||
if 'ADDON_USERNAME' in os.environ and 'ADDON_PASSWORD' in os.environ:
|
|
||||||
# print('Using credentials from the environment variables ADDON_USERNAME and ADDON_PASSWORD')
|
|
||||||
settings[ADDON_ID]['username'] = os.environ.get('ADDON_USERNAME')
|
|
||||||
settings[ADDON_ID]['password'] = os.environ.get('ADDON_PASSWORD')
|
|
||||||
elif os.path.exists('tests/userdata/credentials.json'):
|
|
||||||
# print('Using credentials from tests/userdata/credentials.json')
|
|
||||||
with open('tests/userdata/credentials.json') as f:
|
|
||||||
credentials = json.load(f)
|
|
||||||
settings[ADDON_ID].update(credentials)
|
|
||||||
else:
|
|
||||||
print("Error: Cannot use 'tests/userdata/credentials.json'")
|
|
||||||
|
|
||||||
if addon_id:
|
|
||||||
return settings[addon_id]
|
|
||||||
|
|
||||||
return settings
|
|
||||||
|
|
||||||
|
|
||||||
def import_language(language):
|
|
||||||
"""Process the language.po file"""
|
|
||||||
try:
|
|
||||||
podb = polib.pofile('resources/language/{language}/strings.po'.format(language=language))
|
|
||||||
except IOError:
|
|
||||||
podb = polib.pofile('resources/language/resource.language.en_gb/strings.po')
|
|
||||||
|
|
||||||
podb.extend([
|
|
||||||
# WEEKDAY_LONG
|
|
||||||
polib.POEntry(msgctxt='#11', msgstr='Monday'),
|
|
||||||
polib.POEntry(msgctxt='#12', msgstr='Tuesday'),
|
|
||||||
polib.POEntry(msgctxt='#13', msgstr='Wednesday'),
|
|
||||||
polib.POEntry(msgctxt='#14', msgstr='Thursday'),
|
|
||||||
polib.POEntry(msgctxt='#15', msgstr='Friday'),
|
|
||||||
polib.POEntry(msgctxt='#16', msgstr='Saturday'),
|
|
||||||
polib.POEntry(msgctxt='#17', msgstr='Sunday'),
|
|
||||||
# MONTH_LONG
|
|
||||||
polib.POEntry(msgctxt='#21', msgstr='January'),
|
|
||||||
polib.POEntry(msgctxt='#22', msgstr='February'),
|
|
||||||
polib.POEntry(msgctxt='#23', msgstr='March'),
|
|
||||||
polib.POEntry(msgctxt='#24', msgstr='April'),
|
|
||||||
polib.POEntry(msgctxt='#25', msgstr='May'),
|
|
||||||
polib.POEntry(msgctxt='#26', msgstr='June'),
|
|
||||||
polib.POEntry(msgctxt='#27', msgstr='July'),
|
|
||||||
polib.POEntry(msgctxt='#28', msgstr='August'),
|
|
||||||
polib.POEntry(msgctxt='#29', msgstr='September'),
|
|
||||||
polib.POEntry(msgctxt='#30', msgstr='October'),
|
|
||||||
polib.POEntry(msgctxt='#31', msgstr='November'),
|
|
||||||
polib.POEntry(msgctxt='#32', msgstr='December'),
|
|
||||||
# WEEKDAY_SHORT
|
|
||||||
polib.POEntry(msgctxt='#41', msgstr='Mon'),
|
|
||||||
polib.POEntry(msgctxt='#42', msgstr='Tue'),
|
|
||||||
polib.POEntry(msgctxt='#43', msgstr='Wed'),
|
|
||||||
polib.POEntry(msgctxt='#44', msgstr='Thu'),
|
|
||||||
polib.POEntry(msgctxt='#45', msgstr='Fri'),
|
|
||||||
polib.POEntry(msgctxt='#46', msgstr='Sat'),
|
|
||||||
polib.POEntry(msgctxt='#47', msgstr='Sun'),
|
|
||||||
# MONTH_LONG
|
|
||||||
polib.POEntry(msgctxt='#51', msgstr='Jan'),
|
|
||||||
polib.POEntry(msgctxt='#52', msgstr='Feb'),
|
|
||||||
polib.POEntry(msgctxt='#53', msgstr='Mar'),
|
|
||||||
polib.POEntry(msgctxt='#54', msgstr='Apr'),
|
|
||||||
polib.POEntry(msgctxt='#55', msgstr='May'),
|
|
||||||
polib.POEntry(msgctxt='#56', msgstr='Jun'),
|
|
||||||
polib.POEntry(msgctxt='#57', msgstr='Jul'),
|
|
||||||
polib.POEntry(msgctxt='#58', msgstr='Aug'),
|
|
||||||
polib.POEntry(msgctxt='#59', msgstr='Sep'),
|
|
||||||
polib.POEntry(msgctxt='#50', msgstr='Oct'),
|
|
||||||
polib.POEntry(msgctxt='#51', msgstr='Nov'),
|
|
||||||
polib.POEntry(msgctxt='#52', msgstr='Dec'),
|
|
||||||
])
|
|
||||||
|
|
||||||
return podb
|
|
||||||
|
|
||||||
|
|
||||||
ADDON_INFO = read_addon_xml('addon.xml')
|
|
||||||
ADDON_ID = next(iter(list(ADDON_INFO.values()))).get('id')
|
|
327
tests/xbmcgui.py
327
tests/xbmcgui.py
@ -1,327 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Copyright: (c) 2019, Dag Wieers (@dagwieers) <dag@wieers.com>
|
|
||||||
# GNU General Public License v3.0 (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
||||||
"""This file implements the Kodi xbmcgui module, either using stubs or alternative functionality"""
|
|
||||||
|
|
||||||
# pylint: disable=invalid-name,super-on-old-class,too-many-arguments,unused-argument,useless-super-delegation
|
|
||||||
|
|
||||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
|
||||||
import sys
|
|
||||||
from xbmcextra import kodi_to_ansi
|
|
||||||
|
|
||||||
|
|
||||||
class Control:
|
|
||||||
"""A reimplementation of the xbmcgui Control class"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
"""A stub constructor for the xbmcgui Control class"""
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def selectItem(index):
|
|
||||||
"""A stub implementation for the xbmcgui Control class selectItem() method"""
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
class ControlLabel(Control):
|
|
||||||
"""A reimplementation of the xbmcgui ControlLabel class"""
|
|
||||||
|
|
||||||
def __init__(self): # pylint: disable=super-init-not-called
|
|
||||||
"""A stub constructor for the xbmcgui ControlLabel class"""
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def getLabel():
|
|
||||||
"""A stub implementation for the xbmcgui ControlLabel class getLabel() method"""
|
|
||||||
return 'Label'
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def setLabel(label='', font=None, textColor=None, disabledColor=None, shadowColor=None, focusedColor=None, label2=''):
|
|
||||||
"""A stub implementation for the xbmcgui ControlLabel class getLabel() method"""
|
|
||||||
|
|
||||||
|
|
||||||
class Dialog:
|
|
||||||
"""A reimplementation of the xbmcgui Dialog class"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
"""A stub constructor for the xbmcgui Dialog class"""
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def notification(heading, message, icon=None, time=None, sound=None):
|
|
||||||
"""A working implementation for the xbmcgui Dialog class notification() method"""
|
|
||||||
heading = kodi_to_ansi(heading)
|
|
||||||
message = kodi_to_ansi(message)
|
|
||||||
print('\033[37;44;1mNOTIFICATION:\033[35;49;1m [%s] \033[37;1m%s\033[39;0m' % (heading, message))
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def ok(heading, message='', line1='', line2='', line3=''):
|
|
||||||
"""A stub implementation for the xbmcgui Dialog class ok() method"""
|
|
||||||
heading = kodi_to_ansi(heading)
|
|
||||||
message = kodi_to_ansi(message)
|
|
||||||
print('\033[37;44;1mOK:\033[35;49;1m [%s] \033[37;1m%s\033[39;0m' % (heading, message or line1))
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def info(listitem):
|
|
||||||
"""A stub implementation for the xbmcgui Dialog class info() method"""
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def select(heading, opt_list, autoclose=0, preselect=None, useDetails=False):
|
|
||||||
"""A stub implementation for the xbmcgui Dialog class select() method"""
|
|
||||||
if preselect is None:
|
|
||||||
preselect = []
|
|
||||||
heading = kodi_to_ansi(heading)
|
|
||||||
print('\033[37;44;1mSELECT:\033[35;49;1m [%s] \033[37;1m%s\033[39;0m' % (heading, ', '.join(opt_list)))
|
|
||||||
return -1
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def multiselect(heading, options, autoclose=0, preselect=None, useDetails=False): # pylint: disable=useless-return
|
|
||||||
"""A stub implementation for the xbmcgui Dialog class multiselect() method"""
|
|
||||||
if preselect is None:
|
|
||||||
preselect = []
|
|
||||||
heading = kodi_to_ansi(heading)
|
|
||||||
print('\033[37;44;1mMULTISELECT:\033[35;49;1m [%s] \033[37;1m%s\033[39;0m' % (heading, ', '.join(options)))
|
|
||||||
return None
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def contextmenu(items):
|
|
||||||
"""A stub implementation for the xbmcgui Dialog class contextmenu() method"""
|
|
||||||
print('\033[37;44;1mCONTEXTMENU:\033[35;49;1m \033[37;1m%s\033[39;0m' % (', '.join(items)))
|
|
||||||
return -1
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def yesno(heading, message='', line1='', line2='', line3='', nolabel=None, yeslabel=None, autoclose=0):
|
|
||||||
"""A stub implementation for the xbmcgui Dialog class yesno() method"""
|
|
||||||
heading = kodi_to_ansi(heading)
|
|
||||||
message = kodi_to_ansi(message)
|
|
||||||
print('\033[37;44;1mYESNO:\033[35;49;1m [%s] \033[37;1m%s\033[39;0m' % (heading, message or line1))
|
|
||||||
return True
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def textviewer(heading, text=None, usemono=None):
|
|
||||||
"""A stub implementation for the xbmcgui Dialog class textviewer() method"""
|
|
||||||
heading = kodi_to_ansi(heading)
|
|
||||||
text = kodi_to_ansi(text)
|
|
||||||
print('\033[37;44;1mTEXTVIEWER:\033[35;49;1m [%s]\n\033[37;1m%s\033[39;0m' % (heading, text))
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def browseSingle(type, heading, shares, mask=None, useThumbs=None, treatAsFolder=None, defaultt=None): # pylint: disable=redefined-builtin
|
|
||||||
"""A stub implementation for the xbmcgui Dialog class browseSingle() method"""
|
|
||||||
print('\033[37;44;1mBROWSESINGLE:\033[35;49;1m [%s] \033[37;1m%s\033[39;0m' % (type, heading))
|
|
||||||
return 'special://masterprofile/addon_data/script.module.inputstreamhelper/'
|
|
||||||
|
|
||||||
|
|
||||||
class DialogProgress:
|
|
||||||
"""A reimplementation of the xbmcgui DialogProgress"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
"""A stub constructor for the xbmcgui DialogProgress class"""
|
|
||||||
self.percent = 0
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
"""A stub implementation for the xbmcgui DialogProgress class close() method"""
|
|
||||||
self.percent = 0
|
|
||||||
print()
|
|
||||||
sys.stdout.flush()
|
|
||||||
|
|
||||||
def create(self, heading, message='', line1='', line2='', line3=''):
|
|
||||||
"""A stub implementation for the xbmcgui DialogProgress class create() method"""
|
|
||||||
self.percent = 0
|
|
||||||
heading = kodi_to_ansi(heading)
|
|
||||||
line1 = kodi_to_ansi(line1)
|
|
||||||
print('\033[37;44;1mPROGRESS:\033[35;49;1m [%s] \033[37;1m%s\033[39;0m' % (heading, message or line1))
|
|
||||||
sys.stdout.flush()
|
|
||||||
|
|
||||||
def iscanceled(self):
|
|
||||||
"""A stub implementation for the xbmcgui DialogProgress class iscanceled() method"""
|
|
||||||
return self.percent > 5 # Cancel at 5%
|
|
||||||
|
|
||||||
def update(self, percent, message='', line1='', line2='', line3=''):
|
|
||||||
"""A stub implementation for the xbmcgui DialogProgress class update() method"""
|
|
||||||
if (percent - 5) < self.percent:
|
|
||||||
return
|
|
||||||
self.percent = percent
|
|
||||||
line1 = kodi_to_ansi(line1)
|
|
||||||
line2 = kodi_to_ansi(line2)
|
|
||||||
line3 = kodi_to_ansi(line3)
|
|
||||||
if line1 or line2 or line3:
|
|
||||||
print('\033[1G\033[37;44;1mPROGRESS:\033[35;49;1m [%d%%] \033[37;1m%s\033[39;0m' % (percent, message or line1 or line2 or line3), end='')
|
|
||||||
else:
|
|
||||||
print('\033[1G\033[37;44;1mPROGRESS:\033[35;49;1m [%d%%]\033[39;0m' % (percent), end='')
|
|
||||||
sys.stdout.flush()
|
|
||||||
|
|
||||||
|
|
||||||
class DialogProgressBG:
|
|
||||||
"""A reimplementation of the xbmcgui DialogProgressBG"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
"""A stub constructor for the xbmcgui DialogProgressBG class"""
|
|
||||||
self.percent = 0
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def close():
|
|
||||||
"""A stub implementation for the xbmcgui DialogProgressBG class close() method"""
|
|
||||||
print()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def create(heading, message):
|
|
||||||
"""A stub implementation for the xbmcgui DialogProgressBG class create() method"""
|
|
||||||
heading = kodi_to_ansi(heading)
|
|
||||||
message = kodi_to_ansi(message)
|
|
||||||
print('\033[37;44;1mPROGRESS:\033[35;49;1m [%s] \033[37;1m%s\033[39;0m' % (heading, message))
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def isfinished():
|
|
||||||
"""A stub implementation for the xbmcgui DialogProgressBG class isfinished() method"""
|
|
||||||
|
|
||||||
def update(self, percent, heading=None, message=None):
|
|
||||||
"""A stub implementation for the xbmcgui DialogProgressBG class update() method"""
|
|
||||||
if (percent - 5) < self.percent:
|
|
||||||
return
|
|
||||||
self.percent = percent
|
|
||||||
message = kodi_to_ansi(message)
|
|
||||||
if message:
|
|
||||||
print('\033[37;44;1mPROGRESS:\033[35;49;1m [%d%%] \033[37;1m%s\033[39;0m' % (percent, message))
|
|
||||||
else:
|
|
||||||
print('\033[1G\033[37;44;1mPROGRESS:\033[35;49;1m [%d%%]\033[39;0m' % (percent), end='')
|
|
||||||
|
|
||||||
|
|
||||||
class DialogBusy:
|
|
||||||
"""A reimplementation of the xbmcgui DialogBusy"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
"""A stub constructor for the xbmcgui DialogBusy class"""
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def close():
|
|
||||||
"""A stub implementation for the xbmcgui DialogBusy class close() method"""
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def create():
|
|
||||||
"""A stub implementation for the xbmcgui DialogBusy class create() method"""
|
|
||||||
|
|
||||||
|
|
||||||
class ListItem:
|
|
||||||
"""A reimplementation of the xbmcgui ListItem class"""
|
|
||||||
|
|
||||||
def __init__(self, label='', label2='', iconImage='', thumbnailImage='', path='', offscreen=False):
|
|
||||||
"""A stub constructor for the xbmcgui ListItem class"""
|
|
||||||
self.label = kodi_to_ansi(label)
|
|
||||||
self.label2 = kodi_to_ansi(label2)
|
|
||||||
self.path = path
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def addContextMenuItems(items, replaceItems=False):
|
|
||||||
"""A stub implementation for the xbmcgui ListItem class addContextMenuItems() method"""
|
|
||||||
return
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def addStreamInfo(stream_type, stream_values):
|
|
||||||
"""A stub implementation for the xbmcgui LitItem class addStreamInfo() method"""
|
|
||||||
return
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def setArt(key):
|
|
||||||
"""A stub implementation for the xbmcgui ListItem class setArt() method"""
|
|
||||||
return
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def setContentLookup(enable):
|
|
||||||
"""A stub implementation for the xbmcgui ListItem class setContentLookup() method"""
|
|
||||||
return
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def setInfo(type, infoLabels): # pylint: disable=redefined-builtin
|
|
||||||
"""A stub implementation for the xbmcgui ListItem class setInfo() method"""
|
|
||||||
return
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def setIsFolder(isFolder):
|
|
||||||
"""A stub implementation for the xbmcgui ListItem class setIsFolder() method"""
|
|
||||||
return
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def setMimeType(mimetype):
|
|
||||||
"""A stub implementation for the xbmcgui ListItem class setMimeType() method"""
|
|
||||||
return
|
|
||||||
|
|
||||||
def setPath(self, path):
|
|
||||||
"""A stub implementation for the xbmcgui ListItem class setPath() method"""
|
|
||||||
self.path = path
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def setProperty(key, value):
|
|
||||||
"""A stub implementation for the xbmcgui ListItem class setProperty() method"""
|
|
||||||
return
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def setProperties(dictionary):
|
|
||||||
"""A stub implementation for the xbmcgui ListItem class setProperties() method"""
|
|
||||||
return
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def setSubtitles(subtitleFiles):
|
|
||||||
"""A stub implementation for the xbmcgui ListItem class setSubtitles() method"""
|
|
||||||
return
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def setUniqueIDs(values, defaultrating=None):
|
|
||||||
"""A stub implementation for the xbmcgui ListItem class setUniqueIDs() method"""
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
class Window:
|
|
||||||
"""A reimplementation of the xbmcgui Window"""
|
|
||||||
|
|
||||||
def __init__(self, existingwindowId=-1):
|
|
||||||
"""A stub constructor for the xbmcgui Window class"""
|
|
||||||
return None
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
"""A stub implementation for the xbmcgui Window class close() method"""
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def getControl():
|
|
||||||
"""A stub implementation for the xbmcgui Window class getControl() method"""
|
|
||||||
return ControlLabel()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def getFocusId():
|
|
||||||
"""A stub implementation for the xbmcgui Window class getFocusId() method"""
|
|
||||||
return 0
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def getProperty(key):
|
|
||||||
"""A stub implementation for the xbmcgui Window class getProperty() method"""
|
|
||||||
return ''
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def setProperty(key, value):
|
|
||||||
"""A stub implementation for the xbmcgui Window class setProperty() method"""
|
|
||||||
return
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def clearProperty(key):
|
|
||||||
"""A stub implementation for the xbmcgui Window class clearProperty() method"""
|
|
||||||
return
|
|
||||||
|
|
||||||
def show(self):
|
|
||||||
"""A stub implementation for the xbmcgui Window class show() method"""
|
|
||||||
|
|
||||||
|
|
||||||
class WindowXML(Window):
|
|
||||||
"""A reimplementation of the xbmcgui WindowXML"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
"""A stub constructor for the xbmcgui WindowXML class"""
|
|
||||||
super(WindowXML, self).__init__()
|
|
||||||
|
|
||||||
|
|
||||||
class WindowXMLDialog(WindowXML):
|
|
||||||
"""A reimplementation of the xbmcgui WindowXMLDialog"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
"""A stub constructor for the xbmcgui WindowXMLDialog class"""
|
|
||||||
super(WindowXMLDialog, self).__init__()
|
|
||||||
|
|
||||||
|
|
||||||
def getCurrentWindowId():
|
|
||||||
"""A stub implementation of the xbmcgui getCurrentWindowId() method"""
|
|
||||||
return 0
|
|
@ -1,112 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Copyright: (c) 2019, Dag Wieers (@dagwieers) <dag@wieers.com>
|
|
||||||
# GNU General Public License v3.0 (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
||||||
"""This file implements the Kodi xbmcplugin module, either using stubs or alternative functionality"""
|
|
||||||
|
|
||||||
# pylint: disable=invalid-name,unused-argument
|
|
||||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
|
||||||
|
|
||||||
from xbmc import LOGFATAL, LOGINFO, log
|
|
||||||
from xbmcextra import kodi_to_ansi, uri_to_path
|
|
||||||
|
|
||||||
try: # Python 3
|
|
||||||
from urllib.error import HTTPError
|
|
||||||
from urllib.request import Request, urlopen
|
|
||||||
except ImportError: # Python 2
|
|
||||||
from urllib2 import HTTPError, Request, urlopen
|
|
||||||
|
|
||||||
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_AND_YEAR = 12
|
|
||||||
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 # This is SORT_METHOD_YEAR in Kodi
|
|
||||||
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_CHANNEL_NUMBER = 42
|
|
||||||
SORT_METHOD_BITRATE = 43
|
|
||||||
SORT_METHOD_DATE_TAKEN = 44
|
|
||||||
|
|
||||||
|
|
||||||
def addDirectoryItem(handle, path, listitem, isFolder=False):
|
|
||||||
"""A reimplementation of the xbmcplugin addDirectoryItems() function"""
|
|
||||||
label = kodi_to_ansi(listitem.label)
|
|
||||||
path = uri_to_path(path) if path else ''
|
|
||||||
# perma = kodi_to_ansi(listitem.label) # FIXME: Add permalink
|
|
||||||
bullet = '»' if isFolder else '·'
|
|
||||||
print('{bullet} {label}{path}'.format(bullet=bullet, label=label, path=path))
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def addDirectoryItems(handle, listing, length):
|
|
||||||
"""A reimplementation of the xbmcplugin addDirectoryItems() function"""
|
|
||||||
for item in listing:
|
|
||||||
addDirectoryItem(handle, item[0], item[1], item[2])
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def addSortMethod(handle, sortMethod):
|
|
||||||
"""A stub implementation of the xbmcplugin addSortMethod() function"""
|
|
||||||
|
|
||||||
|
|
||||||
def endOfDirectory(handle, succeeded=True, updateListing=True, cacheToDisc=True):
|
|
||||||
"""A stub implementation of the xbmcplugin endOfDirectory() function"""
|
|
||||||
# print(kodi_to_ansi('[B]-=( [COLOR cyan]--------[/COLOR] )=-[/B]'))
|
|
||||||
|
|
||||||
|
|
||||||
def setContent(handle, content):
|
|
||||||
"""A stub implementation of the xbmcplugin setContent() function"""
|
|
||||||
|
|
||||||
|
|
||||||
def setPluginFanart(handle, image, color1=None, color2=None, color3=None):
|
|
||||||
"""A stub implementation of the xbmcplugin setPluginFanart() function"""
|
|
||||||
|
|
||||||
|
|
||||||
def setPluginCategory(handle, category):
|
|
||||||
"""A reimplementation of the xbmcplugin setPluginCategory() function"""
|
|
||||||
print(kodi_to_ansi('[B]-=( [COLOR cyan]%s[/COLOR] )=-[/B]' % category))
|
|
||||||
|
|
||||||
|
|
||||||
def setResolvedUrl(handle, succeeded, listitem):
|
|
||||||
"""A stub implementation of the xbmcplugin setResolvedUrl() function"""
|
|
||||||
request = Request(listitem.path)
|
|
||||||
request.get_method = lambda: 'HEAD'
|
|
||||||
try:
|
|
||||||
response = urlopen(request)
|
|
||||||
log('Stream playing successfully: %s' % response.code, LOGINFO)
|
|
||||||
except HTTPError as exc:
|
|
||||||
log('Playing stream returned: %s' % exc, LOGFATAL)
|
|
@ -1,80 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Copyright: (c) 2019, Dag Wieers (@dagwieers) <dag@wieers.com>
|
|
||||||
# GNU General Public License v3.0 (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
||||||
"""This file implements the Kodi xbmcvfs module, either using stubs or alternative functionality"""
|
|
||||||
|
|
||||||
# pylint: disable=invalid-name
|
|
||||||
|
|
||||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
|
||||||
import os
|
|
||||||
from shutil import copyfile
|
|
||||||
|
|
||||||
|
|
||||||
def File(path, flags='r'):
|
|
||||||
"""A reimplementation of the xbmcvfs File() function"""
|
|
||||||
return open(path, flags)
|
|
||||||
|
|
||||||
|
|
||||||
def Stat(path):
|
|
||||||
"""A reimplementation of the xbmcvfs Stat() function"""
|
|
||||||
|
|
||||||
class stat:
|
|
||||||
"""A reimplementation of the xbmcvfs stat class"""
|
|
||||||
|
|
||||||
def __init__(self, path):
|
|
||||||
"""The constructor xbmcvfs stat class"""
|
|
||||||
self._stat = os.stat(path)
|
|
||||||
|
|
||||||
def st_mtime(self):
|
|
||||||
"""The xbmcvfs stat class st_mtime method"""
|
|
||||||
return self._stat.st_mtime
|
|
||||||
|
|
||||||
return stat(path)
|
|
||||||
|
|
||||||
|
|
||||||
def copy(src, dst):
|
|
||||||
"""A reimplementation of the xbmcvfs mkdir() function"""
|
|
||||||
return copyfile(src, dst) == dst
|
|
||||||
|
|
||||||
|
|
||||||
def delete(path):
|
|
||||||
"""A reimplementation of the xbmcvfs delete() function"""
|
|
||||||
try:
|
|
||||||
os.remove(path)
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def exists(path):
|
|
||||||
"""A reimplementation of the xbmcvfs exists() function"""
|
|
||||||
return os.path.exists(path)
|
|
||||||
|
|
||||||
|
|
||||||
def listdir(path):
|
|
||||||
"""A reimplementation of the xbmcvfs listdir() function"""
|
|
||||||
files = []
|
|
||||||
dirs = []
|
|
||||||
if not exists(path):
|
|
||||||
return dirs, files
|
|
||||||
for filename in os.listdir(path):
|
|
||||||
fullname = os.path.join(path, filename)
|
|
||||||
if os.path.isfile(fullname):
|
|
||||||
files.append(filename)
|
|
||||||
if os.path.isdir(fullname):
|
|
||||||
dirs.append(filename)
|
|
||||||
return dirs, files
|
|
||||||
|
|
||||||
|
|
||||||
def mkdir(path):
|
|
||||||
"""A reimplementation of the xbmcvfs mkdir() function"""
|
|
||||||
return os.mkdir(path)
|
|
||||||
|
|
||||||
|
|
||||||
def mkdirs(path):
|
|
||||||
"""A reimplementation of the xbmcvfs mkdirs() function"""
|
|
||||||
return os.makedirs(path)
|
|
||||||
|
|
||||||
|
|
||||||
def rmdir(path):
|
|
||||||
"""A reimplementation of the xbmcvfs rmdir() function"""
|
|
||||||
return os.rmdir(path)
|
|
Loading…
Reference in New Issue
Block a user