def get_extensions(user, repo, addon_xml=None): extensions = [] if not addon_xml: addon_xml = API.get_file(user, repo, "addon.xml", text=True) if addon_xml: tools.log("Checking for extensions in {}/{}".format(user, repo)) root = tools.parse_xml(text=addon_xml.encode("utf-8")) try: tags = root.findall("extension") if tags is not None: for ext in tags: point = ext.get("point") if point and point in _extensions: ext_point = _extensions[point] if isinstance(ext_point, dict): provides = ext.find("provides") if provides is not None and provides.text: all_provides = provides.text.split(" ") for p in all_provides: if p in ext_point: extensions.append(ext_point[p]) else: extensions.append(ext_point[None]) else: extensions.append(ext_point) except Exception as e: tools.log("Could not check for extensions: {}".format(e), level="warning") return extensions
def run(self): """ Executes the workload :return: :rtype: """ while not self.tasks.empty() and not self.stop_flag.is_set(): try: func, result_callback, args, kwargs = self.tasks.get( timeout=0.1) self.name = func result_callback(func(*args, **kwargs)) except Empty: break except BaseException as ex: tools.log(traceback.format_exc(), "error") self.exception_handler(ex) break finally: try: self.tasks.task_done() except Exception as e: print("task done error: {}".format(repr(e))) pass
def setupForMusicVideo(self): log('settings() - setupForMusicVideo') if self.music_preset == 1: #preset Ballad saturation = 3.0 value = 10.0 speed = 20.0 autospeed = 0.0 interpolation = 1 threshold = 0.0 elif self.music_preset == 2: #preset Rock saturation = 3.0 value = 10.0 speed = 80.0 autospeed = 0.0 interpolation = 0 threshold = 0.0 elif self.music_preset == 3: #preset disabled saturation = 0.0 value = 0.0 speed = 0.0 autospeed = 0.0 interpolation = 0 threshold = 0.0 elif self.music_preset == 0: #custom saturation = self.music_saturation value = self.music_value speed = self.music_speed autospeed = self.music_autospeed interpolation = self.music_interpolation threshold = self.music_threshold return (saturation, value, speed, autospeed, interpolation, threshold)
def _get_log_contents(logfile=None): if logfile == None: logfile = os.path.join(_log_location, "kodi.log") if os.path.exists(logfile): return tools.read_from_file(logfile) else: tools.log("Error finding logs!", "error")
def get_icon(user, repo, plugin_id, addon_xml=None): icon = "" addon_path = os.path.join(_addons, plugin_id) if os.path.exists(addon_path): addon_xml = tools.read_from_file(os.path.join(addon_path, "addon.xml")) if not addon_xml: addon_xml = API.get_file(user, repo, "addon.xml", text=True) if addon_xml: tools.log("Finding icon in addon.xml from {}/{}".format(user, repo)) addon = tools.parse_xml(text=addon_xml.encode("utf-8")) try: icon_path = "icon.png" def_icon = list(addon.iter("icon")) if def_icon and len(def_icon) > 0: icon_path = def_icon[0].text if os.path.exists(addon_path): icon = os.path.join(addon_path, icon_path) else: icon_url = API.get_file(user, repo, icon_path)["download_url"] icon = requests.head(icon_url, allow_redirects=True).url except Exception as e: tools.log("Could not get icon: {}".format(e), level="warning") return icon
def setupForFiles(self): log('settings() - setupForFiles') if self.files_preset == 1: #preset smooth saturation = 3.0 value = 10.0 speed = 20.0 autospeed = 0.0 interpolation = 0 threshold = 0.0 elif self.files_preset == 2: #preset action saturation = 3.0 value = 10.0 speed = 80.0 autospeed = 0.0 interpolation = 0 threshold = 0.0 elif self.files_preset == 3: #preset disabled saturation = 0.0 value = 0.0 speed = 0.0 autospeed = 0.0 interpolation = 0 threshold = 0.0 elif self.files_preset == 0: #custom saturation = self.files_saturation value = self.files_value speed = self.files_speed autospeed = self.files_autospeed interpolation = self.files_interpolation threshold = self.files_threshold return (saturation, value, speed, autospeed, interpolation, threshold)
def handleStereoscopic(self, isStereoscopic): log('settings() - handleStereoscopic(%s) - disableon3d (%s)' % (isStereoscopic, self.bobdisableon3d)) if self.bobdisableon3d and isStereoscopic: log('settings()- disable due to 3d') self.bobdisable = True else: self.resetBobDisable()
def setupForStatic(self): log('settings() - setupForStatic') saturation = 4.0 value = 1.0 speed = 50.0 autospeed = 0.0 interpolation = 1 threshold = 0.0 return (saturation, value, speed, autospeed, interpolation, threshold)
def setupForOther(self): log('settings() - setupForOther') # FIXME don't use them for now - reactivate when boblight works on non rendered scenes (e.x. menu) # saturation = float(__addon__.getSetting("other_saturation")) # value = float(__addon__.getSetting("other_value")) # speed = float(__addon__.getSetting("other_speed")) # autospeed = float(__addon__.getSetting("other_autospeed")) # interpolation = __addon__.getSetting("other_interpolation") == "true" # threshold = float(__addon__.getSetting("other_threshold")) return self.setupForStatic()
def raise_issue(repo): dialog = xbmcgui.Dialog() title = dialog.input(settings.get_localized_string(30006)) if title: description = dialog.input(settings.get_localized_string(30007)) log_key = None response, log_key = logging.upload_log() if response: try: resp = API.raise_issue( repo["user"], repo["repo"], _format_issue(title, description, log_key), ) if "message" not in resp: qr_code = qr.generate_qr( resp["html_url"], _addon_data, "{}.png".format(resp["number"]), ) top = [ ( settings.get_localized_string(30008), "#efefefff", ), ( "{}/{}".format(repo["user"], repo["repo"]), _color, ), ] bottom = [ (settings.get_localized_string(30079), "#efefefff"), (resp["html_url"], _color), ] qr.qr_dialog( qr_code, top_text=top, bottom_text=bottom, ) tools.execute_builtin("ShowPicture({})".format(qr_code)) while tools.get_condition("Window.IsActive(slideshow)"): xbmc.sleep(1000) os.remove(qr_code) else: dialog.ok(_addon_name, resp["message"]) except requests.exceptions.RequestException as e: dialog.notification(_addon_name, settings.get_localized_string(30009)) tools.log("Error opening issue: {}".format(e), "error") else: dialog.ok(_addon_name, settings.get_localized_string(30010)) del dialog
def __init__(self, *args, **kwargs): log('settings() - __init__') self.staticBobActive = False self.run_init = True self.category = "static" self.networkaccess = __addon__.getSetting("networkaccess") == "true" if not self.networkaccess: self.hostip = None self.hostport = -1 else: self.hostip = __addon__.getSetting("hostip") self.hostport = int(__addon__.getSetting("hostport")) self.start()
def _update_addon_version(addon, gitsha): addon_xml = os.path.join(_addons, addon, "addon.xml") tools.log("Rewriting addon version: {}".format(addon_xml)) replace_regex = r'<\1"\2.\3.\4-{}"\7>'.format(gitsha[:7]) content = tools.read_from_file(addon_xml) content = re.sub( r"<(addon id.*version=)\"([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+)?(-.*?)?\"(.*)>", replace_regex, content, ) tools.write_to_file(addon_xml, content)
def handleStaticBgSettings(self): log('settings() - handleStaticBgSettings') if (self.category == "static" and # only for 'static' category self.other_static_bg ): # only if we want it displayed on static bob.bob_set_priority(128) # allow lights to be turned on rgb = (c_int * 3)(self.other_static_red, self.other_static_green, self.other_static_blue) ret = bob.bob_set_static_color(byref(rgb)) self.staticBobActive = True else: bob.bob_set_priority(255) self.staticBobActive = False
def _exists(addon): params = { "jsonrpc": "2.0", "method": "Addons.GetAddons", "id": 1, } addons = tools.execute_jsonrpc(params) exists = False if addon in [ a.get("addonid") for a in addons.get("result", {}).get("addons", {}) ]: exists = True tools.log("{} {} installed".format(addon, "is" if exists else "not")) return exists
def listVideos(url, pattern): data = tools.getUrl(url) matches = re.compile(pattern, re.DOTALL).findall(data) for match in matches: tools.log("listVideos->match: "+str(match)) for scrapedurl, scrapedthumbnail, scrapedtitle, scrapeddate in matches: scrapedtitle = tools.cleanText(scrapedtitle) scrapeddate = scrapeddate.replace('<span class="dateseparator">|</span>\r\n ', ' | ').strip() tools.addLink(scrapedtitle+" ["+scrapeddate+"]", scrapedurl, 'playVideo', scrapedthumbnail) xbmcplugin.endOfDirectory(pluginhandle) if forceViewMode: tools.set_view(viewMode)
def _install_deps(addon): failed_deps = [] visible_cond = "Window.IsTopMost(yesnodialog)" xml_path = os.path.join(_addons, addon, "addon.xml") tools.log("Finding dependencies in {}".format(xml_path)) root = tools.parse_xml(file=xml_path) requires = root.find("requires") if not requires: return deps = requires.findall("import") for dep in [ d for d in deps if not d.get("addon").startswith("xbmc") and not d.get("optional") == "true" ]: plugin_id = dep.get("addon") installed_cond = "System.HasAddon({0})".format(plugin_id) if tools.get_condition(installed_cond): continue tools.log("Installing dependency: {}".format(plugin_id)) tools.execute_builtin("InstallAddon({0})".format(plugin_id)) clicked = False start = time.time() timeout = 10 while not tools.get_condition(installed_cond): if time.time() >= start + timeout: tools.log( "Timed out installing dependency: {}".format(plugin_id), level="warning", ) failed_deps.append(plugin_id) break tools.sleep(500) if tools.get_condition(visible_cond) and not clicked: tools.log("Dialog to click open") tools.execute_builtin("SendClick(yesnodialog, 11)") clicked = True else: tools.log("...waiting") return failed_deps
def _set_enabled(addon, enabled, exists=True): enabled_params = { "jsonrpc": "2.0", "method": "Addons.GetAddonDetails", "params": { "addonid": addon, "properties": ["enabled"] }, } params = { "jsonrpc": "2.0", "method": "Addons.SetAddonEnabled", "params": { "addonid": addon, "enabled": enabled }, } if not exists and not enabled: return False elif not exists and enabled: db_file = _get_addons_db() connection = sqlite3.connect(db_file) cursor = connection.cursor() date = time.strftime("%Y-%m-%d %H:%M:%S") cursor.execute("DELETE FROM installed WHERE addonID = ?", (addon, )) cursor.execute( "INSERT INTO installed (addonID, enabled, installDate) VALUES (?, 1, ?)", (addon, date), ) connection.commit() connection.close() else: tools.execute_jsonrpc(params) new_status = (tools.execute_jsonrpc(enabled_params).get("result", {}).get( "addon", {}).get("enabled", enabled)) == enabled tools.log("{}{}{}abled".format(addon, " " if new_status else " not ", "en" if enabled else "dis")) return new_status
def _rewrite_kodi_dependency_versions(addon): kodi_version = tools.kodi_version() tools.log("KODI_VERSION: {}".format(kodi_version)) kodi_dep_versions = { 18: { "xbmc.python": "2.26.0", "xbmc.gui": "5.14.0" }, 19: { "xbmc.python": "3.0.0", "xbmc.gui": "5.15.0" }, } if kodi_version in kodi_dep_versions: kodi_deps = kodi_dep_versions[kodi_version] else: # Take latest version if we don't have version specific kodi_deps = kodi_dep_versions[max(kodi_dep_versions)] tools.log("KODI DEPENDENCY VERSIONS: {}".format(kodi_deps)) addon_xml = os.path.join(_addons, addon, "addon.xml") tools.log("Rewriting {}".format(addon_xml)) content = tools.read_from_file(addon_xml) for dep in kodi_deps: content = re.sub( '<import addon="' + dep + r'" version=".*?"\s?/>', '<import addon="' + dep + '" version="' + kodi_deps[dep] + '" />', content, ) tools.write_to_file(addon_xml, content)
def upload_log(choose=False, dialog=False): logfile = _select_log_file() if choose else None log_data = _censor_log_content(_get_log_contents(logfile)) user_agent = "{}: {}".format(_addon_id, _addon_version) try: response = requests.post( _paste_url + "documents", data=log_data.encode("utf-8"), headers={ "User-Agent": user_agent }, ).json() if "key" in response: if dialog: _log_dialog(response["key"]) return True, response["key"] elif "message" in response: tools.log("Upload failed: {}".format(response["message"]), level="error") return False, response["message"] else: tools.log("Invalid response: {}".format(response), level="error") return False, "Error posting the log file." except requests.exceptions.RequestException as e: tools.log("Failed to retrieve the paste URL: {}".format(e), level="error") return False, "Failed to retrieve the paste URL."
def handleGlobalSettings(self): log('settings() - handleGlobalSettings') if (self.current_option != self.category) or self.force_update: #call the right setup function according to category #switch case in python - dictionary with function pointers option = { "movie": self.setupForMovie, "tvshow": self.setupForTVShow, "livetv": self.setupForLiveTV, "files": self.setupForFiles, "musicvideo": self.setupForMusicVideo, "other": self.setupForOther, "static": self.setupForStatic, } saturation, value, speed, autospeed, interpolation, threshold = option[ self.category]() for opt in OPTS: ret = bob.bob_setoption("%s %s" % (opt, str(locals()[opt]))) log("changed %s to %s ret: %s" % (opt, str(locals()[opt]), ret)) self.current_option = self.category self.force_update = False
def get_repo_info(repo_def): user = repo_def["owner"]["login"] repo = repo_def["name"] addon_xml = API.get_file(user, repo, "addon.xml", text=True) if not addon_xml: return tools.log("Reading addon.xml from {}/{}".format(user, repo)) addon = tools.parse_xml(text=addon_xml.encode("utf-8")) def_name = addon.get("name") icon = get_icon(user, repo, addon_xml) extensions = get_extensions(user, repo, addon_xml) return [{ "name": def_name, "user": user, "repo_name": repo, "updated_at": repo_def["updated_at"], "icon": icon, "extensions": extensions, }]
def _extract_addon(zip_location, repo): tools.log("Opening {}".format(zip_location)) with zipfile.ZipFile(zip_location) as file: base_directory = file.namelist()[0] tools.log("Extracting to: {}".format( os.path.join(_temp, base_directory))) for f in [ i for i in file.namelist() if all(e not in i for e in repo.get("exclude_items", [])) ]: try: file.extract(f, _temp) except Exception as e: tools.log("Could not extract {}: {}".format(f, e)) install_path = os.path.join(_addons, repo["plugin_id"]) tools.copytree(os.path.join(_temp, base_directory), install_path, ignore=True) tools.remove_folder(os.path.join(_temp, base_directory)) tools.remove_file(zip_location)
def bob_init(self): if self.run_init: log('bob_init') nrLights = bob.bob_getnrlights() log("settings() - Found %s lights" % str(nrLights)) for i in range(nrLights): lightname = bob.bob_getlightname(i) log("settings() - Light[%.2d] - %s" % (i + 1, lightname)) self.handleGlobalSettings() bob.bob_set_priority( 128) # allow lights to be turned on, we will switch them off # in 'handleStaticBgSettings()' if they are not needed if self.other_misc_initialflash: for i in range(len(BLING)): rgb = (c_int * 3)(BLING[i][0], BLING[i][1], BLING[i][2]) bob.bob_set_static_color(byref(rgb)) xbmc.sleep(1000) else: rgb = (c_int * 3)(0, 0, 0) bob.bob_set_static_color(byref(rgb)) self.run_init = False xbmc.sleep(500) return True
def _try_raise(self): if self.exception: tools.log(traceback.format_exc(), "error") raise self.exception
def start(self): log('settings() - start') self.reconnect = False self.force_update = True self.networkaccess = __addon__.getSetting("networkaccess") == "true" self.overwrite_cat = __addon__.getSetting("overwrite_cat") == "true" self.overwrite_cat_val = int(__addon__.getSetting("overwrite_cat_val")) self.bobdisableonscreensaver = __addon__.getSetting( "bobdisableonscreensaver") == "true" self.bobdisable = __addon__.getSetting("bobdisable") == "true" self.bobdisableon3d = __addon__.getSetting("bobdisableon3d") == "true" self.current_option = "" if not self.networkaccess: self.hostip = None self.hostport = -1 self.reconnect = True else: hostip = __addon__.getSetting("hostip") hostport = int(__addon__.getSetting("hostport")) if ((self.hostip != hostip) or (self.hostport != hostport)): self.hostip = hostip self.hostport = hostport self.reconnect = True # Other settings self.other_static_bg = __addon__.getSetting( "other_static_bg") == "true" self.other_static_red = int( float(__addon__.getSetting("other_static_red"))) self.other_static_green = int( float(__addon__.getSetting("other_static_green"))) self.other_static_blue = int( float(__addon__.getSetting("other_static_blue"))) self.other_misc_initialflash = __addon__.getSetting( "other_misc_initialflash") == "true" self.other_misc_notifications = __addon__.getSetting( "other_misc_notifications") == "true" # Movie settings self.movie_saturation = float(__addon__.getSetting("movie_saturation")) self.movie_value = float(__addon__.getSetting("movie_value")) self.movie_speed = float(__addon__.getSetting("movie_speed")) self.movie_autospeed = float(__addon__.getSetting("movie_autospeed")) self.movie_interpolation = int( __addon__.getSetting("movie_interpolation") == "true") self.movie_threshold = float(__addon__.getSetting("movie_threshold")) self.movie_preset = int(__addon__.getSetting("movie_preset")) # TV Shows settings self.tvshow_saturation = float( __addon__.getSetting("tvshow_saturation")) self.tvshow_value = float(__addon__.getSetting("tvshow_value")) self.tvshow_speed = float(__addon__.getSetting("tvshow_speed")) self.tvshow_autospeed = float(__addon__.getSetting("tvshow_autospeed")) self.tvshow_interpolation = int( __addon__.getSetting("tvshow_interpolation") == "true") self.tvshow_threshold = float(__addon__.getSetting("tvshow_threshold")) self.tvshow_preset = int(__addon__.getSetting("tvshow_preset")) # LiveTV settings self.livetv_saturation = float( __addon__.getSetting("livetv_saturation")) self.livetv_value = float(__addon__.getSetting("livetv_value")) self.livetv_speed = float(__addon__.getSetting("livetv_speed")) self.livetv_autospeed = float(__addon__.getSetting("livetv_autospeed")) self.livetv_interpolation = int( __addon__.getSetting("livetv_interpolation") == "true") self.livetv_threshold = float(__addon__.getSetting("livetv_threshold")) self.livetv_preset = int(__addon__.getSetting("livetv_preset")) # Files settings self.files_saturation = float(__addon__.getSetting("files_saturation")) self.files_value = float(__addon__.getSetting("files_value")) self.files_speed = float(__addon__.getSetting("files_speed")) self.files_autospeed = float(__addon__.getSetting("files_autospeed")) self.files_interpolation = int( __addon__.getSetting("files_interpolation") == "true") self.files_threshold = float(__addon__.getSetting("files_threshold")) self.files_preset = int(__addon__.getSetting("files_preset")) # Music Video settings self.music_saturation = float( __addon__.getSetting("musicvideo_saturation")) self.music_value = float(__addon__.getSetting("musicvideo_value")) self.music_speed = float(__addon__.getSetting("musicvideo_speed")) self.music_autospeed = float(__addon__.getSetting("movie_autospeed")) self.music_interpolation = int( __addon__.getSetting("musicvideo_interpolation") == "true") self.music_threshold = float( __addon__.getSetting("musicvideo_threshold")) self.music_preset = int(__addon__.getSetting("musicvideo_preset"))
def handleCategory(self, category): log('settings() - handleCategory(%s)' % category) self.category = category self.handleGlobalSettings() self.handleStaticBgSettings()
def add_repository(): dialog = xbmcgui.Dialog() pool = ThreadPool() user = dialog.input(settings.get_localized_string(30022)).lower() if not user: dialog.notification(_addon_name, settings.get_localized_string(30023)) del dialog return if API.get_user(user).get("type", "User") == "Organization": user_repos = API.get_org_repos(user) elif user == _user.lower(): access_level = [ "owner", "collaborator" if _collaborator else "", "organization_member" if _organization else "", ] user_repos = API.get_repos(",".join(access_level)) else: user_repos = API.get_user_repos(user) addon_repos = [] repo_items = [] with tools.busy_dialog(): for user_repo in user_repos: if "message" in user_repo: dialog.ok(_addon_name, settings.get_localized_string(30065)) del dialog return pool.put(get_repo_info, user_repo) repos = pool.wait_completion() if not repos: dialog.ok(_addon_name, settings.get_localized_string(30066)) del dialog return repos.sort(key=lambda b: b["updated_at"], reverse=True) addon_repos = [i["repo_name"] for i in repos] for i in repos: byline = ("{} - ".format(i["repo_name"]) + ", ".join([ settings.get_localized_string(30049).format(i["user"]), settings.get_localized_string(30016).format( tools.to_local_time(i["updated_at"])), ]) if i["user"].lower() != user else "{} - ".format(i["repo_name"]) + settings.get_localized_string(30016).format( tools.to_local_time(i["updated_at"]))) li = xbmcgui.ListItem( "{} - ({})".format( i["name"], ", ".join([e.title() for e in i["extensions"]])), label2=byline, ) if not _compact: li.setArt({"thumb": i["icon"]}) repo_items.append(li) if len(addon_repos) == 0: dialog.ok(_addon_name, settings.get_localized_string(30058)) del dialog return selection = dialog.select(settings.get_localized_string(30011), repo_items, useDetails=not _compact) if selection < 0: del dialog return user = repos[selection]["user"] repo = addon_repos[selection] if not _check_repo(user, repo): del dialog return addon_xml = API.get_file(user, repo, "addon.xml", text=True) if not addon_xml: del dialog return tools.log("Reading addon.xml from {}/{}".format(user, repo)) addon = tools.parse_xml(text=addon_xml.encode("utf-8")) name = addon.get("name") plugin_id = addon.get("id") if dialog.yesno(_addon_name, settings.get_localized_string(30059).format(name)): _add_repo(user, repo, name, plugin_id) del dialog