Skip to content

Commit

Permalink
Python add-on for ISA tests
Browse files Browse the repository at this point in the history
  • Loading branch information
CastagnaIT committed Oct 30, 2024
1 parent c77b406 commit 2cfb688
Show file tree
Hide file tree
Showing 8 changed files with 1,326 additions and 0 deletions.
12 changes: 12 additions & 0 deletions plugin.video.isasamples/addon.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# -*- coding: utf-8 -*-
"""
Copyright (C) 2024 Team Kodi
SPDX-License-Identifier: GPL-2.0-or-later
See LICENSES/README.md for more information.
"""
import sys

from main import router

if __name__ == '__main__':
router(sys.argv)
22 changes: 22 additions & 0 deletions plugin.video.isasamples/addon.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.video.isasamples" name="InputStream Adaptive Samples" version="0.0.1" provider-name="Kodi Team">
<requires>
<import addon="xbmc.python" version="3.0.0"/>
</requires>
<extension point="xbmc.python.pluginsource" library="addon.py">
<provides>video</provides>
</extension>
<extension point="xbmc.addon.metadata">
<platform>all</platform>
<summary>InputStream Adaptive Samples</summary>
<description>This add-on provides sample streams for testing InputStream Adaptive demuxer. DRM libraries are not provided and must be installed separately.</description>
<disclaimer lang="en">This add-on includes links to websites that provide free unrestricted test streams; no files are hosted by us. These streams may be copyrighted by their respective authors.</disclaimer>
<license>GPL-2.0-or-later</license>
<website>https://github.com/xbmc/inputstream.adaptive/wiki</website>
<source>https://github.com/xbmc/inputstream.adaptive</source>
<assets>
<icon>resources/icon.png</icon>
</assets>
<reuselanguageinvoker>false</reuselanguageinvoker>
</extension>
</addon>
225 changes: 225 additions & 0 deletions plugin.video.isasamples/helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
# -*- coding: utf-8 -*-
"""
Copyright (C) 2024 Team Kodi
SPDX-License-Identifier: GPL-2.0-or-later
See LICENSES/README.md for more information.
"""
import operator

import xbmc
import xbmcaddon

LOG_ERROR = "error"
LOG_DEBUG = "debug"
LOG_INFO = "info"

ISA_ADDON_NAME = "inputstream.adaptive"
ISAS_ADDON_NAME = "plugin.video.isasamples"

SETTING_SHOW_FEATURE_FLAGS = 'streams.show.featureflags'
SETTING_SHOW_CRYPTO_FLAGS = 'streams.show.cryptoflags'
SETTING_SHOW_CODECS = 'streams.show.codecs'
# Each drm method setting, must match the enum on the "isa.drm.preferred.method" xml setting
SETTING_DRM_METHOD_PROPS = 'props'
SETTING_DRM_METHOD_DRMLEGACY = 'drm-legacy'
SETTING_DRM_METHOD_DRM = 'drm'


def log(log_type, message):
log_level = xbmc.LOGDEBUG
if log_type == LOG_ERROR:
log_level = xbmc.LOGERROR
elif log_type == LOG_INFO:
log_level = xbmc.LOGINFO
elif log_type == LOG_DEBUG:
log_level = xbmc.LOGDEBUG
xbmc.log(f'[{ISAS_ADDON_NAME}] ' + message, log_level)


def get_value_from_path(path, json_data):
if not path:
return None
keys = path.split('/')
current_data = json_data
try:
for key in keys:
current_data = current_data[key]
except KeyError:
log(LOG_ERROR, f'The json path: "{path}" dont exists')
return None
return current_data


class CmpVersion:
"""Comparator for version numbers"""

def __init__(self, version):
self.__version = str(version or '')
self.__ver_list = (self.__version or '0').split('.')

def __str__(self):
return self.__version

def __repr__(self):
return self.__version

def __bool__(self):
"""
Allow "if" operator to check if there is a version set.
Will return False only when "version" set is an empty string or None.
"""
return bool(self.__version)

def __iter__(self):
"""Allow to get the version list by using "list" command builtin"""
return iter(self.__ver_list)

def __lt__(self, other):
"""Operator <"""
return operator.lt(*zip(*map(lambda x, y: (x or 0, y or 0),
map(int, self.__ver_list),
map(int, self.__conv_to_list(other)))))

def __le__(self, other):
"""Operator <="""
return operator.le(*zip(*map(lambda x, y: (x or 0, y or 0),
map(int, self.__ver_list),
map(int, self.__conv_to_list(other)))))

def __gt__(self, other):
"""Operator >"""
return operator.gt(*zip(*map(lambda x, y: (x or 0, y or 0),
map(int, self.__ver_list),
map(int, self.__conv_to_list(other)))))

def __ge__(self, other):
"""Operator >="""
return operator.ge(*zip(*map(lambda x, y: (x or 0, y or 0),
map(int, self.__ver_list),
map(int, self.__conv_to_list(other)))))

