# -*- coding: utf-8 -*- """ Copyright (C) 2014-2016 bromix (plugin.video.youtube) Copyright (C) 2016-2018 plugin.video.youtube SPDX-License-Identifier: GPL-2.0-only See LICENSES/GPL-2.0-only for more information. """ from six.moves import urllib import datetime import json import os import sys import weakref import xbmc import xbmcaddon import xbmcplugin import xbmcvfs from ..abstract_context import AbstractContext from .xbmc_plugin_settings import XbmcPluginSettings from .xbmc_context_ui import XbmcContextUI from .xbmc_playlist import XbmcPlaylist from .xbmc_player import XbmcPlayer from ... import utils try: xbmc.translatePath = xbmcvfs.translatePath except AttributeError: pass class XbmcContext(AbstractContext): def __init__(self, path='/', params=None, plugin_name='', plugin_id='', override=True): AbstractContext.__init__(self, path, params, plugin_name, plugin_id) if plugin_id: self._addon = xbmcaddon.Addon(id=plugin_id) else: self._addon = xbmcaddon.Addon(id='plugin.video.youtube') """ I don't know what xbmc/kodi is doing with a simple uri, but we have to extract the information from the sys parameters and re-build our clean uri. Also we extract the path and parameters - man, that would be so simple with the normal url-parsing routines. """ # first the path of the uri if override: self._uri = sys.argv[0] comps = urllib.parse.urlparse(self._uri) self._path = urllib.parse.unquote(comps.path) # after that try to get the params if len(sys.argv) > 2: params = sys.argv[2][1:] if len(params) > 0: self._uri = '?'.join([self._uri, params]) self._params = {} params = dict(urllib.parse.parse_qsl(params)) for _param in params: item = params[_param] self._params[_param] = item self._ui = None self._video_playlist = None self._audio_playlist = None self._video_player = None self._audio_player = None self._plugin_handle = int(sys.argv[1]) if len(sys.argv) > 1 else None self._plugin_id = plugin_id or self._addon.getAddonInfo('id') self._plugin_name = plugin_name or self._addon.getAddonInfo('name') self._version = self._addon.getAddonInfo('version') self._native_path = xbmc.translatePath(self._addon.getAddonInfo('path')) self._settings = XbmcPluginSettings(self._addon) """ Set the data path for this addon and create the folder """ try: self._data_path = xbmc.translatePath(self._addon.getAddonInfo('profile')).decode('utf-8') except AttributeError: self._data_path = xbmc.translatePath(self._addon.getAddonInfo('profile')) if not xbmcvfs.exists(self._data_path): xbmcvfs.mkdir(self._data_path) def get_region(self): pass # implement from abstract def addon(self): return self._addon def is_plugin_path(self, uri, uri_path): return uri.startswith('plugin://%s/%s/' % (self.get_id(), uri_path)) def format_date_short(self, date_obj): date_format = xbmc.getRegion('dateshort') _date_obj = date_obj if isinstance(_date_obj, datetime.date): _date_obj = datetime.datetime(_date_obj.year, _date_obj.month, _date_obj.day) return _date_obj.strftime(date_format) def format_time(self, time_obj): time_format = xbmc.getRegion('time') _time_obj = time_obj if isinstance(_time_obj, datetime.time): _time_obj = datetime.time(_time_obj.hour, _time_obj.minute, _time_obj.second) return _time_obj.strftime(time_format.replace("%H%H", "%H")) def get_language(self): """ The xbmc.getLanguage() method is fucked up!!! We always return 'en-US' for now """ ''' if self.get_system_version().get_release_name() == 'Frodo': return 'en-US' try: language = xbmc.getLanguage(0, region=True) language = language.split('-') language = '%s-%s' % (language[0].lower(), language[1].upper()) return language except Exception, ex: self.log_error('Failed to get system language (%s)', ex.__str__()) return 'en-US' ''' return 'en-US' def get_video_playlist(self): if not self._video_playlist: self._video_playlist = XbmcPlaylist('video', weakref.proxy(self)) return self._video_playlist def get_audio_playlist(self): if not self._audio_playlist: self._audio_playlist = XbmcPlaylist('audio', weakref.proxy(self)) return self._audio_playlist def get_video_player(self): if not self._video_player: self._video_player = XbmcPlayer('video', weakref.proxy(self)) return self._video_player def get_audio_player(self): if not self._audio_player: self._audio_player = XbmcPlayer('audio', weakref.proxy(self)) return self._audio_player def get_ui(self): if not self._ui: self._ui = XbmcContextUI(self._addon, weakref.proxy(self)) return self._ui def get_handle(self): return self._plugin_handle def get_data_path(self): return self._data_path def get_debug_path(self): if not self._debug_path: self._debug_path = os.path.join(self.get_data_path(), 'debug') if not xbmcvfs.exists(self._debug_path): xbmcvfs.mkdir(self._debug_path) return self._debug_path def get_native_path(self): return self._native_path def get_settings(self): return self._settings def localize(self, text_id, default_text=u''): result = None if isinstance(text_id, int): """ We want to use all localization strings! Addons should only use the range 30000 thru 30999 (see: http://kodi.wiki/view/Language_support) but we do it anyway. I want some of the localized strings for the views of a skin. """ if text_id >= 0 and (text_id < 30000 or text_id > 30999): result = xbmc.getLocalizedString(text_id) if result is not None and result: result = utils.to_unicode(result) if not result: try: result = self._addon.getLocalizedString(int(text_id)) if result is not None and result: result = utils.to_unicode(result) except ValueError: pass if not result: result = default_text return result def set_content_type(self, content_type): self.log_debug('Setting content-type: "%s" for "%s"' % (content_type, self.get_path())) xbmcplugin.setContent(self._plugin_handle, content_type) def add_sort_method(self, *sort_methods): for sort_method in sort_methods: xbmcplugin.addSortMethod(self._plugin_handle, sort_method) def clone(self, new_path=None, new_params=None): if not new_path: new_path = self.get_path() if not new_params: new_params = self.get_params() new_context = XbmcContext(path=new_path, params=new_params, plugin_name=self._plugin_name, plugin_id=self._plugin_id, override=False) new_context._function_cache = self._function_cache new_context._search_history = self._search_history new_context._favorite_list = self._favorite_list new_context._watch_later_list = self._watch_later_list new_context._access_manager = self._access_manager new_context._ui = self._ui new_context._video_playlist = self._video_playlist new_context._video_player = self._video_player return new_context def execute(self, command): xbmc.executebuiltin(command) def sleep(self, milli_seconds): xbmc.sleep(milli_seconds) def addon_enabled(self, addon_id): rpc_request = json.dumps({"jsonrpc": "2.0", "method": "Addons.GetAddonDetails", "id": 1, "params": {"addonid": "%s" % addon_id, "properties": ["enabled"]} }) response = json.loads(xbmc.executeJSONRPC(rpc_request)) try: return response['result']['addon']['enabled'] is True except KeyError: message = response['error']['message'] code = response['error']['code'] error = 'Requested |%s| received error |%s| and code: |%s|' % (rpc_request, message, code) self.log_debug(error) return False def set_addon_enabled(self, addon_id, enabled=True): rpc_request = json.dumps({"jsonrpc": "2.0", "method": "Addons.SetAddonEnabled", "id": 1, "params": {"addonid": "%s" % addon_id, "enabled": enabled} }) response = json.loads(xbmc.executeJSONRPC(rpc_request)) try: return response['result'] == 'OK' except KeyError: message = response['error']['message'] code = response['error']['code'] error = 'Requested |%s| received error |%s| and code: |%s|' % (rpc_request, message, code) self.log_debug(error) return False def send_notification(self, method, data): data = json.dumps(data) self.log_debug('send_notification: |%s| -> |%s|' % (method, data)) data = '\\"[\\"%s\\"]\\"' % urllib.parse.quote(data) self.execute('NotifyAll(plugin.video.youtube,%s,%s)' % (method, data)) def use_inputstream_adaptive(self): addon_enabled = self.addon_enabled('inputstream.adaptive') if self._settings.use_dash() and not addon_enabled: if self.get_ui().on_yes_no_input(self.get_name(), self.localize(30579)): use_dash = self.set_addon_enabled('inputstream.adaptive') else: use_dash = False elif self._settings.use_dash() and addon_enabled: use_dash = True else: use_dash = False return use_dash def inputstream_adaptive_capabilities(self, capability=None): # return a list inputstream.adaptive capabilities, if capability set return version required capabilities = [] use_dash = self.use_inputstream_adaptive() try: inputstream_version = xbmcaddon.Addon('inputstream.adaptive').getAddonInfo('version') except RuntimeError: inputstream_version = '' if not use_dash or not inputstream_version: return None if capability is not None else capabilities capability_map = { 'live': '2.0.12', 'drm': '2.2.12', 'vp9': '2.3.14', 'vp9.2': '2.3.14', 'vorbis': None, 'opus': None, 'av1': None, } if capability is None: ia_loose_version = utils.loose_version(inputstream_version) for key in list(capability_map.keys()): if capability_map[key] and (ia_loose_version >= utils.loose_version(capability_map[key])): capabilities.append(key) return capabilities else: return capability_map[capability] if capability_map.get(capability) else None def inputstream_adaptive_auto_stream_selection(self): try: return xbmcaddon.Addon('inputstream.adaptive').getSetting('STREAMSELECTION') == '0' except RuntimeError: return False def abort_requested(self): return str(self.get_ui().get_home_window_property('abort_requested')).lower() == 'true'