diff --git a/plugin.video.isasamples/addon.py b/plugin.video.isasamples/addon.py new file mode 100644 index 000000000..daf6d9496 --- /dev/null +++ b/plugin.video.isasamples/addon.py @@ -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) diff --git a/plugin.video.isasamples/addon.xml b/plugin.video.isasamples/addon.xml new file mode 100644 index 000000000..19a7a5144 --- /dev/null +++ b/plugin.video.isasamples/addon.xml @@ -0,0 +1,22 @@ + + + + + + + video + + + all + InputStream Adaptive Samples + This add-on provides sample streams for testing InputStream Adaptive demuxer. DRM libraries are not provided and must be installed separately. + 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. + GPL-2.0-or-later + https://github.com/xbmc/inputstream.adaptive/wiki + https://github.com/xbmc/inputstream.adaptive + + resources/icon.png + + false + + diff --git a/plugin.video.isasamples/helper.py b/plugin.video.isasamples/helper.py new file mode 100644 index 000000000..b0ea73f3e --- /dev/null +++ b/plugin.video.isasamples/helper.py @@ -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 diff --git a/plugin.video.isasamples/main.py b/plugin.video.isasamples/main.py new file mode 100644 index 000000000..8f52c2895 --- /dev/null +++ b/plugin.video.isasamples/main.py @@ -0,0 +1,302 @@ +# -*- coding: utf-8 -*- +""" + Copyright (C) 2024 Team Kodi + SPDX-License-Identifier: GPL-2.0-or-later + See LICENSES/README.md for more information. +""" +import copy +import json +import urllib +from urllib.parse import parse_qsl + +import xbmcaddon +import xbmcgui +import xbmcplugin + +import menu_data as md +from helper import (log, LOG_DEBUG, LOG_ERROR, LOG_INFO, get_value_from_path, check_isa_addon, show_dialog_ok, + get_isa_version, determines_mime_type, ISA_ADDON_NAME, SETTING_DRM_METHOD_PROPS, + SETTING_DRM_METHOD_DRMLEGACY, SETTING_DRM_METHOD_DRM, KodiVersion, get_menu_config, + SETTING_SHOW_CRYPTO_FLAGS, + SETTING_SHOW_FEATURE_FLAGS, SETTING_SHOW_CODECS, ISAS_ADDON_NAME, show_dialog_text) + +PLUGIN_URL = '' +PLUGIN_HANDLE = 0 +ACTION_LISTING = 'listing' +ACTION_FILTER = 'filter' +ACTION_PLAY = 'play' +ACTION_SHOW_DIALOG = 'show_dialog' +EXTRADATA_ENC_FLAGS = 'extradata_encrypt_flags' +EXTRADATA_FEAT_FLAGS = 'extradata_feature_flags' + + +def make_li_url(action, path, wnd_title=None, extradata=''): + """Make the URL for a ListItem""" + if not wnd_title: + wnd_title = path.replace('/', ' / ') + if extradata: + extradata = '&extradata=' + urllib.parse.quote(extradata) + return '{0}?action={1}&path={2}&title={3}'.format(PLUGIN_URL, action, urllib.parse.quote(path), + urllib.parse.quote(wnd_title)) + extradata + + +def make_play_listitem(cfg): + """Make a ListItem configured to play a stream with InputStream Adaptive""" + addon = xbmcaddon.Addon() + isa_version = get_isa_version() + if not 'manifest_url' in cfg: + raise Exception('Missing "manifest_url" in the stream configuration') + manifest_url = cfg.pop('manifest_url') + + # On ListItem use "offscreen" to avoid locking GUI, since it will not be rendered on the screen + play_item = xbmcgui.ListItem(path=manifest_url, offscreen=True) + play_item.setProperty('isPlayable', 'true') + play_item.setProperty('inputstream', ISA_ADDON_NAME) + + # Following two lines are to avoid HTTP HEAD requests from Kodi core (cause problems to some services) + play_item.setMimeType(determines_mime_type(manifest_url)) + play_item.setContentLookup(False) + + # Try to find the preferred DRM property, if available + prefer_drm_method = addon.getSettingString('isa.drm.preferred.method') + selected_prop = None + is_drm_legacy_found = 'drm_legacy' in cfg + is_drm_found = 'drm' in cfg + is_old_props_found = 'license_type' in cfg or 'license_key' in cfg + + if prefer_drm_method == SETTING_DRM_METHOD_DRMLEGACY and is_drm_legacy_found: + selected_prop = SETTING_DRM_METHOD_DRMLEGACY + elif prefer_drm_method == SETTING_DRM_METHOD_DRM and is_drm_found and isa_version >= 22: + selected_prop = SETTING_DRM_METHOD_DRM + elif prefer_drm_method == SETTING_DRM_METHOD_PROPS and is_old_props_found: + selected_prop = SETTING_DRM_METHOD_PROPS + + if not selected_prop: # preferred not found, take the first available + if is_drm_found and isa_version >= 22: + selected_prop = SETTING_DRM_METHOD_DRM + elif is_drm_legacy_found: + selected_prop = SETTING_DRM_METHOD_DRMLEGACY + elif is_old_props_found: + selected_prop = SETTING_DRM_METHOD_PROPS + + if selected_prop: + # delete unused DRM configuration methods, using multiple DRM methods are not allowed on ISA add-on, + # we allow to set multiple methods on streams to test all use cases without having to edit the streams each time + if selected_prop != SETTING_DRM_METHOD_PROPS: + cfg.pop('license_type', None) + cfg.pop('license_key', None) + cfg.pop('license_data', None) + cfg.pop('license_flags', None) + cfg.pop('server_certificate', None) + if selected_prop != SETTING_DRM_METHOD_DRMLEGACY: + cfg.pop('drm_legacy', None) + if selected_prop != SETTING_DRM_METHOD_DRM: + cfg.pop('drm', None) + + # Set ISA properties + prop_prefix = f'{ISA_ADDON_NAME}.' + for prop_name, value in cfg.items(): + play_item.setProperty(prop_prefix + prop_name, value) + return play_item + + +def make_menu_listitem(path, item_name, title, data, menu_cfg): + # Create a ListItem for the new entry + list_item = xbmcgui.ListItem(title) + if md.MI_CONFIG in data: # Make a menu item + is_folder = True + action = ACTION_LISTING + elif md.SI_CONFIG in data: # Make an audio/video stream item + is_folder = False + action = ACTION_PLAY + if menu_cfg[SETTING_SHOW_FEATURE_FLAGS] and md.SI_FEATURE in data: + title += ' [COLOR blue][' + data[md.SI_FEATURE] + '][/COLOR]' + if menu_cfg[SETTING_SHOW_CRYPTO_FLAGS] and md.SI_ENCRYPT in data: + title += ' [COLOR red][' + data[md.SI_ENCRYPT] + '][/COLOR]' + if menu_cfg[SETTING_SHOW_CODECS] and md.SI_CODECS in data: + title += ' [' + data[md.SI_CODECS] + ']' + list_item.setProperty('IsPlayable', 'true') + list_item.setInfo('video', {'mediatype': 'movie', 'title': title}) + ctx_menus = [('InputStream Adaptive settings', f'Addon.OpenSettings({ISA_ADDON_NAME})'), + ('Add-on settings', f'Addon.OpenSettings({ISAS_ADDON_NAME})'), ] + if md.SI_CONFIG in data: + query = '{0}?action={1}&path={2}&title={3}&extradata={4}'.format(PLUGIN_URL, ACTION_SHOW_DIALOG, + urllib.parse.quote(path + '/' + item_name), + urllib.parse.quote(title), md.SI_CONFIG) + ctx_menus.append(('Show ISA properties', f'RunPlugin({query})')) + list_item.addContextMenuItems(ctx_menus) + # Construct stream info + info = '' + if md.SI_FEATURE in data: + text = '[COLOR blue]FEATURES:' + for feat in data.get(md.SI_FEATURE, '').split(','): + text += '[CR]' + md.STREAM_FEAT.get(feat, f'UNKNOWN FEATURE FLAG "{feat}"') + info += text + '[/COLOR][CR]' + if md.SI_ENCRYPT in data: + text = '[COLOR red]ENCRYPTIONS:' + for enc in data.get(md.SI_ENCRYPT, '').split(','): + text += '[CR]' + md.STREAM_ENC.get(enc, f'UNKNOWN ENCRYPT FLAG "{enc}"') + info += text + '[/COLOR][CR]' + if md.SI_CODECS in data: + info += 'CODECS: ' + str(data[md.SI_CODECS]) + '[CR]' + if md.SI_INFO in data: + info += '[COLOR green]INFO: ' + str(data[md.SI_INFO]) + '[/COLOR][CR]' + if info: + video_info = list_item.getVideoInfoTag() + video_info.setPlot(info) + else: + log(LOG_ERROR, f'Cannot determine the menu item type for "{item_name}" on path "{path}"') + return None + url_path = '/'.join([path, item_name]).strip('/') + return make_li_url(action, url_path), list_item, is_folder + + +def play_stream(path): + log(LOG_INFO, f'Playing stream: {path}') + try: + if not check_isa_addon(): + show_dialog_ok('Cannot play the stream, InputStream Adaptive is not installed or not enabled.') + raise Exception('InputStream Adaptive not available') + # retrieve the stream config from the specified json path + stream_data = get_value_from_path(path, md.menu_data) + cfg = stream_data.get(md.SI_CONFIG) + if not cfg: + log(LOG_ERROR, f'Missing "{md.SI_CONFIG}" dict on json stream dict') + xbmcplugin.endOfDirectory(handle=PLUGIN_HANDLE, succeeded=False) + raise Exception(f'Missing "{md.SI_CONFIG}" dict on json stream dict') + + xbmcplugin.setResolvedUrl(PLUGIN_HANDLE, True, listitem=make_play_listitem(copy.deepcopy(cfg))) + except Exception as exc: + log(LOG_ERROR, f'Something was wrong, exception: {exc}') + import traceback + log(LOG_ERROR, traceback.format_exc()) + xbmcplugin.endOfDirectory(handle=PLUGIN_HANDLE, succeeded=False) + + +def show_text(path, wnd_title, extradata): + log(LOG_DEBUG, f'Show text data from: {path}') + try: + stream_data = get_value_from_path(path, md.menu_data) + if extradata == md.SI_CONFIG: + show_dialog_text(json.dumps(stream_data.get(md.SI_CONFIG), indent=4), wnd_title) + else: + log(LOG_ERROR, f'Unhandled show_text extradata "{extradata}"') + except Exception as exc: + log(LOG_ERROR, f'Something was wrong, exception: {exc}') + import traceback + log(LOG_ERROR, traceback.format_exc()) + + +def load_menu(path='', wnd_title=''): + log(LOG_DEBUG, f'Loading menu "{path}"') + if not path: + menu_data = md.menu_data + else: + menu_data = get_value_from_path(path, md.menu_data) + if not menu_data: + log(LOG_ERROR, 'Unable to load the menu data') + xbmcplugin.endOfDirectory(PLUGIN_HANDLE, False) + return + # Load menu items + listing = [] + menu_cfg = get_menu_config() + for e_title, e_data in menu_data.items(): + if e_title == md.MI_CONFIG: # ignore it due to special handling + continue + list_item = make_menu_listitem(path, e_title, e_title, e_data, menu_cfg) + if list_item is None: + break + listing.append(list_item) + # Assume is main menu, add additional menus + if not path: + list_item = xbmcgui.ListItem(label='List DRM Widevine streams') + url = make_li_url(ACTION_FILTER, 'DRMWV', list_item.getLabel(), EXTRADATA_ENC_FLAGS) + listing.append((url, list_item, True)) + + list_item = xbmcgui.ListItem(label='List DRM PlayReady streams') + url = make_li_url(ACTION_FILTER, 'DRMPR', list_item.getLabel(), EXTRADATA_ENC_FLAGS) + listing.append((url, list_item, True)) + + list_item = xbmcgui.ListItem(label='List DRM ClearKey streams') + url = make_li_url(ACTION_FILTER, 'DRMCK', list_item.getLabel(), EXTRADATA_ENC_FLAGS) + listing.append((url, list_item, True)) + + list_item = xbmcgui.ListItem(label='List CBCS streams') + url = make_li_url(ACTION_FILTER, 'CBCS', list_item.getLabel(), EXTRADATA_ENC_FLAGS) + listing.append((url, list_item, True)) + + list_item = xbmcgui.ListItem(label='List streams with subtitles') + url = make_li_url(ACTION_FILTER, 'SUB,SUBEXT,SUBMP4', list_item.getLabel(), EXTRADATA_FEAT_FLAGS) + listing.append((url, list_item, True)) + + xbmcplugin.addDirectoryItems(PLUGIN_HANDLE, listing, len(listing)) + xbmcplugin.setPluginCategory(PLUGIN_HANDLE, wnd_title) # Set window title + xbmcplugin.addSortMethod(PLUGIN_HANDLE, xbmcplugin.SORT_METHOD_LABEL_IGNORE_FOLDERS) + xbmcplugin.endOfDirectory(PLUGIN_HANDLE, True, False, False) + log(LOG_DEBUG, f'Menu "{path}" loaded.') + + +def filter_search(path, wnd_title, extradata): + log(LOG_DEBUG, f'Loading filtered menu "{path}" with extradata "{extradata}"') + if not path: + log(LOG_ERROR, 'Unable to search an empty path') + xbmcplugin.endOfDirectory(PLUGIN_HANDLE, False) + return + listing = [] + menu_cfg = get_menu_config() + if extradata == EXTRADATA_ENC_FLAGS: + for e_full_path, e_parent, e_title, e_data in md.find_encrypt_entries(md.menu_data, path): + list_item = make_menu_listitem(e_full_path, e_title, f'[LIGHT][{e_parent}][/LIGHT] ' + e_title, e_data, + menu_cfg) + if list_item is None: + break + listing.append(list_item) + if extradata == EXTRADATA_FEAT_FLAGS: + feat_list = path.split(',') + for e_full_path, e_parent, e_title, e_data in md.find_feature_entries(md.menu_data, feat_list): + list_item = make_menu_listitem(e_full_path, e_title, f'[LIGHT][{e_parent}][/LIGHT] ' + e_title, e_data, + menu_cfg) + if list_item is None: + break + listing.append(list_item) + xbmcplugin.addDirectoryItems(PLUGIN_HANDLE, listing, len(listing)) + xbmcplugin.setPluginCategory(PLUGIN_HANDLE, wnd_title) # Set window title + xbmcplugin.addSortMethod(PLUGIN_HANDLE, xbmcplugin.SORT_METHOD_LABEL_IGNORE_FOLDERS) + xbmcplugin.endOfDirectory(PLUGIN_HANDLE, True, False, False) + log(LOG_DEBUG, f'Filtered menu "{path}" loaded.') + + +def router(argv): + # Update the global vars + global PLUGIN_URL + global PLUGIN_HANDLE + PLUGIN_URL = argv[0] + PLUGIN_HANDLE = int(argv[1]) + if KodiVersion() < 21: + show_dialog_ok(f'This add-on is not compatible with Kodi version {KodiVersion().version}.[CR]' + 'You must use Kodi v21 or above.') + return + # Parse the plugin parameters + params_string = argv[2] + params = dict(parse_qsl(params_string[1:])) + if not check_isa_addon(): + show_dialog_ok('This add-on require InputStream Adaptive installed and enabled.') + return + # Route the callback to execute the requested action + if params: + action = params.get('action') + path = urllib.parse.unquote(params.get('path', '')) + title = urllib.parse.unquote(params.get('title', '')) + extradata = urllib.parse.unquote(params.get('extradata', '')) + if action == ACTION_LISTING: # List items for the requested menu + load_menu(path, title) + elif action == ACTION_FILTER: # List items for the requested filtered search + filter_search(path, title, extradata) + elif action == ACTION_PLAY: # Kodi player callback to play a stream + play_stream(path) + elif action == ACTION_SHOW_DIALOG: # Show a window text dialog + show_text(path, title, extradata) + elif action is None: + log(LOG_ERROR, 'Missing "action" parameter to the ListItem path') + else: # Add-on called from Kodi UI without any parameters + load_menu(wnd_title='Main menu') diff --git a/plugin.video.isasamples/menu_data.py b/plugin.video.isasamples/menu_data.py new file mode 100644 index 000000000..6ace9c195 --- /dev/null +++ b/plugin.video.isasamples/menu_data.py @@ -0,0 +1,651 @@ +# -*- coding: utf-8 -*- +""" + Copyright (C) 2024 Team Kodi + SPDX-License-Identifier: GPL-2.0-or-later + See LICENSES/README.md for more information. +""" +# IMPORTANT: Don't use "/" char on menus and streams titles, because it's reserved for paths. +# Dict key to declare a directory menu item +MI_CONFIG = 'menu_config' +# Dict key to declare a playable stream item +SI_CONFIG = 'stream_config' +# SI_CONFIG how to use it: +# It must contains a dictionary to store all ISA properties as is, simply remove the prefix 'inputstream.adaptive.' +# from ISA property name to be added, the only exception is for the manifest url, +# it can be set by using 'manifest_url' key name. +# For DRM protected cases, SI_CONFIG can accept multiple DRM configuration methods at same time, +# this would not be allowed on ISA, but here we allow to set them all in order to test all use cases without modify +# every time the menu data, and so they will be filtered automatically on settings basis. + +# Optionals dict keys to add info in the stream item +SI_CODECS = 'codecs' # string split by ',' of codec names +SI_INFO = 'info' # text note +SI_FEATURE = 'feature_flags' # string split by ',' by using flags from STREAM_FEAT dict +SI_ENCRYPT = 'encrypt_flags' # string split by ',' by using flags from STREAM_ENC dict + +# SI_FEATURE flags for common stream features +STREAM_FEAT = { + 'ADP': 'Adaptive a/v streams', + 'ADPV': 'Adaptive video streams', + 'ADPA': 'Adaptive audio streams', + 'SUB': 'Text subtitles (segmented files)', + 'SUBEXT': 'Text subtitles (single external file)', + 'SUBMP4': 'MP4/sidecar subtitles (segmented files)', + 'AUD': 'Audio only', + 'AUDI': 'Audio included to video stream', + 'CMP4': 'Container: MP4', + 'CWEBM': 'Container: WEBM', +} +# SI_ENCRYPT flags for common stream encryption types +STREAM_ENC = { + 'DRMWV': 'Widevine', + 'DRMPR': 'PlayReady', + 'DRMWP': 'WisePlay', + 'DRMCK': 'ClearKey', + 'DRMFP': 'FairPlay', + 'CENC': 'CENC', + 'CBCS': 'CBCS', + 'AES': 'AES-128', + 'SAES': 'SAMPLE-AES', + 'KROT': 'Key rotation', +} + +menu_data = { + 'Manifest Dash': { + MI_CONFIG: {}, + 'Dash VOD': { + MI_CONFIG: {}, + 'Flowplayer night [segment base]': { + SI_FEATURE: 'ADPV', + SI_CODECS: 'avc1,mp4a', + SI_CONFIG: { + 'manifest_url': 'http://edge.flowplayer.org/night1.mpd', + } + }, + 'Akamaized bunny [multi-period]': { + SI_CONFIG: { + 'manifest_url': 'https://dash.akamaized.net/dash264/TestCases/5a/nomor/1.mpd', + } + }, + 'Axiom sintel [multi-codec]': { + SI_CONFIG: { + 'manifest_url': 'http://media.axprod.net/Temp/KVS/media/MPD/Clear/MultiCodec/sintel.mpd' + } + }, + 'Axiom v7 1080p [subtitles]': { + SI_FEATURE: 'ADP,CMP4,SUBMP4', + SI_CODECS: 'avc1,hev1,mp4a,wvtt,stpp', + SI_CONFIG: { + 'manifest_url': 'https://media.axprod.net/TestVectors/v7-Clear/Manifest_1080p.mpd', + } + }, + 'TravelXP [video]': { + SI_FEATURE: 'ADP,CMP4,CWEBM', + SI_CODECS: 'vp9,hvc1,mp4a', + SI_INFO: 'Two adaptive videos VP9 and HEVC', + SI_CONFIG: { + 'manifest_url': 'https://travelxp.s.llnwi.net/watch1/61025c11781ce3c543f5abcd/manifest_v4.mpd' + } + }, + 'Dashif testpic_2s [subtitles]': { + SI_FEATURE: 'SUBMP4', + SI_CODECS: 'avc1,mp4a,stpp', + SI_CONFIG: { + 'manifest_url': 'https://livesim.dashif.org/dash/vod/testpic_2s/multi_subs.mpd', + } + }, + 'Akamaized IT1 [subtitles]': { + SI_FEATURE: 'SUBEXT', + SI_CODECS: 'avc1,mp4a,ttml', + SI_CONFIG: { + 'manifest_url': 'https://dash.akamaized.net/dash264/CTA/imsc1/IT1-20171027_dash.mpd', + } + }, + 'Akamaized ED_OnDemand_5SecSeg [subtitles]': { + SI_FEATURE: 'ADP,CMP4,SUBEXT', + SI_CODECS: 'avc1,mp4a,ttml', + SI_INFO: 'Two subtitle tracks. Subs seem malformed, async', + SI_CONFIG: { + 'manifest_url': 'https://dash.akamaized.net/dash264/TestCases/4b/qualcomm/1/ED_OnDemand_5SecSeg_Subtitles.mpd', + } + }, + 'Akamaized bbb_30fps 4k': { + SI_FEATURE: 'ADP,CMP4', + SI_CONFIG: { + 'manifest_url': 'https://dash.akamaized.net/akamai/bbb_30fps/bbb_30fps.mpd', + } + }, + 'Dolby atmos': { + SI_CONFIG: { + 'manifest_url': 'https://ott.dolby.com/OnDelKits/DDP/Dolby_Digital_Plus_Online_Delivery_Kit_v1.4.1/Test_Signals/muxed_streams/DASH/OnDemand_MPD/ChID_voices_1280x720p_25fps_h264_6ch_640kbps_ddp_joc.mpd' + } + }, + 'Dolby atmos multi-bitrate': { + SI_FEATURE: 'ADP,CMP4', + SI_CODECS: 'hvc1,dvh1,ec-3', + SI_INFO: 'Two video tracks HEVC and DV', + SI_CONFIG: { + 'manifest_url': 'https://ott.dolby.com/OnDelKits/DDP/Dolby_Digital_Plus_Online_Delivery_Kit_v1.5/Test_Signals/example_streams/DASH/OnDemand/MPD/Holi_25fps_example_1_clean.mpd' + } + }, + 'Radiant 4k-av1-avc': { + SI_FEATURE: 'ADP,CMP4,CWEBM', + SI_CODECS: 'av01,avc1,opus', + SI_INFO: 'Two video tracks AV1 (WEBM) and AVC (MP4), audio OPUS (WEBM)', + SI_CONFIG: { + 'manifest_url': 'https://www.radiantmediaplayer.com/media/dash/4k-av1-avc/manifest.mpd', + 'manifest_headers': 'User-Agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:98.0) Gecko/20100101 Firefox/98.0&Host=www.radiantmediaplayer.com&Accept=text/html,application/xhtml+xml,application/xml&Upgrade-Insecure-Requests=1&Accept-Encoding=gzip,defalte,br&Connection=keep-alive', + 'stream_headers': 'User-Agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:98.0) Gecko/20100101 Firefox/98.0&Host=www.radiantmediaplayer.com&Accept=text/html,application/xhtml+xml,application/xml&Upgrade-Insecure-Requests=1&Accept-Encoding=gzip,defalte,br&Connection=keep-alive', + } + }, + 'Bitmovin "av01"': { + SI_FEATURE: 'ADPV,CWEBM', + SI_CODECS: 'av01,opus', + SI_INFO: 'All streams use WEBM', + SI_CONFIG: { + 'manifest_url': 'https://storage.googleapis.com/bitmovin-demos/av1/stream_chrome.mpd', + } + }, + 'Uni Beauty_4sec 4k [multi-codec]': { + SI_FEATURE: 'ADP', + SI_INFO: 'VP9 (WEBM), HEVC (MP4), H264 (MP4), AV1 (WEBM), No audio', + SI_CONFIG: { + 'manifest_url': 'https://ftp.itec.aau.at/datasets/mmsys18/testing/Beauty_4sec/multi-codec.mpd', + } + }, + 'Bitmovin "av1"': { + SI_FEATURE: 'ADPV,CWEBM', + SI_CODECS: 'av1,opus', + SI_INFO: 'All streams use WEBM', + SI_CONFIG: { + 'manifest_url': 'https://storage.googleapis.com/bitmovin-demos/av1/stream.mpd', + } + }, + 'Akamaized spring 4k [segment base]': { + SI_CONFIG: { + 'manifest_url': 'https://dash.akamaized.net/akamai/streamroot/050714/Spring_4Ktest.mpd' + } + }, + 'Akamaized LastSegmentNumber': { + SI_INFO: 'To test last segment signal on MP4 box', + SI_CONFIG: { + 'manifest_url': 'http://dash.akamaized.net/dash264/TestCasesIOP41/LastSegmentNumber/1/manifest_last_segment_num.mpd' + } + }, + 'Dashif audio only': { + SI_CONFIG: { + 'manifest_url': 'https://livesim.dashif.org/dash/vod/testpic_2s/audio.mpd' + } + }, + 'YouTube [SegmentList + SegmentTimeline]': { + SI_FEATURE: 'ADPA', + SI_INFO: 'VP9 (WEBM), MP4A (MP4)', + SI_CONFIG: { + 'manifest_url': 'http://www.youtube.com/api/manifest/dash/id/bf5bb2419360daf1/source/youtube?as=fmp4_audio_clear,webm2_sd_hd_clear&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0&ipbits=0&expire=19000000000&signature=249B04F79E984D7F86B4D8DB48AE6FAF41C17AB3.7B9F0EC0505E1566E59B8E488E9419F253DDF413&key=ik0', + } + }, + 'Edgesuite ElephantsDream [subtitles] ': { + SI_FEATURE: 'SUBEXT', + SI_CODECS: 'avc1,mp4a,vtt', + SI_CONFIG: { + 'manifest_url': 'http://dash.edgesuite.net/akamai/test/caption_test/ElephantsDream/elephants_dream_480p_heaac5_1.mpd', + } + }, + 'Exoplayer captions2 [subtitles]': { + SI_FEATURE: 'SUBMP4', + SI_CODECS: 'avc1,mp4a,wvtt', + SI_INFO: 'Three subtitles tracks', + SI_CONFIG: { + 'manifest_url': 'http://media.axprod.net/ExoPlayer/Captions2/Manifest.mpd', + } + }, + 'Dashif testpic_2s CEA-608 caption tracks (eng-swe)': { + SI_CONFIG: { + 'manifest_url': 'https://livesim.dashif.org/dash/vod/testpic_2s/cea608.mpd', + } + }, + }, + 'Dash Live': { + MI_CONFIG: {}, + 'Dashif segtimeline_1 [SegmentTemplate + SegmentTimeline]': { + SI_CONFIG: { + 'manifest_url': 'https://livesim2.dashif.org/livesim2/segtimeline_1/testpic_2s/Manifest.mpd' + } + }, + 'Dashif periods_20 multi-period [SegmentTemplate - no timeline]': { + SI_INFO: 'A new period every 3 min (20 times/hour)', + SI_CONFIG: { + 'manifest_url': 'https://livesim.dashif.org/livesim/periods_20/testpic_2s/Manifest.mpd' + } + }, + 'Dashif ato_10 [SegmentTemplate - no timeline]': { + SI_CONFIG: { + 'manifest_url': 'https://livesim2.dashif.org/livesim2/ato_10/testpic_2s/Manifest.mpd' + } + }, + }, + 'Dash VOD with DRM': { + MI_CONFIG: {}, + 'Google Tears [CBCS]': { + SI_ENCRYPT: 'CBCS,DRMWV', + SI_CONFIG: { + 'manifest_url': 'https://storage.googleapis.com/wvmedia/cbcs/h264/tears/tears_aes_cbcs.mpd', + 'license_type': 'com.widevine.alpha', + 'license_key': 'https://proxy.uat.widevine.com/proxy?provider=widevine_test||R{SSM}|R', + 'drm_legacy': 'com.widevine.alpha|https://proxy.uat.widevine.com/proxy?provider=widevine_test', + 'drm': '{"com.widevine.alpha": {"license": {"server_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"}}}' + } + }, + 'Axiom 1080p [CBCS]': { + SI_ENCRYPT: 'CBCS,DRMWV,DRMPR', + SI_INFO: 'DRM config set to widevine', + SI_CONFIG: { + 'manifest_url': 'https://media.axprod.net/TestVectors/v9-MultiFormat/Encrypted_Cbcs/Manifest_1080p.mpd', + 'license_type': 'com.widevine.alpha', + 'license_key': 'https://drm-widevine-licensing.axtest.net/AcquireLicense|X-AxDRM-Message=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiNjllNTQwODgtZTllMC00NTMwLThjMWEtMWViNmRjZDBkMTRlIiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsInZlcnNpb24iOjIsImxpY2Vuc2UiOnsiYWxsb3dfcGVyc2lzdGVuY2UiOnRydWV9LCJjb250ZW50X2tleXNfc291cmNlIjp7ImlubGluZSI6W3siaWQiOiJmOGM4MGMyNS02OTBmLTQ3MzYtODEzMi00MzBlNWM2OTk0Y2UiLCJlbmNyeXB0ZWRfa2V5IjoiaVhxNDlaODlzOGRDajBqbTJBN1h6UT09IiwidXNhZ2VfcG9saWN5IjoiUG9saWN5IEEifV19LCJjb250ZW50X2tleV91c2FnZV9wb2xpY2llcyI6W3sibmFtZSI6IlBvbGljeSBBIiwicGxheXJlYWR5Ijp7Im1pbl9kZXZpY2Vfc2VjdXJpdHlfbGV2ZWwiOjE1MCwicGxheV9lbmFibGVycyI6WyI3ODY2MjdEOC1DMkE2LTQ0QkUtOEY4OC0wOEFFMjU1QjAxQTciXX19XX19.k9OlwW0rUwuf5d5Eb0iO98AFR3qp7qKdFzSbg2PQj78|R{SSM}|', + 'drm_legacy': 'com.widevine.alpha|https://drm-widevine-licensing.axtest.net/AcquireLicense|X-AxDRM-Message=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiNjllNTQwODgtZTllMC00NTMwLThjMWEtMWViNmRjZDBkMTRlIiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsInZlcnNpb24iOjIsImxpY2Vuc2UiOnsiYWxsb3dfcGVyc2lzdGVuY2UiOnRydWV9LCJjb250ZW50X2tleXNfc291cmNlIjp7ImlubGluZSI6W3siaWQiOiJmOGM4MGMyNS02OTBmLTQ3MzYtODEzMi00MzBlNWM2OTk0Y2UiLCJlbmNyeXB0ZWRfa2V5IjoiaVhxNDlaODlzOGRDajBqbTJBN1h6UT09IiwidXNhZ2VfcG9saWN5IjoiUG9saWN5IEEifV19LCJjb250ZW50X2tleV91c2FnZV9wb2xpY2llcyI6W3sibmFtZSI6IlBvbGljeSBBIiwicGxheXJlYWR5Ijp7Im1pbl9kZXZpY2Vfc2VjdXJpdHlfbGV2ZWwiOjE1MCwicGxheV9lbmFibGVycyI6WyI3ODY2MjdEOC1DMkE2LTQ0QkUtOEY4OC0wOEFFMjU1QjAxQTciXX19XX19.k9OlwW0rUwuf5d5Eb0iO98AFR3qp7qKdFzSbg2PQj78', + 'drm': '{"com.widevine.alpha": {"license": {"server_url": "https://drm-widevine-licensing.axtest.net/AcquireLicense|X-AxDRM-Message=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiNjllNTQwODgtZTllMC00NTMwLThjMWEtMWViNmRjZDBkMTRlIiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsInZlcnNpb24iOjIsImxpY2Vuc2UiOnsiYWxsb3dfcGVyc2lzdGVuY2UiOnRydWV9LCJjb250ZW50X2tleXNfc291cmNlIjp7ImlubGluZSI6W3siaWQiOiJmOGM4MGMyNS02OTBmLTQ3MzYtODEzMi00MzBlNWM2OTk0Y2UiLCJlbmNyeXB0ZWRfa2V5IjoiaVhxNDlaODlzOGRDajBqbTJBN1h6UT09IiwidXNhZ2VfcG9saWN5IjoiUG9saWN5IEEifV19LCJjb250ZW50X2tleV91c2FnZV9wb2xpY2llcyI6W3sibmFtZSI6IlBvbGljeSBBIiwicGxheXJlYWR5Ijp7Im1pbl9kZXZpY2Vfc2VjdXJpdHlfbGV2ZWwiOjE1MCwicGxheV9lbmFibGVycyI6WyI3ODY2MjdEOC1DMkE2LTQ0QkUtOEY4OC0wOEFFMjU1QjAxQTciXX19XX19.k9OlwW0rUwuf5d5Eb0iO98AFR3qp7qKdFzSbg2PQj78"}}}' + } + }, + 'Google tears [default_KID on DRM CP]': { + SI_INFO: 'The default_KID is set on DRM ContentProtection tag', + SI_ENCRYPT: 'DRMWV', + SI_CONFIG: { + 'manifest_url': 'https://storage.googleapis.com/wvmedia/cenc/vp9/tears/tears.mpd', + 'license_type': 'com.widevine.alpha', + 'license_key': 'https://proxy.uat.widevine.com/proxy?video_id=2015_tears&provider=widevine_test||R{SSM}|R', + 'drm_legacy': 'com.widevine.alpha|https://proxy.uat.widevine.com/proxy?video_id=2015_tears&provider=widevine_test', + 'drm': '{"com.widevine.alpha": {"license": {"server_url": "https://proxy.uat.widevine.com/proxy?video_id=2015_tears&provider=widevine_test"}}}' + } + }, + 'Google tears [default_KID on common CP]': { + SI_INFO: 'The default_KID is set on Common MP4 ContentProtection tag', + SI_ENCRYPT: 'DRMWV', + SI_CONFIG: { + 'manifest_url': 'https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears.mpd', + 'license_type': 'com.widevine.alpha', + 'license_key': 'https://proxy.uat.widevine.com/proxy?video_id=2015_tears&provider=widevine_test||R{SSM}|R', + 'drm_legacy': 'com.widevine.alpha|https://proxy.uat.widevine.com/proxy?video_id=2015_tears&provider=widevine_test', + 'drm': '{"com.widevine.alpha": {"license": {"server_url": "https://proxy.uat.widevine.com/proxy?video_id=2015_tears&provider=widevine_test"}}}' + } + }, + 'Google angel one': { + SI_ENCRYPT: 'DRMWV', + SI_INFO: 'Multiple audio and subtitles tracks', + SI_CONFIG: { + 'manifest_url': 'https://storage.googleapis.com/shaka-demo-assets/angel-one-widevine/dash.mpd', + 'license_type': 'com.widevine.alpha', + 'license_key': 'https://cwip-shaka-proxy.appspot.com/no_auth', + 'drm_legacy': 'com.widevine.alpha|https://cwip-shaka-proxy.appspot.com/no_auth', + 'drm': '{"com.widevine.alpha": {"license": {"server_url": "https://cwip-shaka-proxy.appspot.com/no_auth"}}}' + } + }, + 'Axiom multiDRM [multi-drm, key rotation]': { + SI_ENCRYPT: 'DRMWV,DRMPR,KROT', + SI_INFO: 'Config set to Widevine', + SI_CONFIG: { + 'manifest_url': 'https://media.axprod.net/TestVectors/v6.1-MultiDRM/Manifest_1080p.mpd', + 'license_type': 'com.widevine.alpha', + 'license_key': 'https://drm-widevine-licensing.axtest.net/AcquireLicense|X-AxDRM-Message=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiNjllNTQwODgtZTllMC00NTMwLThjMWEtMWViNmRjZDBkMTRlIiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImtleXMiOlt7ImlkIjoiNmU1YTFkMjYtMjc1Ny00N2Q3LTgwNDYtZWFhNWQxZDM0YjVhIn1dfX0.yF7PflOPv9qHnu3ZWJNZ12jgkqTabmwXbDWk_47tLNE|R{SSM}|', + 'drm_legacy': 'com.widevine.alpha|https://drm-widevine-licensing.axtest.net/AcquireLicense|X-AxDRM-Message=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiNjllNTQwODgtZTllMC00NTMwLThjMWEtMWViNmRjZDBkMTRlIiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImtleXMiOlt7ImlkIjoiNmU1YTFkMjYtMjc1Ny00N2Q3LTgwNDYtZWFhNWQxZDM0YjVhIn1dfX0.yF7PflOPv9qHnu3ZWJNZ12jgkqTabmwXbDWk_47tLNE', + 'drm': '{"com.widevine.alpha": {"license": {"server_url": "https://drm-widevine-licensing.axtest.net/AcquireLicense", "req_headers": "X-AxDRM-Message=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiNjllNTQwODgtZTllMC00NTMwLThjMWEtMWViNmRjZDBkMTRlIiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImtleXMiOlt7ImlkIjoiNmU1YTFkMjYtMjc1Ny00N2Q3LTgwNDYtZWFhNWQxZDM0YjVhIn1dfX0.yF7PflOPv9qHnu3ZWJNZ12jgkqTabmwXbDWk_47tLNE"}}}' + } + }, + 'Axiom multiDRM [multi-drm] WV': { + SI_ENCRYPT: 'DRMWV,DRMPR', + SI_INFO: 'Config set to Widevine', + SI_CONFIG: { + 'manifest_url': 'https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest_1080p.mpd', + 'license_type': 'com.widevine.alpha', + 'license_key': 'https://drm-widevine-licensing.axtest.net/AcquireLicense|X-AxDRM-Message=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiYjMzNjRlYjUtNTFmNi00YWUzLThjOTgtMzNjZWQ1ZTMxYzc4IiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImZpcnN0X3BsYXlfZXhwaXJhdGlvbiI6NjAsInBsYXlyZWFkeSI6eyJyZWFsX3RpbWVfZXhwaXJhdGlvbiI6dHJ1ZX0sImtleXMiOlt7ImlkIjoiOWViNDA1MGQtZTQ0Yi00ODAyLTkzMmUtMjdkNzUwODNlMjY2IiwiZW5jcnlwdGVkX2tleSI6ImxLM09qSExZVzI0Y3Iya3RSNzRmbnc9PSJ9XX19.FAbIiPxX8BHi9RwfzD7Yn-wugU19ghrkBFKsaCPrZmU|R{SSM}|', + 'drm_legacy': 'com.widevine.alpha|https://drm-widevine-licensing.axtest.net/AcquireLicense|X-AxDRM-Message=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiYjMzNjRlYjUtNTFmNi00YWUzLThjOTgtMzNjZWQ1ZTMxYzc4IiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImZpcnN0X3BsYXlfZXhwaXJhdGlvbiI6NjAsInBsYXlyZWFkeSI6eyJyZWFsX3RpbWVfZXhwaXJhdGlvbiI6dHJ1ZX0sImtleXMiOlt7ImlkIjoiOWViNDA1MGQtZTQ0Yi00ODAyLTkzMmUtMjdkNzUwODNlMjY2IiwiZW5jcnlwdGVkX2tleSI6ImxLM09qSExZVzI0Y3Iya3RSNzRmbnc9PSJ9XX19.FAbIiPxX8BHi9RwfzD7Yn-wugU19ghrkBFKsaCPrZmU', + 'drm': '{"com.widevine.alpha": {"license": {"server_url": "https://drm-widevine-licensing.axtest.net/AcquireLicense", "req_headers": "X-AxDRM-Message=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiYjMzNjRlYjUtNTFmNi00YWUzLThjOTgtMzNjZWQ1ZTMxYzc4IiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImZpcnN0X3BsYXlfZXhwaXJhdGlvbiI6NjAsInBsYXlyZWFkeSI6eyJyZWFsX3RpbWVfZXhwaXJhdGlvbiI6dHJ1ZX0sImtleXMiOlt7ImlkIjoiOWViNDA1MGQtZTQ0Yi00ODAyLTkzMmUtMjdkNzUwODNlMjY2IiwiZW5jcnlwdGVkX2tleSI6ImxLM09qSExZVzI0Y3Iya3RSNzRmbnc9PSJ9XX19.FAbIiPxX8BHi9RwfzD7Yn-wugU19ghrkBFKsaCPrZmU"}}}' + } + }, + 'Axiom multiDRM [multi-drm] PR': { + SI_ENCRYPT: 'DRMWV,DRMPR', + SI_INFO: 'Config set to PlayReady', + 'stream_config': { + 'manifest_url': 'https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest.mpd', + 'drm_legacy': 'com.microsoft.playready|https://drm-playready-licensing.axtest.net/AcquireLicense|X-AxDRM-Message=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiYjMzNjRlYjUtNTFmNi00YWUzLThjOTgtMzNjZWQ1ZTMxYzc4IiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsInZlcnNpb24iOjIsImxpY2Vuc2UiOnsiYWxsb3dfcGVyc2lzdGVuY2UiOnRydWV9LCJjb250ZW50X2tleXNfc291cmNlIjp7ImlubGluZSI6W3siaWQiOiI4MDM5OWJmNS04YTIxLTQwMTQtODA1My1lMjdlNzQ4ZTk4YzAiLCJlbmNyeXB0ZWRfa2V5IjoibGlOSnFWYVlrTmgrTUtjeEpGazdJZz09IiwidXNhZ2VfcG9saWN5IjoiUG9saWN5IEEifSx7ImlkIjoiOTA5NTNlMDktNmNiMi00OWEzLWEyNjAtN2E1ZmVmZWFkNDk5IiwiZW5jcnlwdGVkX2tleSI6ImtZdEhIdnJyZkNNZVZkSjZMa2Jrbmc9PSIsInVzYWdlX3BvbGljeSI6IlBvbGljeSBBIn0seyJpZCI6IjBlNGRhOTJiLWQwZTgtNGE2Ni04YzNmLWMyNWE5N2ViNjUzMiIsImVuY3J5cHRlZF9rZXkiOiI3dzdOWkhITE1nSjRtUUtFSzVMVE1RPT0iLCJ1c2FnZV9wb2xpY3kiOiJQb2xpY3kgQSJ9LHsiaWQiOiI1ODVmMjMzZi0zMDcyLTQ2ZjEtOWZhNC02ZGMyMmM2NmEwMTQiLCJlbmNyeXB0ZWRfa2V5IjoiQWM0VVVtWXRCSjVuUFE5TjE1cmMzZz09IiwidXNhZ2VfcG9saWN5IjoiUG9saWN5IEEifSx7ImlkIjoiNDIyMmJkNzgtYmM0NS00MWJmLWI2M2UtNmY4MTRkYzM5MWRmIiwiZW5jcnlwdGVkX2tleSI6Ik82Rk8wZnFTV29wcDdiamMvRDRsTUE9PSIsInVzYWdlX3BvbGljeSI6IlBvbGljeSBBIn1dfSwiY29udGVudF9rZXlfdXNhZ2VfcG9saWNpZXMiOlt7Im5hbWUiOiJQb2xpY3kgQSIsInBsYXlyZWFkeSI6eyJtaW5fZGV2aWNlX3NlY3VyaXR5X2xldmVsIjoxNTAsInBsYXlfZW5hYmxlcnMiOlsiNzg2NjI3RDgtQzJBNi00NEJFLThGODgtMDhBRTI1NUIwMUE3Il19fV19fQ.nMT-_Xor0ROh9sSAbDGu5nsMjrsCJ1W7FZGcr2xpqBY', + 'drm': '{"com.microsoft.playready": {"license": {"server_url": "https://drm-playready-licensing.axtest.net/AcquireLicense", "req_headers": "X-AxDRM-Message=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiYjMzNjRlYjUtNTFmNi00YWUzLThjOTgtMzNjZWQ1ZTMxYzc4IiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsInZlcnNpb24iOjIsImxpY2Vuc2UiOnsiYWxsb3dfcGVyc2lzdGVuY2UiOnRydWV9LCJjb250ZW50X2tleXNfc291cmNlIjp7ImlubGluZSI6W3siaWQiOiI4MDM5OWJmNS04YTIxLTQwMTQtODA1My1lMjdlNzQ4ZTk4YzAiLCJlbmNyeXB0ZWRfa2V5IjoibGlOSnFWYVlrTmgrTUtjeEpGazdJZz09IiwidXNhZ2VfcG9saWN5IjoiUG9saWN5IEEifSx7ImlkIjoiOTA5NTNlMDktNmNiMi00OWEzLWEyNjAtN2E1ZmVmZWFkNDk5IiwiZW5jcnlwdGVkX2tleSI6ImtZdEhIdnJyZkNNZVZkSjZMa2Jrbmc9PSIsInVzYWdlX3BvbGljeSI6IlBvbGljeSBBIn0seyJpZCI6IjBlNGRhOTJiLWQwZTgtNGE2Ni04YzNmLWMyNWE5N2ViNjUzMiIsImVuY3J5cHRlZF9rZXkiOiI3dzdOWkhITE1nSjRtUUtFSzVMVE1RPT0iLCJ1c2FnZV9wb2xpY3kiOiJQb2xpY3kgQSJ9LHsiaWQiOiI1ODVmMjMzZi0zMDcyLTQ2ZjEtOWZhNC02ZGMyMmM2NmEwMTQiLCJlbmNyeXB0ZWRfa2V5IjoiQWM0VVVtWXRCSjVuUFE5TjE1cmMzZz09IiwidXNhZ2VfcG9saWN5IjoiUG9saWN5IEEifSx7ImlkIjoiNDIyMmJkNzgtYmM0NS00MWJmLWI2M2UtNmY4MTRkYzM5MWRmIiwiZW5jcnlwdGVkX2tleSI6Ik82Rk8wZnFTV29wcDdiamMvRDRsTUE9PSIsInVzYWdlX3BvbGljeSI6IlBvbGljeSBBIn1dfSwiY29udGVudF9rZXlfdXNhZ2VfcG9saWNpZXMiOlt7Im5hbWUiOiJQb2xpY3kgQSIsInBsYXlyZWFkeSI6eyJtaW5fZGV2aWNlX3NlY3VyaXR5X2xldmVsIjoxNTAsInBsYXlfZW5hYmxlcnMiOlsiNzg2NjI3RDgtQzJBNi00NEJFLThGODgtMDhBRTI1NUIwMUE3Il19fV19fQ.nMT-_Xor0ROh9sSAbDGu5nsMjrsCJ1W7FZGcr2xpqBY"}}}' + } + }, + 'Bitmovin art of motion parkour': { + SI_ENCRYPT: 'DRMWV', + SI_CONFIG: { + 'manifest_url': 'https://bitmovin-a.akamaihd.net/content/art-of-motion_drm/mpds/11331.mpd', + 'license_type': 'com.widevine.alpha', + 'license_key': 'https://cwip-shaka-proxy.appspot.com/no_auth|R{SSM}|R', + 'drm_legacy': 'com.widevine.alpha|https://cwip-shaka-proxy.appspot.com/no_auth', + 'drm': '{"com.widevine.alpha": {"license": {"server_url": "https://cwip-shaka-proxy.appspot.com/no_auth"}}}' + } + }, + 'Google sintel [key rotation]': { + SI_ENCRYPT: 'DRMWV,KROT', + SI_INFO: 'Key rotation every 3 minutes', + SI_CONFIG: { + 'manifest_url': 'https://storage.googleapis.com/playerinfra/wv/sintel_3min_rotate.mpd', + 'license_type': 'com.widevine.alpha', + 'license_key': 'https://widevine-proxy.appspot.com/proxy', + 'drm_legacy': 'com.widevine.alpha|https://widevine-proxy.appspot.com/proxy', + 'drm': '{"com.widevine.alpha": {"license": {"server_url": "https://widevine-proxy.appspot.com/proxy"}}}' + } + }, + 'Microsoft appleset [CBCS and CENC]': { + SI_ENCRYPT: 'CBCS,DRMPR', + SI_INFO: 'Video use CBCS, Audio use CENC', + 'stream_config': { + 'manifest_url': 'https://test.playready.microsoft.com/media/dash/APPLEENC_CBCS_BBB_1080p/1080p.mpd', + 'license_type': 'com.microsoft.playready', + 'license_key': 'https://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,ck:W31bfVt9W31bfVt9W31bfQ==,ckt:aescbc)', + 'drm_legacy': 'com.microsoft.playready|https://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,ck:W31bfVt9W31bfVt9W31bfQ==,ckt:aescbc)', + 'drm': '{"com.microsoft.playready": {"license": {"server_url": "https://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,ck:W31bfVt9W31bfVt9W31bfQ==,ckt:aescbc)"}}}' + } + }, + 'Axiom v7multiDRM [clear key, license url embedded]': { + SI_ENCRYPT: 'DRMCK', + SI_INFO: 'License url embedded on ContentProtection Laurl tag. Expired ssl certificate on license url, disabled SSL verify peer.', + SI_CONFIG: { + 'manifest_url': 'https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest_1080p_ClearKey.mpd', + 'drm_legacy': 'org.w3.clearkey', + 'drm': '{"org.w3.clearkey": {}}', + 'config': '{"ssl_verify_peer":false}' + } + }, + 'Google angel one [clear key, keys on property]': { + SI_ENCRYPT: 'DRMCK', + SI_CONFIG: { + 'manifest_url': 'https://storage.googleapis.com/shaka-demo-assets/angel-one-clearkey/dash.mpd', + 'drm_legacy': 'org.w3.clearkey|feedf00deedeadbeeff0baadf00dd00d:00112233445566778899aabbccddeeff,1234f00deedeadbeeff0baadf00dd00d:8899aabbccddeeff8899aabbccddeeff', + 'drm': '{"org.w3.clearkey": {"license": {"keyids": {"feedf00deedeadbeeff0baadf00dd00d": "00112233445566778899aabbccddeeff", "1234f00deedeadbeeff0baadf00dd00d": "8899aabbccddeeff8899aabbccddeeff"}}}}', + } + }, + 'Bitmovin art of motion [widevine to clear key, keys on property]': { + SI_ENCRYPT: 'DRMCK', + SI_INFO: 'Override widevine content protection to use clear key', + SI_CONFIG: { + 'manifest_url': 'https://cdn.bitmovin.com/content/assets/art-of-motion_drm/mpds/11331.mpd', + 'drm_legacy': 'org.w3.clearkey|eb676abbcb345e96bbcf616630f1a3da:100b6c20940f779a4589152b57d2dacb', + 'drm': '{"org.w3.clearkey": {"license": {"keyids": {"eb676abbcb345e96bbcf616630f1a3da": "100b6c20940f779a4589152b57d2dacb"}}}}', + } + }, + 'TEST WV': { + SI_ENCRYPT: 'DRMWV,DRMPR', + SI_INFO: 'Config set to Widevine', + SI_CONFIG: { + 'manifest_url': 'https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest_1080p.mpd', + 'drm': '{"com.widevine.alpha":{"license": {"server_url": "https://drm-widevine-licensing.axtest.net/AcquireLicense","req_data": "eyJ0b2tlbiI6ICJwcmNiNzgyY2Y0NmI0MDM1YmU4Y2Y4ZGMyOTFmNmU0ODQ3MTkyZTViYjk1OGMwMDI5MjJmMTNlNmY1NThhZDM1NTk3ODViYWNmYjZjNjQxMjUyNTk2YTBlNTRlZWFhN2MwNWU4MGFjODI4MDJhMTNlZDk1NmVmMjY1MzIwZTA1NTQyMjA3NjA1MDkyNzk2YjY0NWIyYWMyMjAxMzVmM2MwNzQ2NzZlZjA0YmVjOTcyZTJmZmI3MWIyMGU5M2VjNWY4OGZhNDI3ZDA0ZDViYTU3OTU5ZGZiNGY1OWFkZDhiMGU1N2VmZDZlODBjYTk0ZGFmODZhMDM1OWYxNTY5NzE3MDViNTZmNWE4N2FlODQ4YjE1Y2I5NGY1YTRkNmU1ODA2NmYyYTcxNDk3OTg5ZTEwNWE4YjgzNjE2NzlhNTcyMjQwNmU3YjczMTk2N2JiNzNmZjAzMTBiZWJhNzU3NzA0ZTJlYWI3YmRlZmM2YmU1OGJhMzNjYWEyNzQ3ZDY1NWExNjIzYWRmMTUzMDNiOWI2NWJjZThmYzg0MGY1MzdiMzc5YmI4MzhlNWM5YjRmYzg1NWE3YWFlMzFmZjFlZjJkZTlhODE5N2RhM2YyNTQ1ODBhMDZkYmVlNTk1MDI3MWEzOTVlOGFiNWI5MGM4ZWYxNGY5ZGM2NGViODE0NzM3ZTczYmVkNzc2MTg5YmJmOTJlMzY5NjcyNTVlNzc1YmNhNWU1YmZmY2M1MTkzYjFmOTNmZDNiMGFmODc0NzE0MjE1MDA2MjY1OWNjZjViOWRkYzQ0NzM2MjQwNjlkZTA5ZjkyMjE4YjZiYTY1MGEyZjc4ZDA5MzNmODc1ODhmZjI0YWQwZTVmYWU4MjNlYjNjYzUyMjMwZTIyNWQ1YzliMDU5MzQ0MzkyNzJhNzYyMjE5NzAzY2Q2ZWQ0ZTI5OTRhN2Y4ZmZiMjJhNjI5MGJkYzdlYzQ1ZDU4MTAyNzk0NmU4M2EwMDg2ODI4MjY0NTdlY2YyMTRiNmQ1M2I3MTFmMTYyNzQ1ZDBmYjdjNzMyMTJiMzQxYTAzYjU4ZjQ5NGM0ODkzMTE1N2NmNDFkMzZhYWNiMTNhYmRiOTAwZjgzZGI4ZGJjMzNlMjQ5OWE3ZDhkZGE4YWFmZDg5ZGQ5NjI4Njg4YzFlOGQxYWE0ZTRhMjEyNDdkYmQ5MTE4MDE1ODExNzAwY2I0OTI5NDY2MjBkM2U0NzE0N2NjOTE5ZjNlYmVmY2E3NDk5YmFjNWYzMDM3NDU0NDk3NmI2ZTBkYWVhNThjNzllMmExMWE1YzNmMjdlOWM0ZjY2OWVjOWUzYTcxOWI3NjA4MDZiYzQ0NGE4NjYwZWU2N2U1MDhlMzY4OWEyMGJiNDNmYjdlZGUxNjY2NzFjZjQ0MWVlZGNkMDBhYWViYTFkNjIyYzJkNjJkOGY3YWI3YWZmZGFkNDNiMDkwM2I5NGNkY2NiNTUwMGEzN2E3MGIwNDJhMTg1Y2Q2Y2EwNTRjMWM4NTY0ZTM5YjMxMGVkYmI0ODE1YTRmMjc0NzljYzk3YzI4NjEwYTQyYjkwZjdkMGM5NGZkNDI0ZjkyOTBiZWU1YWIzMmI0ZmVmNTI4NTc5N2Q4ZWYwZDBjNTZlZTIwYjM0YWMyOGQxZjQyMjFlM2NlZjRmZDlmNjk4ZmFkNGU3MmVhMDNmMmJjM2RkNGYxMGEyMTBlZjQ1NDFiOTQ2ZTE4M2YxZDI0ZTZlY2JiOGFjMGZmYWQ0M2QxZTIxZGNlM2ExZWI2NmQ0NjFkMGExMzAyYWM1M2I3ZGM5NDcwMzhlNGY5ZWM1ZWNmNTQzMTMyODY2Nzg1NDI3NzNiYTZjYzUwNmQwODZhZDA1NDZiYWJlYmJjYmRjZWFiZGI2MWE4Mjk3YzU1NWQwZmNkZGYwZTY4ZjE4ZGU0YWZkMTRlMTA0ZjFhODYyMTBkOTc0OGQ4ZTE4YTI5ODBhZmQ1NTdjNzdkMTU1MjUyNThhNDI1MzAyYTgyNzJmMWZiOTFlNDJlNWM1NzUyZDZhMGRjMTU3NDI5ZmEzZGViZTYyNTM1ZmRmMDRkYjI5OGM1MzVlM2QyZmI1NDAwNzc3ZDcwZGIyNWY0OWJkNDg4ZDg0NmNhODY2NzNhNDc0YThhNzIwOThjNWQyNWM2YjM4N2IzNDI0NWMxZDViYjJiN2ZmYTc2MTNlZjFlZDM5Mjg2Zjg5MGFiZDUzNjM5NzcxN2Q0ZGEwZTVhOGVhZWQ2NzkyYjI3NjA0ZDlmYTFhZWY1ZjgwYjA2MGFkNTljZWJlOTIxYzZkYzQxM2Q3YTE5MDQ3MjhkNzlhMDE3OTUzYmQ1MmMwZmYxOWE0M2YxNWNkZjRlMThlMTNhYjE5ZmRlNGQ0NDEwN2U3ZjQ5ZjIwMjI5ZDE4ZTRhNTRlM2Q0ZGJjNzRiZWQyNWM4OWE2NWZiODQzMGZjZWVkZDIyNGQ3ZmFhNzlhNzMxNmY3YWFjNTdjNmVkNjA2NjM3NzY1NWVjOTVlMjA3NzAxYzViYzkzMDVhNzU0MGIzOGQwNjVlZDc2ZDliZjdkZTAyZDFhMGRkYWJjODczNDE5MmNmYTkyMTM5YjJlNDZjMjljODJmMzdkMTM5NTc2ZDAzMTQ2NDMxMmRhN2RlMzQ3NTM3N2NlNTE1ZmEyZGNjYjBlNDY2ZGYwMGI4MDYwMjJiYjYwMDZhZjk2MWUzOWEwODY1YjIzMTI1ZmQ1ZTIwNjUyYjY3OTI4MGI5YWY1M2U4N2RmMWJlZjIxNDcxY2IwMDk0YzJlYmI2OTc4M2M2M2Y1YTlkNTVkYTk5ZjA3ZTlkYWMxMTc2YjMwYjg4OGY0MzM3N2FmNjJmZDVlMjgyNzE3MzlmZDBjN2JlZjQ4NmM3YWFjNjg1MWNmZDBlOTAyYTg5OGNjMGM0OWQ0OWIxNDQ1ZjZhYjNkY2QxYmVlODY3OGM5NTU5OWQ2OTVlZTlmNzkzMmRmOWQ4OTIwMzc1NTUzNDk4MjFiMGNlNWZiZjc5Y2ZhNWNkNGRlNmY3ZTAxZTJlMjZhNGQ2Y2M3MGMzMThjOGUyMjk2NjA5YmE1N2VjNWVlNTA1YTMwNzc1Mzk2YWY1ZDE1ZDA5YjliYTM0MTE5NzBlMjZmMTAwMmM5MzYzMmQ2NGM1ZGYxMzUyZjA0MGQ4Mjc5YTY4ZGUxMDQ5Y2UxNzI3ZGZiYjcxMzI2Y2M1NGM1OWRlNjAxZTYyNzYyZmI1NTA0ZTQzNjMzMjQxN2JkZmZlNmZiZDQ1NTc3ZWRkNWIzMWZhZjcyMzA3MjFlOTg4NTEwYTA2ZDhjOWRjMTc2MTlmNzVkYTQ4MjU3MzU2NGY1NGRiYzM0MGVlYjk2YWVjNjgzOTMwYWZhODI2ZjA3Njc2NzkyMGM4OTQ1MDY4NWU3NjkyODI1NTk3OWUyMDE3ZmJiMGE3YWRjMTNhMjM4YWU2MzQ2NTgyN2Q5NzEzYThhNzY3MDRkNTA3ZmQyZDM5NmRlZDllM2RmODhhOTE0MTg4NWNmNzAxMzZhMmE5MDIyNWM4NGM3MGRiZDc4YzM1ZTliNjhjZGIyMTJkODhlNWUwMjBiNDliYmQ5ZjQ0NDlkMzc1NDY5YTk5YTJjNzJmMDU3ZGE1MTVmNDRkOGNlZmQyMzdjYzZlYTUxMWExMWE0NGE1Y2EyZWQ4ZjEzMmZiOTc5M2E1YjIwNjBmYzcxOGZjOTVhMmM1Y2IwNmJkNzZhNGM5YTg5NWFmZWRjOTQ1MTY0ZTFmYzRmNmQzNTdmZjVlNTU1MzJjMjBkZjYzOGQyNzI2YmVjZGRmOTJjNTc0NzVlYzRjNGIzMDI2OGZhMDk4MjVkMWNlOGIyYjhhMWZmZjkxYjVmYzFkMWFjOTAxMDE3OGU4MGE4YTBhMzJiMDVjZjRkM2RhNTM1YTU4ZDJlZTY1ZDNjZDhjMDgzZTlkZjc3ODdhNDEwMjMyMGMzMWFlYmJiOTNjZTY2ODBlZWM5NGFkODNiZTYwZmE3OWM0ZDRhYWZlYjkyYjJjZWY4YmU3NzUxMTUwOWJkNzEzOGZhY2YxMDlmOTJhZGUxNDM3MDg4NzQzMDRlZmQ4NzcxMTA1NzE1NzcyN2MxZmIyMjMyYjM5Y2Y4NzNkYzU5ODMyNzNmZWMwY2U0ZTg4MDkzZWI5MjgwM2Y2NDg2MzJhNDNjNTFhZTUwZjRkZTM0YmQ0OTY0YTY5NGEzN2IzYzRkNjUzOTA2MzgxYmM5NmNjZGQ1NjZiNzhhOGFhODZiNDA4ZTlkZDgxZmMzNGJkNzVlZWRmMjMzMTlhOGRmNjNmMTU4ODE1NmIwMDJiYjE2MTZhODNkNzU2NjJjZjE0MDc1MjI0OGQyYWVjMjhiN2E2NzBiZDYyM2E1MWI5MTEzZjBmN2I0YThiOTExOGQ2NzFkNmZlNjg2OTJhMmEzOTEzNWM1M2NkNzFjMTk2MDA5NjM0M2JmMjVlYWNkYjIwODJlZjU2YmNkMjAzZTkzOGNkYjg3MTZkZDBhZGM5ODBlZWI1MmMxNTAxODA2NGMxZmY2ZDMyZDMwZDliMjI4OTFiOWNjYTViNjRjNzFhNmI3ZmNlYjkwMTkzM2E3ODUwYyIsICJjaGFsbGVuZ2VfYmFzZTY0IjogIntDSEEtQjY0fSJ9"}}}' + } + }, + }, + }, + 'Manifest HLS': { + MI_CONFIG: {}, + 'HLS VOD': { + MI_CONFIG: {}, + 'Google shaka demo [multi-audio-codecs, subtitles]': { + SI_FEATURE: 'ADPV,SUB', + SI_CODECS: 'avc1,mp4a,ac-3,ec-3,wvtt', + SI_CONFIG: { + 'manifest_url': 'https://storage.googleapis.com/shaka-demo-assets/apple-advanced-stream-ts/master.m3u8' + } + }, + 'Theoplayer elephants dream [subtitles]': { + SI_FEATURE: 'ADPV,SUB', + SI_CODECS: 'avc1,mp4a,wvtt', + SI_INFO: 'Two subtitles tracks', + SI_CONFIG: { + 'manifest_url': 'http://cdn.theoplayer.com/video/elephants-dream/playlist-single-audio.m3u8' + } + }, + 'Longtailvideo oceans [audio included and also separate track]': { + SI_FEATURE: 'ADPV,AUDI', + SI_ENCRYPT: 'AES', + SI_CONFIG: { + 'manifest_url': 'https://playertest.longtailvideo.com/adaptive/oceans_aes/oceans_aes.m3u8' + } + }, + 'Theoplayer big bunny - key for each segment [media playlist]': { + SI_ENCRYPT: 'AES', + SI_INFO: 'This is media m3u8 manifest, not master m3u8', + SI_CONFIG: { + 'manifest_url': 'https://cdn.theoplayer.com/video/big_buck_bunny_encrypted/stream-800/index.m3u8' + } + }, + 'UnifiedStreaming tears of steel': { + SI_FEATURE: 'ADPV,SUB', + SI_ENCRYPT: 'AES', + SI_CODECS: 'avc1,hvc1,aac-lc,wvtt', + SI_INFO: 'Two subtitles tracks', + SI_CONFIG: { + 'manifest_url': 'https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel-aes.ism/.m3u8' + } + }, + 'Apple bipbop_adv [multi-codec]': { + SI_FEATURE: 'ADPV,SUB', + SI_CODECS: 'avc1,hvc1,ac-3,ec-3,wvtt', + SI_CONFIG: { + 'manifest_url': 'https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_adv_example_hevc/master.m3u8' + } + }, + 'Apple adv_dv_atmos [multi-codec]': { + SI_FEATURE: 'ADPV,SUB', + SI_CODECS: 'avc1,hvc1,dvh1,ac-3,ec-3,aac-lc,he-aac,wvtt', + SI_INFO: 'Audio have also atmos, multiple subtitles tracks', + SI_CONFIG: { + 'manifest_url': 'https://devstreaming-cdn.apple.com/videos/streaming/examples/adv_dv_atmos/main.m3u8' + } + }, + 'UnifiedStreaming tears of steel - impaired subs': { + SI_FEATURE: 'ADPV,SUB', + SI_CONFIG: { + 'manifest_url': 'https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel-hoh-subs.ism/.m3u8', + } + }, + 'UnifiedStreaming tears of steel [sample-aes, key rotation]': { + SI_ENCRYPT: 'SAES', + SI_CONFIG: { + 'manifest_url': 'https://demo.unified-streaming.com/k8s/keyrotation/stable/keyrotation/keyrotation.isml/.m3u8' + } + }, + 'UnifiedStreaming tears of steel [sample-aes]': { + SI_ENCRYPT: 'SAES', + SI_CONFIG: { + 'manifest_url': 'https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel-sample-aes.ism/.m3u8' + } + }, + 'Videojs singlefiles [byte range segments]': { + SI_CONFIG: { + 'manifest_url': 'https://videojs-test-1.s3.eu-central-1.amazonaws.com/HLS_SingleFiles/master.m3u8' + } + }, + 'Longtailvideo elephants dream': { + SI_FEATURE: 'ADPV,SUB', + SI_CODECS: 'avc1,aac,wvtt', + SI_INFO: 'Multiple audio and subtitles tracks', + SI_CONFIG: { + 'manifest_url': 'https://playertest.longtailvideo.com/adaptive/elephants_dream_v4/index.m3u8' + } + }, + 'Apple bipbop [media playlist]': { + SI_CONFIG: { + 'manifest_url': 'http://devimages.apple.com/iphone/samples/bipbop/gear1/prog_index.m3u8' + } + }, + 'AWS bipbop advanced [subtitles]': { + SI_FEATURE: 'ADPV,SUB', + SI_INFO: 'Multiple subtitles tracks with different flags', + SI_CONFIG: { + 'manifest_url': 'https://s3.amazonaws.com/_bc_dml/example-content/bipbop-advanced/bipbop_16x9_variant.m3u8', + } + }, + 'Kaltura sample 4/3 resolution': { + SI_CONFIG: { + 'manifest_url': 'http://cdnbakmi.kaltura.com/p/243342/sp/24334200/playManifest/entryId/0_uka1msg4/flavorIds/1_vqhfu6uy,1_80sohj7p/format/applehttp/protocol/http/a.m3u8', + } + }, + 'mux.dev ADS deltatre': { + SI_INFO: 'Multiple periods, the first and the last one are AES-128 encrypted', + SI_CONFIG: { + 'manifest_url': 'https://test-streams.mux.dev/dai-discontinuity-deltatre/manifest.m3u8', + } + }, + 'mux.dev Tears of Steel, IMSC Captions': { + SI_FEATURE: 'ADPV,SUBMP4', + SI_CODECS: 'avc1,mp4a,ttml', + SI_INFO: 'Subtitle TTML MP4 container', + SI_CONFIG: { + 'manifest_url': 'https://test-streams.mux.dev/tos_ismc/main.m3u8', + } + }, + }, + 'HLS VOD with DRM': { + MI_CONFIG: {}, + 'Ezdrm bunny [multi-drm]': { + SI_ENCRYPT: 'DRMWV,DRMFP', + SI_INFO: 'DRM config set to widevine', + SI_CONFIG: { + 'manifest_url': 'https://drm-test-cf.softvelum.com/live_ezdrm/bunny/playlist.m3u8', + 'license_type': 'com.widevine.alpha', + 'license_key': 'https://widevine-dash.ezdrm.com/widevine-php/widevine-foreignkey.php?pX=B03B45||R{SSM}|R', + 'drm_legacy': 'com.widevine.alpha|https://widevine-dash.ezdrm.com/widevine-php/widevine-foreignkey.php?pX=B03B45', + 'drm': '{"com.widevine.alpha": {"license": {"server_url": "https://widevine-dash.ezdrm.com/widevine-php/widevine-foreignkey.php?pX=B03B45"}}}' + } + }, + 'Google angel one [multi-period]': { + SI_ENCRYPT: 'DRMWV', + SI_CONFIG: { + 'manifest_url': 'https://storage.googleapis.com/shaka-demo-assets/angel-one-widevine-hls/hls.m3u8', + 'license_type': 'com.widevine.alpha', + 'license_key': 'https://cwip-shaka-proxy.appspot.com/no_auth||R{SSM}|R', + 'drm_legacy': 'com.widevine.alpha|https://cwip-shaka-proxy.appspot.com/no_auth', + 'drm': '{"com.widevine.alpha": {"license": {"server_url": "https://cwip-shaka-proxy.appspot.com/no_auth"}}}' + } + }, + 'Microsoft applenc [CBCS]': { + SI_ENCRYPT: 'CBCS,DRMPR', + SI_CONFIG: { + 'manifest_url': 'https://test.playready.microsoft.com/media/dash/APPLEENC_CBCS_BBB_1080p/1080p_alternate.m3u8', + 'license_type': 'com.microsoft.playready', + 'license_key': 'https://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,ck:W31bfVt9W31bfVt9W31bfQ==,ckt:aescbc)', + 'drm_legacy': 'com.microsoft.playready|https://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,ck:W31bfVt9W31bfVt9W31bfQ==,ckt:aescbc)', + 'drm': '{"com.microsoft.playready": {"license": {"server_url": "https://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,ck:W31bfVt9W31bfVt9W31bfQ==,ckt:aescbc)"}}}' + } + }, + 'Google angel one [clear key, keys on manifest]': { + SI_ENCRYPT: 'DRMCK', + SI_CONFIG: { + 'manifest_url': 'https://storage.googleapis.com/shaka-demo-assets/angel-one-sample-aes-ctr-multiple-key/manifest.m3u8', + 'drm_legacy': 'org.w3.clearkey', + 'drm': '{"org.w3.clearkey": {}}', + } + }, + 'Google angel one [clear key, keys on property]': { + SI_ENCRYPT: 'DRMCK', + SI_CONFIG: { + 'manifest_url': 'https://storage.googleapis.com/shaka-demo-assets/angel-one-sample-aes-ctr-multiple-key/manifest.m3u8', + 'drm_legacy': 'org.w3.clearkey|abba271e8bcf552bbd2e86a434a9a5d9:abba271e8bcf552bbd2e86a434a9a5d9,a4631a153a443df9eed0593043db7519:a4631a153a443df9eed0593043db7519', + 'drm': '{"org.w3.clearkey": {"license": {"keyids": {"abba271e8bcf552bbd2e86a434a9a5d9": "abba271e8bcf552bbd2e86a434a9a5d9", "a4631a153a443df9eed0593043db7519": "a4631a153a443df9eed0593043db7519"}}}}', + } + }, + }, + }, + 'Manifest Smooth Streaming': { + MI_CONFIG: {}, + 'ISM VOD': { + MI_CONFIG: {}, + 'UnifiedStreaming Tears of steel [subtitles]': { + SI_FEATURE: 'ADP,SUBMP4', + SI_INFO: 'Multiple subtitles', + SI_CONFIG: { + 'manifest_url': 'https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel-multiple-subtitles.ism/Manifest', + } + }, + }, + 'ISM VOD with DRM': { + MI_CONFIG: {}, + 'Microsoft SuperSpeedway': { + SI_ENCRYPT: 'DRMPR', + SI_INFO: 'WRM-HEADER has outdated license url, its replaced by the provided one', + SI_CONFIG: { + 'manifest_url': 'https://test.playready.microsoft.com/smoothstreaming/SSWSS720H264PR/SuperSpeedway_720.ism/Manifest', + 'license_type': 'com.microsoft.playready', + 'license_key': 'https://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,sl:150)', + 'drm_legacy': 'com.microsoft.playready|https://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,sl:150)', + 'drm': '{"com.microsoft.playready": {"license": {"server_url": "https://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,sl:150)"}}}' + } + }, + 'Microsoft tears of steel 4k': { + SI_ENCRYPT: 'DRMPR', + SI_INFO: 'WRM-HEADER has outdated license url, its replaced by the provided one', + SI_CONFIG: { + 'manifest_url': 'https://test.playready.microsoft.com/media/profficialsite/tearsofsteel_4k.ism.smoothstreaming/manifest', + 'license_type': 'com.microsoft.playready', + 'license_key': 'https://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,sl:150)', + 'drm_legacy': 'com.microsoft.playready|https://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,sl:150)', + 'drm': '{"com.microsoft.playready": {"license": {"server_url": "https://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,sl:150)"}}}' + } + }, + }, + 'ISM Live': { + MI_CONFIG: {}, + 'UnifiedStreaming k8s_stable': { + SI_CONFIG: { + 'manifest_url': 'https://demo.unified-streaming.com/k8s/live/stable/live.isml/Manifest', + } + }, + }, + }, +} + + +def find_encrypt_entries(data, encrypt_flag=None): + """Traverse all dictionaries on json data to find all streams with encrypt flags""" + + def traverse_dict(d, path, curr_key): + """ + Traverse a dictionary + :param d: Json data dict + :param path: The current json path + :param curr_key: The current dict key (e.g. the title of menu) + :return: a yield of tuple as + (the full json path, the parent dict key name, the current dict key, the current dict value) + """ + if isinstance(d, dict): + if SI_CONFIG in d and SI_ENCRYPT in d and (encrypt_flag is None or encrypt_flag in d[SI_ENCRYPT]): + yield '/'.join(path), path[0], curr_key, d + else: + if curr_key: + path.append(curr_key) + for k, v in d.items(): + yield from traverse_dict(v, path, k) + if path: + del path[-1] + yield from traverse_dict(data, [], '') + + +def find_feature_entries(data, feat_flags): + """Traverse all dictionaries on json data to find all streams with some feature flags""" + if not feat_flags: + return + def traverse_dict(d, path, curr_key): + """ + Traverse a dictionary + :param d: Json data dict + :param path: The current json path + :param curr_key: The current dict key (e.g. the title of menu) + :return: a yield of tuple as + (the full json path, the parent dict key name, the current dict key, the current dict value) + """ + if isinstance(d, dict): + if SI_CONFIG in d and SI_FEATURE in d and any(value in d[SI_FEATURE] for value in feat_flags): + yield '/'.join(path), path[0], curr_key, d + else: + if curr_key: + path.append(curr_key) + for k, v in d.items(): + yield from traverse_dict(v, path, k) + if path: + del path[-1] + yield from traverse_dict(data, [], '') diff --git a/plugin.video.isasamples/resources/icon.png b/plugin.video.isasamples/resources/icon.png new file mode 100644 index 000000000..5dfa337fc Binary files /dev/null and b/plugin.video.isasamples/resources/icon.png differ diff --git a/plugin.video.isasamples/resources/language/resource.language.en_gb/strings.po b/plugin.video.isasamples/resources/language/resource.language.en_gb/strings.po new file mode 100644 index 000000000..c93b52715 --- /dev/null +++ b/plugin.video.isasamples/resources/language/resource.language.en_gb/strings.po @@ -0,0 +1,69 @@ +# Kodi Media Center language file +msgid "" +msgstr "" +"Project-Id-Version: plugin.video.isasamples\n" +"Report-Msgid-Bugs-To: https://github.com/xbmc/inputstream.adaptive/issues\n" +"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Kodi Translation Team\n" +"Language-Team: English\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: en\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. Settings window category "general" +msgctxt "#30000" +msgid "General" +msgstr "" + +#. Group for menu settings +msgctxt "#30002" +msgid "Menu options" +msgstr "" + +msgctxt "#30003" +msgid "Show feature flags on streams titles" +msgstr "" + +msgctxt "#30004" +msgid "Show crypto flags on streams titles" +msgstr "" + +msgctxt "#30005" +msgid "Show codecs on streams titles" +msgstr "" + +#. Group for ISA settings +msgctxt "#30100" +msgid "InputStream Adaptive settings" +msgstr "" + +msgctxt "#30101" +msgid "Preferred DRM config method" +msgstr "" + +#. Help of 30101 +msgctxt "#30102" +msgid "If are available multiple DRM configs methods on a stream, try to use the preferred one, otherwise the first available." +msgstr "" + +#. Entry item of 30101 +msgctxt "#30104" +msgid "Advanced method (properties/deprecated)" +msgstr "" + +#. Entry item of 30101 +msgctxt "#30105" +msgid "Simple method (drm_legacy)" +msgstr "" + +#. Entry item of 30101 +msgctxt "#30106" +msgid "Advanced method (drm)" +msgstr "" + +msgctxt "#30200" +msgid "Open settings" +msgstr "" diff --git a/plugin.video.isasamples/resources/settings.xml b/plugin.video.isasamples/resources/settings.xml new file mode 100644 index 000000000..e15119d3a --- /dev/null +++ b/plugin.video.isasamples/resources/settings.xml @@ -0,0 +1,45 @@ + + +
+ + + + 0 + Addon.OpenSettings(inputstream.adaptive) + + true + + + + 0 + props + + + + + + + + + + + + + 0 + false + + + + 0 + false + + + + 0 + false + + + + +
+