def __eq__(self, other):
"""Operator =="""
return operator.eq(*zip(*map(lambda x, y: (x or 0, y or 0),
map(int, self.__ver_list),
map(int, self.__conv_to_list(other)))))

def __ne__(self, other):
"""Operator !="""
return operator.ne(*zip(*map(lambda x, y: (x or 0, y or 0),
map(int, self.__ver_list),
map(int, self.__conv_to_list(other)))))

def __conv_to_list(self, value):
"""Convert a string or number or CmpVersion object to a list of strings"""
if isinstance(value, CmpVersion):
return list(value)
return str(value or '0').split('.')


class KodiVersion(CmpVersion):
"""Comparator for Kodi version numbers"""
# Examples of some types of supported strings:
# 10.1 Git:Unknown PRE-11.0 Git:Unknown 11.0-BETA1 Git:20111222-22ad8e4
# 18.1-RC1 Git:20190211-379f5f9903 19.0-ALPHA1 Git:20190419-c963b64487
def __init__(self):
import re
self.build_version = xbmc.getInfoLabel('System.BuildVersion')
# Parse the version number
result = re.search(r'\d+\.\d+', self.build_version)
self.version = result.group(0) if result else ''
super().__init__(self.version)
# Parse the date of GIT build
result = re.search(r'(Git:)(\d+?(?=(-|$)))', self.build_version)
self.date = int(result.group(2)) if result and len(result.groups()) >= 2 else None
# Parse the stage name
result = re.search(r'(\d+\.\d+-)(.+)(?=\s)', self.build_version)
if not result:
result = re.search(r'^(.+)(-\d+\.\d+)', self.build_version)
self.stage = result.group(1) if result else ''
else:
self.stage = result.group(2) if result else ''


def get_isa_version():
"""Return the InputStream Adaptive version in a CmpVersion object"""
try:
addon = xbmcaddon.Addon(ISA_ADDON_NAME)
except RuntimeError:
log(LOG_ERROR, f'Cannot get {ISA_ADDON_NAME} version')
return CmpVersion(0)
return CmpVersion(addon.getAddonInfo('version'))


def jsonrpc(*args, **kwargs):
"""Perform JSONRPC calls"""
from json import dumps, loads
if args and kwargs:
log(LOG_ERROR, 'Bad jsonrpc() method arguments')
return None
# Process a list of actions
if args:
for (idx, cmd) in enumerate(args):
if cmd.get('id') is None:
cmd.update(id=idx)
if cmd.get('jsonrpc') is None:
cmd.update(jsonrpc='2.0')
return loads(xbmc.executeJSONRPC(dumps(args)))
# Process a single action
if kwargs.get('id') is None:
kwargs.update(id=0)
if kwargs.get('jsonrpc') is None:
kwargs.update(jsonrpc='2.0')
return loads(xbmc.executeJSONRPC(dumps(kwargs)))


def check_isa_addon():
"""Returns whether InputStream Adaptive add-on is installed and enabled"""
data = jsonrpc(method='Addons.GetAddonDetails',
params={'addonid': ISA_ADDON_NAME, 'properties': ['enabled']})
if data.get('result', {}).get('addon', {}).get('enabled'):
return True
return False


def show_dialog_ok(message='', heading=''):
"""Show Kodi's OK dialog"""
from xbmcgui import Dialog
if not heading:
heading = xbmcaddon.Addon(ISAS_ADDON_NAME).getAddonInfo('name')
return Dialog().ok(heading=heading, message=message)


def show_dialog_text(message='', heading=''):
"""Show Kodi's Text viewer dialog"""
from xbmcgui import Dialog
if not heading:
heading = xbmcaddon.Addon(ISAS_ADDON_NAME).getAddonInfo('name')
return Dialog().textviewer(heading=heading, text=message)


def determines_mime_type(url):
"""
Determines mime type from a manifest URL
Returns mime type value
"""
url_lw = url.lower()
if url_lw.endswith('.m3u8'):
return 'application/vnd.apple.mpegurl'
if url_lw.endswith('.mpd'):
return 'application/dash+xml'
if (url_lw.endswith('.ism/manifest') or url_lw.endswith('.isml/manifest')
or url_lw.endswith('.isml') or url_lw.endswith('.ism')):
return 'application/vnd.ms-sstr+xml'
# Unknown, don't matter if wrong it's irrelevant to ISA
return 'application/dash+xml'


def get_menu_config():
"""Fetch menu settings, for a fast menu loading"""
cfg = {}
addon = xbmcaddon.Addon()
cfg[SETTING_SHOW_FEATURE_FLAGS] = addon.getSettingBool(SETTING_SHOW_FEATURE_FLAGS)
cfg[SETTING_SHOW_CRYPTO_FLAGS] = addon.getSettingBool(SETTING_SHOW_CRYPTO_FLAGS)
cfg[SETTING_SHOW_CODECS] = addon.getSettingBool(SETTING_SHOW_CODECS)
return cfg
Loading

0 comments on commit 2cfb688

Please sign in to comment.