def remove_repository(repo): dialog = xbmcgui.Dialog() repos = get_repos() filename = repo["filename"] repo_defs = [i for i in repos.values()] indices = [i for i, x in enumerate(repo_defs) if x["filename"] == filename] if len(indices) > 1: remove = dialog.yesno( _addon_name, settings.get_localized_string(30026).format(", ".join( [repo_defs[i]["name"] for i in indices])), ) else: remove = dialog.yesno( _addon_name, settings.get_localized_string(30027).format(repo["name"])) if remove: os.remove(filename) dialog.notification( _addon_name, settings.get_localized_string(30028 if len(indices) == 1 else 30029).format(len(indices)), ) del dialog
def revoke(): dialog = xbmcgui.Dialog() if dialog.yesno( settings.get_localized_string(30045), settings.get_localized_string(30033).format( color.color_string(_auth_url)), ): _clear_oauth() dialog.notification(_addon_name, settings.get_localized_string(30034)) settings.open_settings()
def authorize(in_addon=False): init = API.authorize() dialog = xbmcgui.Dialog() qr_code = qr.generate_qr(init["verification_uri"], _addon_data, "auth.png") top = [ (settings.get_localized_string(30077), "#efefefff"), (init["verification_uri"], _color), ] bottom = [ (settings.get_localized_string(30078), "#efefefff"), (init["user_code"], _color), ] qr.qr_dialog( qr_code, top_text=top, bottom_text=bottom, ) tools.execute_builtin("ShowPicture({})".format(qr_code)) expires = time.time() + init["expires_in"] while True: time.sleep(init["interval"]) token = API.authorize(init["device_code"]) pct_timeout = (time.time() - expires) / init["expires_in"] * 100 pct_timeout = 100 - int(abs(pct_timeout)) if pct_timeout >= 100: tools.execute_builtin('Action(Back)') dialog.notification(_addon_name, settings.get_localized_string(30030)) break if not tools.get_condition("Window.IsActive(slideshow)"): dialog.notification(_addon_name, settings.get_localized_string(30031)) break if "access_token" in token: tools.execute_builtin('Action(Back)') _save_oauth(token) dialog.notification(_addon_name, settings.get_localized_string(30032)) break del dialog os.remove(qr_code) if in_addon: tools.execute_builtin("RunScript({})".format(_addon_id)) else: settings.open_settings()
def build_menu(items): action_items = [] for action in items: li = xbmcgui.ListItem( settings.get_localized_string(action[0]) if type(action[0]) == int else action[0], label2=settings.get_localized_string(action[1]) if type(action[1]) == int else action[1], ) li.setArt({"thumb": os.path.join(_media_path, action[3])}) action_items.append(li) return (items, action_items)
def repo_menu(): dialog = xbmcgui.Dialog() repos = get_repos() repo_items = [] with tools.busy_dialog(): add = xbmcgui.ListItem( settings.get_localized_string(30002), label2=settings.get_localized_string(30056), ) add.setArt({"thumb": os.path.join(_media_path, "plus.png")}) repo_items.append(add) repo_defs = sorted( repos.values(), key=lambda b: b.get("timestamp", 0) if _sort_repos else b.get("name"), reverse=True, ) for repo in repo_defs: user = repo["user"] repo_name = repo["repo_name"] name = repo["name"] plugin_id = repo["plugin_id"] li = xbmcgui.ListItem( name, label2="{} - ".format(repo_name) + settings.get_localized_string(30049).format(user), ) if not _compact: li.setArt({"thumb": get_icon(user, repo_name, plugin_id)}) repo_items.append(li) selection = dialog.select(settings.get_localized_string(30011), repo_items, useDetails=not _compact) if selection == -1: dialog.notification(_addon_name, settings.get_localized_string(30023)) del dialog return None else: del dialog if selection == 0: add_repository() else: manage_menu(repo_defs[selection - 1])
def _prompt_for_update(key): dialog = xbmcgui.Dialog() if dialog.yesno(_addon_name, settings.get_localized_string(30053)): tools.execute_builtin("RunScript({},action=update_addon,id={})".format( _addon_id, key)) del dialog
def manage_menu(repo): actions = tools.build_menu([ (30000, 30054, update_addon.update_menu, "update.png", { "repo": repo }), ( 30001, 30055, raise_issue.raise_issue, "issue.png", { "selection": repo }, ), (30095, 30096, _exclude_filter, "xor.png", { "repo": repo }), (30003, 30057, remove_repository, "minus.png", { "repo": repo }), ]) _update_repo(repo, timestamp=time.time()) dialog = xbmcgui.Dialog() selection = dialog.select(settings.get_localized_string(30004), actions[1], useDetails=not _compact) del dialog if selection > -1: if len(actions[0][selection]) == 4: actions[0][selection][2]() elif len(actions[0][selection]) == 5: actions[0][selection][2](**actions[0][selection][4])
def _add_repo(user, repo, name, plugin_id, timestamp=None, update=False, path=None): dialog = xbmcgui.Dialog() key = user + "-" + plugin_id addon_def = { key: { "user": user, "repo_name": repo, "name": name, "plugin_id": plugin_id, "exclude_items": [], "timestamp": timestamp or time.time(), } } filename = key + ".json" tools.create_folder(_json_path) tools.write_to_file( os.path.join(_json_path, filename) if path is None else path, json.dumps(addon_def, indent=4), ) if not update: dialog.notification(_addon_name, settings.get_localized_string(30025)) _prompt_for_update(key) del dialog
def _format_issue(title, description, log_key): log_desc = "{}\n\n{}\n\nLog File - {}".format( settings.get_localized_string(30012).format(_addon_name), description, logging.log_url(log_key), ) return {"title": title, "body": log_desc}
def _check_repo(user, repo): dialog = xbmcgui.Dialog() can_get = API.get("repos/{}/{}".format(user, repo)) if not can_get.ok: dialog.ok(_addon_name, settings.get_localized_string(30024)) del dialog return False return True
def _log_dialog(log_key): url = log_url(log_key) copied = tools.copy2clip(url) qr_code = qr.generate_qr(url, _addon_data, "{}.png".format(log_key)) top = [(settings.get_localized_string(30068), "#efefefff"), (url, _color)] bottom = [(settings.get_localized_string(30072), "#efefefff")] if copied else [] 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)
def to_local_time(utc_time): date_long = xbmc.getRegion("datelong") time_long = xbmc.getRegion("time") format_string = settings.get_localized_string(30035).format( date_long, time_long) utc_parsed = parser.parse(utc_time) local_time = utc_parsed.astimezone(tz.tzlocal()) return local_time.strftime(format_string)
def _branch_menu(repo, repo_branches): dialog = xbmcgui.Dialog() pool = ThreadPool() with tools.busy_dialog(): for branch in repo_branches: if "message" in branch: break pool.put(repository.get_branch_info, repo, branch) branches = pool.wait_completion() default_branch, protected_branches, sorted_branches = repository.sort_branches( repo, branches) branch_items = [] for i in sorted_branches: date = tools.to_local_time(i["updated_at"]) li = xbmcgui.ListItem( "{} - ({})".format(i["branch"]["name"], color.color_string(i["sha"][:7])), label2=settings.get_localized_string(30016).format(date), ) if not _compact: art = os.path.join(_media_path, "branch.png") if i in default_branch: art = os.path.join(_media_path, "default-branch.png") elif i in protected_branches: art = os.path.join(_media_path, "protected-branch.png") li.setArt({"thumb": art}) branch_items.append(li) selection = dialog.select(settings.get_localized_string(30017), branch_items, useDetails=not _compact) if selection > -1: _commit_menu(repo, sorted_branches[selection]) else: del dialog return
def _select_log_file(): dialog = xbmcgui.Dialog() log_files = _get_log_files() filenames = [os.path.split(i)[1] for i in log_files] f = dialog.select( settings.get_localized_string(30076), filenames, preselect=filenames.index("kodi.log"), ) del dialog if f > -1: return log_files[f]
def _tag_menu(repo, repo_tags): pool = ThreadPool() commits = [] tag_items = [] tags = [] with tools.busy_dialog(): for tag in repo_tags: if "message" in tag: break tags.append((os.path.split(tag["ref"])[1], tag["object"]["sha"])) pool.put( repository.get_commit_info, repo["user"], repo["repo_name"], tag["object"]["sha"], ) commits = pool.wait_completion() sorted_commits = sorted( commits, key=lambda b: b["commit"]["author"]["date"] if "commit" in b else b["author"]["date"], reverse=True, ) for commit in sorted_commits: tag = [i[0] for i in tags if i[1] == commit["sha"]][0] label = color.color_string(tag) if label not in [i.getLabel() for i in tag_items]: li = xbmcgui.ListItem(label) if not _compact: li.setArt({"thumb": os.path.join(_media_path, "tag.png")}) tag_items.append(li) dialog = xbmcgui.Dialog() selection = dialog.select(settings.get_localized_string(30014), tag_items, useDetails=not _compact) del dialog if selection > -1: sha = sorted_commits[selection]["sha"] update_addon( repo, sorted_commits[selection], [i[0] for i in tags if i[1] == sha][0], )
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 color_picker(): select_list = [] dialog = xbmcgui.Dialog() current_color = settings.get_setting_string("general.color") for i in _color_chart: select_list.append(color_string(i, i)) color = dialog.select( "{}: {}".format(_addon_name, settings.get_localized_string(30036)), select_list, preselect=_color_chart.index(current_color), ) if color > -1: settings.set_setting_string( "general.display_color", color_string(_color_chart[color], _color_chart[color]), ) settings.set_setting_string("general.color", _color_chart[color])
def main_menu(): auth = oauth.check_auth() if auth: actions = tools.build_menu([ (30074, 30075, repository.repo_menu, "github.png"), ( 30069, 30070, logging.upload_log, "log.png", { "choose": True, "dialog": True }, ), ]) else: actions = tools.build_menu([ (30043, 30071, oauth.authorize, "github.png", { "in_addon": True }), ( 30069, 30070, logging.upload_log, "log.png", { "choose": True, "dialog": True }, ), ]) dialog = xbmcgui.Dialog() selection = dialog.select(settings.get_localized_string(30004), actions[1], useDetails=not _compact) del dialog if selection > -1: if len(actions[0][selection]) == 4: actions[0][selection][2]() elif len(actions[0][selection]) == 5: actions[0][selection][2](**actions[0][selection][4])
def _exclude_filter(repo): excludes = repo.get("exclude_items") addon_path = os.path.join(_addons, repo["plugin_id"]) dialog = xbmcgui.Dialog() if not os.path.exists(addon_path): dialog.ok( settings.get_localized_string(30095), settings.get_localized_string(30097), ) return items = sorted([ "/{}".format(i) for i in os.listdir(addon_path) if os.path.isdir(os.path.join(addon_path, i)) ]) items += sorted([ i for i in os.listdir(addon_path) if not os.path.isdir(os.path.join(addon_path, i)) ]) if excludes is not None: items += excludes selected = [] list_items = [] for i in items: li = xbmcgui.ListItem(i) if not _compact: art = "file.png" if os.path.isdir(os.path.join(addon_path, i.lstrip('/'))): art = "folder.png" else: ext = i.split('.')[-1] file = os.path.join(_media_path, "{}.png".format(ext)) if os.path.exists(file): art = "{}.png".format(ext) li.setArt({"thumb": os.path.join(_media_path, art)}) list_items.append(li) if i in excludes or i.startswith(('.', "/.")): selected.append(items.index(i)) selection = dialog.multiselect( settings.get_localized_string(30100), list_items, preselect=selected, useDetails=not _compact, ) if selection is None: dialog.notification(_addon_name, settings.get_localized_string(30101)) del dialog return else: excluded_items = [items[i] for i in selection] if excluded_items == excludes: dialog.notification(_addon_name, settings.get_localized_string(30101)) del dialog return update = dialog.yesno( settings.get_localized_string(30095), settings.get_localized_string(30098).format( color.color_string(len(selection)), color.color_string(repo["repo_name"]), ), ) if update: _update_repo(repo, exclude_items=excluded_items) delete = dialog.yesno(settings.get_localized_string(30095), settings.get_localized_string(30099)) del dialog if delete: for i in excluded_items: filepath = os.path.join(addon_path, i.lstrip('/')) if os.path.isdir(filepath): tools.remove_folder(filepath) else: tools.remove_file(filepath) else: dialog.notification(_addon_name, settings.get_localized_string(30101)) del dialog return
def update_menu(repo): action_items = [] with tools.busy_dialog(): if type(repo) != dict: repo = repository.get_repos(repo) if not repo: return repo_tags = list(API.get_tags(repo["user"], repo["repo_name"])) default_branch = API.get_default_branch(repo["user"], repo["repo_name"]) repo_branches = list( API.get_repo_branches(repo["user"], repo["repo_name"])) action_items.append(( 30088, settings.get_localized_string(30089).format( color.color_string(default_branch)), update_addon, "default-branch.png", { "repo": repo, "commit": API.get_commit(repo["user"], repo["repo_name"], default_branch), "label": default_branch, }, )) if len(repo_tags) > 0 and "message" not in repo_tags[0]: action_items.append((30084, 30094, _tag_menu, "tag.png", { "repo": repo, "repo_tags": repo_tags })) if len(repo_branches) > 1: action_items.append(( 30086, 30087, _branch_menu, "branch.png", { "repo": repo, "repo_branches": repo_branches }, )) elif len(repo_branches) == 1: action_items.append(( 30082, settings.get_localized_string(30090).format( color.color_string(default_branch)), _commit_menu, "commit.png", { "repo": repo, "branch": repository.get_branch_info(repo, default_branch), }, )) actions = tools.build_menu(action_items) dialog = xbmcgui.Dialog() selection = dialog.select(settings.get_localized_string(30004), actions[1], useDetails=not _compact) del dialog if selection > -1: if len(actions[0][selection]) == 4: actions[0][selection][2]() elif len(actions[0][selection]) == 5: actions[0][selection][2](**actions[0][selection][4])
def _commit_menu(repo, branch): pool = ThreadPool() commits = [] commit_items = [] with tools.busy_dialog(): for branch_commit in list( API.get_branch_commits(repo["user"], repo["repo_name"], branch["name"])): pool.put( _get_commit_info, repo["user"], repo["repo_name"], branch_commit["sha"], ) commits = pool.wait_completion() sorted_commits = sorted( commits, key=lambda b: b["commit"]["author"]["date"] if "commit" in b else b["author"]["date"], reverse=True, ) for commit in sorted_commits: date = tools.to_local_time(commit["commit"]["author"]["date"]) byline = settings.get_localized_string(30013).format( commit["commit"]["author"]["name"], date) if _commit_stats: stats = commit['stats'] adds = stats.get("additions", 0) deletes = stats.get("deletions", 0) add_text = (color.color_string("[B]+[/B] {}".format(adds), "springgreen") if adds > 0 else "[B]+[/B] {}".format(adds)) delete_text = (color.color_string( "[B]-[/B] {}".format(deletes), "crimson") if deletes > 0 else "[B]-[/B] {}".format(deletes)) byline = "{} {}: ".format(add_text, delete_text) + byline li = xbmcgui.ListItem( "{} - {}".format( color.color_string(commit["sha"][:7]), commit["commit"]["message"].replace("\n", "; "), ), label2=byline, ) if not _compact: art = os.path.join(_media_path, "commit.png") if "pull" in commit["commit"]["message"]: art = os.path.join(_media_path, "pull.png") elif "merge" in commit["commit"]["message"]: art = os.path.join(_media_path, "merge.png") li.setArt({"thumb": art}) commit_items.append(li) dialog = xbmcgui.Dialog() selection = dialog.select(settings.get_localized_string(30014), commit_items, useDetails=not _compact) if selection > -1: del dialog update_addon(repo, sorted_commits[selection], sorted_commits[selection]["sha"][:7]) else: dialog.notification(_addon_name, settings.get_localized_string(30015)) del dialog
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
def force_auth(): dialog = xbmcgui.Dialog() if not _access_token: if dialog.yesno(_addon_name, settings.get_localized_string(30005)): authorize(True) del dialog
def update_addon(repo, commit=None, label=None): dialog = xbmcgui.Dialog() tools.cleanup_old_files() if not dialog.yesno( settings.get_localized_string(30000), settings.get_localized_string(30018).format( color.color_string(repo["name"]), color.color_string(label), ), ): dialog.notification(_addon_name, settings.get_localized_string(30015)) del dialog return progress = xbmcgui.DialogProgress() progress.create( _addon_name, settings.get_localized_string(30019).format( color.color_string(repo["name"])), ) progress.update(0) location = _get_zip_file(repo["user"], repo["repo_name"], sha=commit["sha"]) if location: progress.update( 25, settings.get_localized_string(30020).format( color.color_string(repo["name"])), ) extensions = repository.get_extensions(repo["user"], repo["repo_name"]) plugin_id = repo["plugin_id"] exists = _exists(plugin_id) is_service = "service" in extensions is_current_skin = "skin" in extensions and tools.get_current_skin( ) == plugin_id if is_service: _set_enabled(plugin_id, False, exists) tools.remove_folder(os.path.join(_addons, plugin_id)) _extract_addon(location, repo) progress.update( 50, settings.get_localized_string(30062).format( color.color_string(repo["name"])), ) if _add_webpdb: _add_webpdb_to_addon(plugin_id) _rewrite_kodi_dependency_versions(plugin_id) _update_addon_version( plugin_id, commit["sha"], ) failed_deps = [] if _dependencies: progress.update( 75, settings.get_localized_string(30063).format( color.color_string(repo["name"])), ) failed_deps = _install_deps(plugin_id) _set_enabled(plugin_id, True, exists) progress.update( 100, settings.get_localized_string(30067 if not exists else 30021)) if failed_deps: dialog.ok( _addon_name, settings.get_localized_string(30064).format( ", ".join(failed_deps), repo["name"]), ) if not exists: tools.reload_profile() elif is_current_skin: tools.reload_skin() progress.close() del progress del dialog