Улучшение кода main.py и конфигурации
This commit is contained in:
+23
-11
@@ -1,11 +1,25 @@
|
|||||||
{
|
{
|
||||||
|
"base": {
|
||||||
"tools": {
|
"tools": {
|
||||||
"apktool_jar_url": "https://bitbucket.org/iBotPeaches/apktool/downloads/apktool_2.12.0.jar",
|
"apktool_jar_url": "https://bitbucket.org/iBotPeaches/apktool/downloads/apktool_2.12.0.jar",
|
||||||
"apktool_wrapper_url": "https://raw.githubusercontent.com/iBotPeaches/Apktool/master/scripts/linux/apktool"
|
"apktool_wrapper_url": "https://raw.githubusercontent.com/iBotPeaches/Apktool/master/scripts/linux/apktool"
|
||||||
},
|
},
|
||||||
"new_package_name": "com.wowlikon.anixart2",
|
"xml_ns": {
|
||||||
"server": "https://anixarty.wowlikon.tech/modding",
|
"android": "http://schemas.android.com/apk/res/android",
|
||||||
"theme": {
|
"app": "http://schemas.android.com/apk/res-auto"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"patches": {
|
||||||
|
"package_name": {
|
||||||
|
"new_package_name": "com.wowlikon.anixart"
|
||||||
|
},
|
||||||
|
"cleanup": {
|
||||||
|
"keep_dirs": ["META-INF", "kotlin"]
|
||||||
|
},
|
||||||
|
"change_server": {
|
||||||
|
"server": "https://anixarty.wowlikon.tech/modding"
|
||||||
|
},
|
||||||
|
"color_theme": {
|
||||||
"colors": {
|
"colors": {
|
||||||
"primary": "#ccff00",
|
"primary": "#ccff00",
|
||||||
"secondary": "#ffffd700",
|
"secondary": "#ffffd700",
|
||||||
@@ -18,10 +32,11 @@
|
|||||||
"to": "#ffccff00"
|
"to": "#ffccff00"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cleanup": {
|
"custom_speed": {
|
||||||
"keep_dirs": ["META-INF", "kotlin"]
|
"speeds": [9.0]
|
||||||
},
|
},
|
||||||
"settings_urls": {
|
"settings_urls": {
|
||||||
|
"menu": {
|
||||||
"Мы в социальных сетях": [
|
"Мы в социальных сетях": [
|
||||||
{
|
{
|
||||||
"title": "wowlikon",
|
"title": "wowlikon",
|
||||||
@@ -54,10 +69,7 @@
|
|||||||
"icon_space_reserved": "false"
|
"icon_space_reserved": "false"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
}
|
||||||
"xml_ns": {
|
}
|
||||||
"android": "http://schemas.android.com/apk/res/android",
|
}
|
||||||
"app": "http://schemas.android.com/apk/res-auto"
|
|
||||||
},
|
|
||||||
"speeds": [9.0]
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,230 +1,220 @@
|
|||||||
import os
|
from pathlib import Path
|
||||||
import sys
|
from typing import List
|
||||||
import json
|
|
||||||
import yaml
|
import httpx
|
||||||
import requests
|
import typer
|
||||||
import argparse
|
|
||||||
import colorama
|
|
||||||
import importlib
|
import importlib
|
||||||
import traceback
|
import traceback
|
||||||
import subprocess
|
import yaml
|
||||||
from tqdm import tqdm
|
|
||||||
|
|
||||||
def init() -> dict:
|
from pydantic import BaseModel, ValidationError
|
||||||
for directory in ["original", "modified", "patches", "tools", "decompiled"]:
|
from plumbum import local, ProcessExecutionError
|
||||||
if not os.path.exists(directory):
|
from rich.console import Console
|
||||||
os.makedirs(directory)
|
from rich.progress import Progress
|
||||||
|
from rich.prompt import Prompt
|
||||||
|
from rich.table import Table
|
||||||
|
|
||||||
with open("./config.json", "r") as config_file:
|
# --- Paths ---
|
||||||
conf = json.load(config_file)
|
TOOLS = Path("tools")
|
||||||
|
ORIGINAL = Path("original")
|
||||||
|
MODIFIED = Path("modified")
|
||||||
|
DECOMPILED = Path("decompiled")
|
||||||
|
PATCHES = Path("patches")
|
||||||
|
|
||||||
if not os.path.exists("./tools/apktool.jar"):
|
console = Console()
|
||||||
|
app = typer.Typer()
|
||||||
|
|
||||||
|
|
||||||
|
# ======================= CONFIG =========================
|
||||||
|
class ToolsConfig(BaseModel):
|
||||||
|
apktool_jar_url: str
|
||||||
|
apktool_wrapper_url: str
|
||||||
|
|
||||||
|
class XmlNamespaces(BaseModel):
|
||||||
|
android: str
|
||||||
|
app: str
|
||||||
|
|
||||||
|
class BaseSection(BaseModel):
|
||||||
|
tools: ToolsConfig
|
||||||
|
xml_ns: XmlNamespaces
|
||||||
|
|
||||||
|
class Config(BaseModel):
|
||||||
|
base: BaseSection
|
||||||
|
patches: dict
|
||||||
|
|
||||||
|
|
||||||
|
def load_config() -> Config:
|
||||||
try:
|
try:
|
||||||
print("Скачивание Apktool...")
|
return Config.model_validate_json(Path("config.json").read_text())
|
||||||
jar_response = requests.get(conf["tools"]["apktool_jar_url"], stream=True)
|
except FileNotFoundError:
|
||||||
jar_path = "tools/apktool.jar"
|
console.print("[red]Файл config.json не найден")
|
||||||
with open(jar_path, "wb") as f:
|
raise typer.Exit(1)
|
||||||
for chunk in jar_response.iter_content(chunk_size=8192):
|
except ValidationError as e:
|
||||||
|
console.print("[red]Ошибка валидации config.json:", e)
|
||||||
|
raise typer.Exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
# ======================= UTILS =========================
|
||||||
|
def ensure_dirs():
|
||||||
|
for d in [TOOLS, ORIGINAL, MODIFIED, DECOMPILED, PATCHES]:
|
||||||
|
d.mkdir(exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
|
def run(cmd: List[str], hide_output=True):
|
||||||
|
prog = local[cmd[0]][cmd[1:]]
|
||||||
|
try:
|
||||||
|
prog() if hide_output else prog & FG
|
||||||
|
except ProcessExecutionError as e:
|
||||||
|
console.print(f"[red]Ошибка при выполнении команды: {' '.join(cmd)}")
|
||||||
|
console.print(e.stderr)
|
||||||
|
raise typer.Exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def download(url: str, dest: Path):
|
||||||
|
console.print(f"[cyan]Скачивание {url} → {dest.name}")
|
||||||
|
with httpx.stream("GET", url, timeout=30) as r:
|
||||||
|
r.raise_for_status()
|
||||||
|
with open(dest, "wb") as f:
|
||||||
|
for chunk in r.iter_bytes():
|
||||||
f.write(chunk)
|
f.write(chunk)
|
||||||
|
|
||||||
wrapper_response = requests.get(conf["tools"]["apktool_wrapper_url"])
|
|
||||||
wrapper_path = "tools/apktool"
|
|
||||||
with open(wrapper_path, "w") as f:
|
|
||||||
f.write(wrapper_response.text)
|
|
||||||
os.chmod(wrapper_path, 0o755)
|
|
||||||
|
|
||||||
except Exception as e:
|
# ======================= INIT =========================
|
||||||
print(f"Ошибка при скачивании Apktool: {e}")
|
@app.command()
|
||||||
exit(1)
|
def init():
|
||||||
|
"""Создание директорий и скачивание инструментов"""
|
||||||
|
ensure_dirs()
|
||||||
|
conf = load_config()
|
||||||
|
|
||||||
|
if not (TOOLS / "apktool.jar").exists():
|
||||||
|
download(conf.base.tools.apktool_jar_url, TOOLS / "apktool.jar")
|
||||||
|
wrapper = httpx.get(conf.base.tools.apktool_wrapper_url, timeout=30).text
|
||||||
|
(TOOLS / "apktool").write_text(wrapper, encoding="utf-8")
|
||||||
|
(TOOLS / "apktool").chmod(0o755)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(
|
local["java"]["-version"]()
|
||||||
["java", "-version"], capture_output=True, text=True, check=True
|
console.print("[green]Java найдена")
|
||||||
)
|
except ProcessExecutionError:
|
||||||
|
console.print("[red]Java не установлена")
|
||||||
version_line = result.stderr.splitlines()[0]
|
raise typer.Exit(1)
|
||||||
if "1.8" in version_line or any(f"{i}." in version_line for i in range(9, 100)):
|
|
||||||
print("Java 8 или более поздняя версия установлена.")
|
|
||||||
else:
|
|
||||||
print("Java 8 или более поздняя версия не установлена.")
|
|
||||||
sys.exit(1)
|
|
||||||
except subprocess.CalledProcessError:
|
|
||||||
print("Java не установлена. Установите Java 8 или более позднюю версию.")
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
return conf
|
|
||||||
|
|
||||||
def select_apk() -> str:
|
|
||||||
apks = []
|
|
||||||
for file in os.listdir("original"):
|
|
||||||
if file.endswith(".apk") and os.path.isfile(os.path.join("original", file)):
|
|
||||||
apks.append(file)
|
|
||||||
|
|
||||||
if not apks:
|
|
||||||
print("Нет файлов .apk в текущей директории")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
if len(apks) == 1:
|
|
||||||
apk = apks[0]
|
|
||||||
print(f"Выбран файл {apk}")
|
|
||||||
return apk
|
|
||||||
|
|
||||||
while True:
|
|
||||||
print("Выберете файл для модификации")
|
|
||||||
for index, apk in enumerate(apks):
|
|
||||||
print(f"{index + 1}. {apk}")
|
|
||||||
print("0. Exit")
|
|
||||||
|
|
||||||
try:
|
|
||||||
selected_index = int(input("\nВведите номер файла: "))
|
|
||||||
if selected_index == 0:
|
|
||||||
sys.exit(0)
|
|
||||||
elif selected_index > len(apks):
|
|
||||||
print("Неверный номер файла")
|
|
||||||
else:
|
|
||||||
apk = apks[selected_index - 1]
|
|
||||||
print(f"Выбран файл {apk}")
|
|
||||||
return apk
|
|
||||||
except ValueError:
|
|
||||||
print("Неверный формат ввода")
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
print("Прервано пользователем")
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
|
|
||||||
def decompile_apk(apk: str):
|
|
||||||
print("Декомпилируем apk...")
|
|
||||||
try:
|
|
||||||
result = subprocess.run(
|
|
||||||
"tools/apktool d -f -o decompiled " + os.path.join("original", apk),
|
|
||||||
shell=True,
|
|
||||||
check=True,
|
|
||||||
text=True,
|
|
||||||
stdout=subprocess.DEVNULL,
|
|
||||||
stderr=subprocess.PIPE,
|
|
||||||
)
|
|
||||||
except subprocess.CalledProcessError as e:
|
|
||||||
print("Ошибка при выполнении команды:")
|
|
||||||
print(e.stderr)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
def compile_apk(apk: str):
|
|
||||||
print("Компилируем apk...")
|
|
||||||
try:
|
|
||||||
subprocess.run(
|
|
||||||
"tools/apktool b decompiled -o " + os.path.join("modified", apk),
|
|
||||||
shell=True,
|
|
||||||
check=True,
|
|
||||||
text=True,
|
|
||||||
stdout=subprocess.DEVNULL,
|
|
||||||
stderr=subprocess.PIPE,
|
|
||||||
)
|
|
||||||
subprocess.run(
|
|
||||||
"zipalign -v 4 " + os.path.join("modified", apk) + " " + os.path.join("modified", apk.replace(".apk", "-aligned.apk")),
|
|
||||||
shell=True,
|
|
||||||
check=True,
|
|
||||||
text=True,
|
|
||||||
stdout=subprocess.DEVNULL,
|
|
||||||
stderr=subprocess.PIPE,
|
|
||||||
)
|
|
||||||
subprocess.run(
|
|
||||||
"apksigner sign " +
|
|
||||||
"--v1-signing-enabled false " +
|
|
||||||
"--v2-signing-enabled true " +
|
|
||||||
"--v3-signing-enabled true " +
|
|
||||||
"--ks keystore.jks " +
|
|
||||||
"--ks-pass file:keystore.pass " +
|
|
||||||
"--out " + os.path.join("modified", apk.replace(".apk", "-mod.apk")) +
|
|
||||||
" " + os.path.join("modified", apk.replace(".apk", "-aligned.apk")),
|
|
||||||
shell=True,
|
|
||||||
check=True,
|
|
||||||
text=True,
|
|
||||||
stdout=subprocess.DEVNULL,
|
|
||||||
stderr=subprocess.PIPE,
|
|
||||||
)
|
|
||||||
title = "anixart mod "
|
|
||||||
with open('./decompiled/apktool.yml') as f:
|
|
||||||
package = yaml.safe_load(f)
|
|
||||||
title += ' '.join([f'{k}: {v}' for k, v in package['versionInfo'].items()])
|
|
||||||
with open("./modified/report.log", "w") as log_file:
|
|
||||||
log_file.write(title+'\n')
|
|
||||||
log_file.write("\n".join([f"{patch.name}: {'applied' if patch.applied else 'failed'}" for patch in patches]))
|
|
||||||
except subprocess.CalledProcessError as e:
|
|
||||||
print("Ошибка при выполнении команды:")
|
|
||||||
print(e.stderr)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
|
# ======================= PATCHING =========================
|
||||||
class Patch:
|
class Patch:
|
||||||
def __init__(self, name, pkg):
|
def __init__(self, name: str, module):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.package = pkg
|
self.module = module
|
||||||
self.applied = False
|
self.applied = False
|
||||||
try:
|
self.priority = getattr(module, "priority", 0)
|
||||||
self.priority = pkg.priority
|
|
||||||
except AttributeError:
|
|
||||||
self.priority = 0
|
|
||||||
|
|
||||||
def apply(self, conf: dict) -> bool:
|
def apply(self, conf: dict) -> bool:
|
||||||
try:
|
try:
|
||||||
self.applied = self.package.apply(conf)
|
self.applied = bool(self.module.apply(conf))
|
||||||
return True
|
return self.applied
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Ошибка при применении патча {self.name}: {e}")
|
console.print(f"[red]Ошибка в патче {self.name}: {e}")
|
||||||
print(type(e), e.args)
|
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
parser = argparse.ArgumentParser(
|
|
||||||
description="Автоматический патчер anixart"
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument("-v", "--verbose",
|
def select_apk() -> Path:
|
||||||
action="store_true",
|
apks = [f for f in ORIGINAL.glob("*.apk")]
|
||||||
help="Выводить подробные сообщения")
|
if not apks:
|
||||||
|
console.print("[red]Нет apk-файлов в папке original")
|
||||||
|
raise typer.Exit(1)
|
||||||
|
|
||||||
parser.add_argument("-f", "--force",
|
if len(apks) == 1:
|
||||||
action="store_true",
|
console.print(f"[green]Выбран {apks[0].name}")
|
||||||
help="Принудительно собрать APK")
|
return apks[0]
|
||||||
|
|
||||||
args = parser.parse_args()
|
options = {str(i): apk for i, apk in enumerate(apks, 1)}
|
||||||
|
for k, v in options.items():
|
||||||
|
console.print(f"{k}. {v.name}")
|
||||||
|
|
||||||
conf = init()
|
choice = Prompt.ask("Выберите номер", choices=list(options.keys()))
|
||||||
|
return options[choice]
|
||||||
|
|
||||||
|
|
||||||
|
def decompile(apk: Path):
|
||||||
|
console.print("[yellow]Декомпиляция apk...")
|
||||||
|
run(["java", "-jar", str(TOOLS / "apktool.jar"), "d", "-f", "-o", str(DECOMPILED), str(apk)])
|
||||||
|
|
||||||
|
|
||||||
|
def compile(apk: Path, patches: List[Patch]):
|
||||||
|
console.print("[yellow]Сборка apk...")
|
||||||
|
out_apk = MODIFIED / apk.name
|
||||||
|
aligned = out_apk.with_stem(out_apk.stem + "-aligned")
|
||||||
|
signed = out_apk.with_stem(out_apk.stem + "-mod")
|
||||||
|
|
||||||
|
run(["java", "-jar", str(TOOLS / "apktool.jar"), "b", str(DECOMPILED), "-o", str(out_apk)])
|
||||||
|
run(["zipalign", "-v", "4", str(out_apk), str(aligned)])
|
||||||
|
run([
|
||||||
|
"apksigner", "sign",
|
||||||
|
"--v1-signing-enabled", "false",
|
||||||
|
"--v2-signing-enabled", "true",
|
||||||
|
"--v3-signing-enabled", "true",
|
||||||
|
"--ks", "keystore.jks",
|
||||||
|
"--ks-pass", "file:keystore.pass",
|
||||||
|
"--out", str(signed),
|
||||||
|
str(aligned)
|
||||||
|
])
|
||||||
|
|
||||||
|
with open(DECOMPILED / "apktool.yml", encoding="utf-8") as f:
|
||||||
|
meta = yaml.safe_load(f)
|
||||||
|
version_str = " ".join(f"{k}:{v}" for k, v in meta.get("versionInfo", {}).items())
|
||||||
|
|
||||||
|
with open(MODIFIED / "report.log", "w", encoding="utf-8") as f:
|
||||||
|
f.write(f"anixart mod {version_str}\n")
|
||||||
|
for p in patches:
|
||||||
|
f.write(f"{p.name}: {'applied' if p.applied else 'failed'}\n")
|
||||||
|
|
||||||
|
|
||||||
|
@app.command()
|
||||||
|
def build(
|
||||||
|
force: bool = typer.Option(False, "--force", "-f", help="Принудительная сборка"),
|
||||||
|
verbose: bool = typer.Option(False, "--verbose", "-v", help="Подробный вывод"),
|
||||||
|
):
|
||||||
|
"""Декомпиляция, патчи и сборка apk"""
|
||||||
|
conf = load_config().model_dump()
|
||||||
apk = select_apk()
|
apk = select_apk()
|
||||||
patch = decompile_apk(apk)
|
decompile(apk)
|
||||||
|
|
||||||
if args.verbose: conf["verbose"] = True
|
patch_settings = conf.get("patches", {})
|
||||||
|
patch_objs: List[Patch] = []
|
||||||
|
|
||||||
patches = []
|
for f in PATCHES.glob("*.py"):
|
||||||
for filename in os.listdir("patches/"):
|
if f.name.startswith("todo_") or f.name == "__init__.py":
|
||||||
if filename.endswith(".py") and filename != "__init__.py" and not filename.startswith("todo_"):
|
continue
|
||||||
module_name = filename[:-3]
|
name = f.stem
|
||||||
module = importlib.import_module(f"patches.{module_name}")
|
settings = patch_settings.get(name, {})
|
||||||
patches.append(Patch(module_name, module))
|
if not settings.get("enable", True):
|
||||||
|
console.print(f"[yellow]≫ Пропускаем {name}")
|
||||||
|
continue
|
||||||
|
module = importlib.import_module(f"patches.{name}")
|
||||||
|
patch_objs.append(Patch(name, module))
|
||||||
|
|
||||||
patches.sort(key=lambda x: x.package.priority, reverse=True)
|
patch_objs.sort(key=lambda p: p.priority, reverse=True)
|
||||||
|
|
||||||
for patch in tqdm(patches, colour="green", desc="Применение патчей"):
|
console.print("[cyan]Применение патчей")
|
||||||
tqdm.write(f"Применение патча: {patch.name}")
|
with Progress() as progress:
|
||||||
patch.apply(conf)
|
task = progress.add_task("Патчи", total=len(patch_objs))
|
||||||
|
for p in patch_objs:
|
||||||
|
ok = p.apply(
|
||||||
|
patch_settings.get(p.name, {}) | conf.get("base", {})
|
||||||
|
)
|
||||||
|
progress.console.print(f"{'✔' if ok else '✘'} {p.name}")
|
||||||
|
progress.advance(task)
|
||||||
|
|
||||||
statuses = {}
|
successes = sum(p.applied for p in patch_objs)
|
||||||
for patch in patches:
|
if successes == len(patch_objs):
|
||||||
statuses[patch.name] = patch.applied
|
compile(apk, patch_objs)
|
||||||
marker = colorama.Fore.GREEN + "✔" if patch.applied else colorama.Fore.RED + "✘"
|
elif successes > 0 and (force or Prompt.ask("Продолжить сборку?", choices=["y", "n"]) == "y"):
|
||||||
print(f"{marker}{colorama.Style.RESET_ALL} {patch.name}")
|
compile(apk, patch_objs)
|
||||||
|
|
||||||
if all(statuses.values()):
|
|
||||||
print(f"{colorama.Fore.GREEN}Все патчи успешно применены{colorama.Style.RESET_ALL}")
|
|
||||||
compile_apk(apk)
|
|
||||||
elif any(statuses.values()):
|
|
||||||
print(f"{colorama.Fore.YELLOW}⚠{colorama.Style.RESET_ALL} Некоторые патчи не были успешно применены")
|
|
||||||
if args.force or input("Продолжить? (y/n): ").lower() == "y":
|
|
||||||
compile_apk(apk)
|
|
||||||
else:
|
else:
|
||||||
print(colorama.Fore.RED + "Операция отменена" + colorama.Style.RESET_ALL)
|
console.print("[red]Сборка отменена")
|
||||||
else:
|
raise typer.Exit(1)
|
||||||
print(f"{colorama.Fore.RED}Ни один патч не был успешно применен{colorama.Style.RESET_ALL}")
|
|
||||||
sys.exit(1)
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app()
|
||||||
|
|||||||
+1
-1
@@ -15,7 +15,7 @@ def apply(config: dict) -> bool:
|
|||||||
if config.get("verbose", False):
|
if config.get("verbose", False):
|
||||||
tqdm.write(f'Удалён файл: {item_path}')
|
tqdm.write(f'Удалён файл: {item_path}')
|
||||||
elif os.path.isdir(item_path):
|
elif os.path.isdir(item_path):
|
||||||
if item not in config["cleanup"]["keep_dirs"]:
|
if item not in config["keep_dirs"]:
|
||||||
shutil.rmtree(item_path)
|
shutil.rmtree(item_path)
|
||||||
if config.get("verbose", False):
|
if config.get("verbose", False):
|
||||||
tqdm.write(f'Удалена папка: {item_path}')
|
tqdm.write(f'Удалена папка: {item_path}')
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ from utils.public import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
def apply(config: dict) -> bool:
|
def apply(config: dict) -> bool:
|
||||||
main_color = config["theme"]["colors"]["primary"]
|
main_color = config["colors"]["primary"]
|
||||||
splash_color = config["theme"]["colors"]["secondary"]
|
splash_color = config["colors"]["secondary"]
|
||||||
gradient_angle = config["theme"]["gradient"]["angle"]
|
gradient_angle = config["gradient"]["angle"]
|
||||||
gradient_from = config["theme"]["gradient"]["from"]
|
gradient_from = config["gradient"]["from"]
|
||||||
gradient_to = config["theme"]["gradient"]["to"]
|
gradient_to = config["gradient"]["to"]
|
||||||
|
|
||||||
# No connection alert coolor
|
# No connection alert coolor
|
||||||
with open("./decompiled/assets/no_connection.html", "r", encoding="utf-8") as file:
|
with open("./decompiled/assets/no_connection.html", "r", encoding="utf-8") as file:
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ def rename_dir(src, dst):
|
|||||||
|
|
||||||
|
|
||||||
def apply(config: dict) -> bool:
|
def apply(config: dict) -> bool:
|
||||||
assert config["new_package_name"] is not None, "new_package_name is not configured"
|
|
||||||
|
|
||||||
for root, dirs, files in os.walk("./decompiled"):
|
for root, dirs, files in os.walk("./decompiled"):
|
||||||
for filename in files:
|
for filename in files:
|
||||||
file_path = os.path.join(root, filename)
|
file_path = os.path.join(root, filename)
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ def apply(config: dict) -> bool:
|
|||||||
# Insert new PreferenceCategory before the last element
|
# Insert new PreferenceCategory before the last element
|
||||||
last = root[-1] # last element
|
last = root[-1] # last element
|
||||||
pos = root.index(last)
|
pos = root.index(last)
|
||||||
for section, items in config["settings_urls"].items():
|
for section, items in config["menu"].items():
|
||||||
root.insert(pos, make_category(config["xml_ns"], section, items))
|
root.insert(pos, make_category(config["xml_ns"], section, items))
|
||||||
pos += 1
|
pos += 1
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
typer[all]>=0.9.0
|
||||||
|
rich>=13.0.0
|
||||||
|
httpx>=1.2.0
|
||||||
|
pydantic>=2.2.0
|
||||||
|
plumbum>=1.8.0
|
||||||
|
lxml>=4.9.3
|
||||||
|
PyYAML>=6.0
|
||||||
|
tqdm>=4.66.0
|
||||||
Reference in New Issue
Block a user