Рефакторинг патчей, реализация Список патчей:

settings_urls: ✔ enabled
  disable_ad: ✔ enabled
  disable_beta_banner: ✔ enabled
  insert_new: ✔ enabled
  color_theme: ✔ enabled
  change_server: ✘ disabled
  package_name: ✔ enabled
  replace_navbar: ✔ enabled
  compress: ✔ enabled, обновление описаний
This commit is contained in:
2025-09-20 23:00:00 +03:00
parent 66336f3a5c
commit 5ff882a8d5
14 changed files with 142 additions and 60 deletions
+1 -1
View File
@@ -118,7 +118,7 @@ def info(patch_name: str = ""):
"""Вывод информации о патче""" """Вывод информации о патче"""
conf = load_config().model_dump() conf = load_config().model_dump()
if patch_name: if patch_name:
patch = Patch(patch_name, __import__(f"patcher.patches.{patch_name}")) patch = Patch(patch_name, __import__(f"patches.{patch_name}", fromlist=['']))
console.print(f"[green]Информация о патче {patch.name}:") console.print(f"[green]Информация о патче {patch.name}:")
console.print(f" [yellow]Приоритет: {patch.priority}") console.print(f" [yellow]Приоритет: {patch.priority}")
console.print(f" [yellow]Описание: {patch.module.__doc__}") console.print(f" [yellow]Описание: {patch.module.__doc__}")
+11
View File
@@ -2,25 +2,32 @@
Заменяет сервер api Заменяет сервер api
"change_server": { "change_server": {
"enabled": true,
"server": "https://anixarty.wowlikon.tech/modding" "server": "https://anixarty.wowlikon.tech/modding"
} }
""" """
priority = 0 priority = 0
# imports
import json import json
import requests import requests
from tqdm import tqdm from tqdm import tqdm
# Patch
def apply(config: dict) -> bool: def apply(config: dict) -> bool:
response = requests.get(config['server']) response = requests.get(config['server'])
assert response.status_code == 200, f"Failed to fetch data {response.status_code} {response.text}" assert response.status_code == 200, f"Failed to fetch data {response.status_code} {response.text}"
new_api = json.loads(response.text) new_api = json.loads(response.text)
for item in new_api['modifications']: for item in new_api['modifications']:
tqdm.write(f"Изменение {item['file']}") tqdm.write(f"Изменение {item['file']}")
filepath = './decompiled/smali_classes2/com/swiftsoft/anixartd/network/api/'+item['file'] filepath = './decompiled/smali_classes2/com/swiftsoft/anixartd/network/api/'+item['file']
with open(filepath, 'r') as f: with open(filepath, 'r') as f:
content = f.read() content = f.read()
with open(filepath, 'w') as f: with open(filepath, 'w') as f:
if content.count(item['src']) == 0: if content.count(item['src']) == 0:
tqdm.write(f"Не найдено {item['src']}") tqdm.write(f"Не найдено {item['src']}")
@@ -28,18 +35,22 @@ def apply(config: dict) -> bool:
tqdm.write(f"Изменение Github ссылки") tqdm.write(f"Изменение Github ссылки")
filepath = './decompiled/smali_classes2/com/swiftsoft/anixartd/utils/anixnet/GithubPagesNetFetcher.smali' filepath = './decompiled/smali_classes2/com/swiftsoft/anixartd/utils/anixnet/GithubPagesNetFetcher.smali'
with open(filepath, 'r') as f: with open(filepath, 'r') as f:
content = f.read() content = f.read()
with open(filepath, 'w') as f: with open(filepath, 'w') as f:
f.write(content.replace('const-string v1, "https://anixhelper.github.io/pages/urls.json"', f'const-string v1, "{new_api["gh"]}"')) f.write(content.replace('const-string v1, "https://anixhelper.github.io/pages/urls.json"', f'const-string v1, "{new_api["gh"]}"'))
content = "" content = ""
tqdm.write("Удаление динамического выбора сервера") tqdm.write("Удаление динамического выбора сервера")
filepath = './decompiled/smali_classes2/com/swiftsoft/anixartd/DaggerApp_HiltComponents_SingletonC$SingletonCImpl$SwitchingProvider.smali' filepath = './decompiled/smali_classes2/com/swiftsoft/anixartd/DaggerApp_HiltComponents_SingletonC$SingletonCImpl$SwitchingProvider.smali'
with open(filepath, 'r') as f: with open(filepath, 'r') as f:
for line in f.readlines(): for line in f.readlines():
if "addInterceptor" in line: continue if "addInterceptor" in line: continue
content += line content += line
with open(filepath, 'w') as f: with open(filepath, 'w') as f:
f.write(content) f.write(content)
+5 -1
View File
@@ -2,6 +2,7 @@
Изменяет цветовую тему приложения и иконку Изменяет цветовую тему приложения и иконку
"color_theme": { "color_theme": {
"enabled": true,
"colors": { "colors": {
"primary": "#ccff00", "primary": "#ccff00",
"secondary": "#ffffd700", "secondary": "#ffffd700",
@@ -15,10 +16,11 @@
} }
} }
""" """
priority = 0 priority = 0
# imports
from lxml import etree from lxml import etree
from utils.public import ( from utils.public import (
insert_after_public, insert_after_public,
insert_after_color, insert_after_color,
@@ -26,6 +28,7 @@ from utils.public import (
) )
# Patch
def apply(config: dict) -> bool: def apply(config: dict) -> bool:
main_color = config["colors"]["primary"] main_color = config["colors"]["primary"]
splash_color = config["colors"]["secondary"] splash_color = config["colors"]["secondary"]
@@ -103,6 +106,7 @@ def apply(config: dict) -> bool:
change_color("accent_alpha_20", main_color[0]+'33'+main_color[1:]) change_color("accent_alpha_20", main_color[0]+'33'+main_color[1:])
change_color("accent_alpha_50", main_color[0]+'80'+main_color[1:]) change_color("accent_alpha_50", main_color[0]+'80'+main_color[1:])
change_color("accent_alpha_70", main_color[0]+'b3'+main_color[1:]) change_color("accent_alpha_70", main_color[0]+'b3'+main_color[1:])
change_color("colorAccent", main_color[0]+'ff'+main_color[1:]) change_color("colorAccent", main_color[0]+'ff'+main_color[1:])
change_color("link_color", main_color[0]+'ff'+main_color[1:]) change_color("link_color", main_color[0]+'ff'+main_color[1:])
change_color("link_color_alpha_70", main_color[0]+'b3'+main_color[1:]) change_color("link_color_alpha_70", main_color[0]+'b3'+main_color[1:])
-30
View File
@@ -1,30 +0,0 @@
# Compress
Патч удаляет ненужные ресурсы что-бы уменьшить размер АПК
## настройки (compress в config.json)
- remove_unknown_files: true/false - удаляет файлы из директории decompiled/unknown
- remove_unknown_files_keep_dirs: list[str] - оставляет указанные директории в decompiled/unknown
- remove_debug_lines: true/false - удаляет строки `.line n` из декомпилированных smali файлов использованные для дебага
- remove_AI_voiceover: true/false - заменяет ИИ озвучку маскота аниксы пустыми mp3 файлами
- compress_png_files: true/false - сжимает PNG в директории decompiled/res
- remove_drawable_files: true/false - удаляет неиспользованные drawable-* из директории decompiled/res
- remove_language_files: true/false - удаляет все языки кроме русского и английского
## efficiency
Проверено с версией 9.0 Beta 7
разница = оригинальный размер апк - патченный размер апк, не учитывая другие патчи
| Настройка | Размер файла | Разница | % |
| :----------- | :-------------------: | :-----------------: | :-: |
| None | 17092 bytes - 17.1 MB | - | - |
| Compress PNG | 17072 bytes - 17.1 MB | 20 bytes - 0.0 MB | 0.11% |
| Remove files | 17020 bytes - 17.0 MB | 72 bytes - 0.1 MB | 0.42% |
| Remove draws | 16940 bytes - 16.9 MB | 152 bytes - 0.2 MB | 0.89% |
| Remove lines | 16444 bytes - 16.4 MB | 648 bytes - 0.7 MB | 3.79% |
| Remove ai vo | 15812 bytes - 15.8 MB | 1280 bytes - 1.3 MB | 7.49% |
| Remove langs | 15764 bytes - 15.7 MB | 1328 bytes - 1.3 MB | 7.76% |
| Все включены | 13592 bytes - 13.6 MB | 3500 bytes - 4.8 MB | 20.5% |
+28 -1
View File
@@ -1,4 +1,31 @@
"""Remove and compress resources""" """
Удаляет ненужное и сжимает ресурсы что-бы уменьшить размер АПК
Эффективность на проверена на версии 9.0 Beta 7
разница = оригинальный размер апк - патченный размер апк, не учитывая другие патчи
| Настройка | Размер файла | Разница | % |
| :----------- | :-------------------: | :-----------------: | :-: |
| None | 17092 bytes - 17.1 MB | - | - |
| Compress PNG | 17072 bytes - 17.1 MB | 20 bytes - 0.0 MB | 0.11% |
| Remove files | 17020 bytes - 17.0 MB | 72 bytes - 0.1 MB | 0.42% |
| Remove draws | 16940 bytes - 16.9 MB | 152 bytes - 0.2 MB | 0.89% |
| Remove lines | 16444 bytes - 16.4 MB | 648 bytes - 0.7 MB | 3.79% |
| Remove ai vo | 15812 bytes - 15.8 MB | 1280 bytes - 1.3 MB | 7.49% |
| Remove langs | 15764 bytes - 15.7 MB | 1328 bytes - 1.3 MB | 7.76% |
| Все включены | 13592 bytes - 13.6 MB | 3500 bytes - 4.8 MB | 20.5% |
"compress": {
"enabled": true,
"remove_language_files": true, // удаляет все языки кроме русского и английского
"remove_AI_voiceover": true, // заменяет ИИ озвучку маскота аниксы пустыми mp3 файлами
"remove_debug_lines": false, // удаляет строки `.line n` из smali файлов использованные для дебага
"remove_drawable_files": false, // удаляет неиспользованные drawable-* из директории decompiled/res
"remove_unknown_files": true, // удаляет файлы из директории decompiled/unknown
"remove_unknown_files_keep_dirs": ["META-INF", "kotlin"], // оставляет указанные директории в decompiled/unknown
"compress_png_files": true // сжимает PNG в директории decompiled/res
}
"""
priority = -1 priority = -1
+15 -9
View File
@@ -1,8 +1,15 @@
""" """
Удаляет баннеры рекламы Удаляет баннеры рекламы
"disable_ad": {
"enabled": true
}
""" """
priority = 0 priority = 0
# imports
import textwrap
from utils.smali_parser import ( from utils.smali_parser import (
find_smali_method_end, find_smali_method_end,
find_smali_method_start, find_smali_method_start,
@@ -11,15 +18,14 @@ from utils.smali_parser import (
) )
replace = """ .locals 0 # Patch
const/4 p0, 0x1
return p0
"""
def apply(config) -> bool: def apply(config) -> bool:
replacement = textwrap.dedent("""\
.locals 0
const/4 p0, 0x1
return p0
""").splitlines()
path = "./decompiled/smali_classes2/com/swiftsoft/anixartd/Prefs.smali" path = "./decompiled/smali_classes2/com/swiftsoft/anixartd/Prefs.smali"
lines = get_smali_lines(path) lines = get_smali_lines(path)
for index, line in enumerate(lines): for index, line in enumerate(lines):
@@ -27,7 +33,7 @@ def apply(config) -> bool:
method_start = find_smali_method_start(lines, index) method_start = find_smali_method_start(lines, index)
method_end = find_smali_method_end(lines, index) method_end = find_smali_method_end(lines, index)
new_content = replace_smali_method_body( new_content = replace_smali_method_body(
lines, method_start, method_end, replace lines, method_start, method_end, replacement
) )
with open(path, "w", encoding="utf-8") as file: with open(path, "w", encoding="utf-8") as file:
+9 -1
View File
@@ -1,13 +1,20 @@
""" """
Удаляет баннеры бета-версии Удаляет баннеры бета-версии
"disable_beta_banner": {
"enabled": true
}
""" """
priority = 0 priority = 0
# imports
import os import os
from tqdm import tqdm from tqdm import tqdm
from lxml import etree from lxml import etree
# Patch
def apply(config) -> bool: def apply(config) -> bool:
attributes = [ attributes = [
"paddingTop", "paddingTop",
@@ -29,7 +36,8 @@ def apply(config) -> bool:
root = tree.getroot() root = tree.getroot()
for attr in attributes: for attr in attributes:
# tqdm.write(f"set {attr} = 0.0dip") if config["verbose"]:
tqdm.write(f"set {attr} = 0.0dip")
root.set(f"{{{config["xml_ns"]['android']}}}{attr}", "0.0dip") root.set(f"{{{config["xml_ns"]['android']}}}{attr}", "0.0dip")
tree.write( tree.write(
+8 -2
View File
@@ -1,14 +1,20 @@
""" """
Вставляет новые файлы в проект Вставляет новые файлы в проект
"insert_new": {
"enabled": true
}
""" """
priority = 0 priority = 0
import shutil # imports
import os import os
import shutil
from utils.public import insert_after_public from utils.public import insert_after_public
# Patch
def apply(config: dict) -> bool: def apply(config: dict) -> bool:
# Mod first launch window # Mod first launch window
shutil.copytree( shutil.copytree(
+4
View File
@@ -2,15 +2,19 @@
Изменяет имя пакета в apk, удаляет вход по google и vk Изменяет имя пакета в apk, удаляет вход по google и vk
"package_name": { "package_name": {
"enabled": true,
"new_package_name": "com.wowlikon.anixart" "new_package_name": "com.wowlikon.anixart"
} }
""" """
priority = -1 priority = -1
# imports
import os import os
from lxml import etree from lxml import etree
# Patch
def rename_dir(src, dst): def rename_dir(src, dst):
os.makedirs(os.path.dirname(dst), exist_ok=True) os.makedirs(os.path.dirname(dst), exist_ok=True)
os.rename(src, dst) os.rename(src, dst)
+5
View File
@@ -2,14 +2,19 @@
Меняет порядок вкладок в панели навигации Меняет порядок вкладок в панели навигации
"replace_navbar": { "replace_navbar": {
"enabled": true,
"items": ["home", "discover", "feed", "bookmarks", "profile"] "items": ["home", "discover", "feed", "bookmarks", "profile"]
} }
""" """
priority = 0 priority = 0
# imports
from lxml import etree from lxml import etree
from tqdm import tqdm from tqdm import tqdm
# Patch
def apply(config: dict) -> bool: def apply(config: dict) -> bool:
file_path = "./decompiled/res/menu/bottom.xml" file_path = "./decompiled/res/menu/bottom.xml"
+5 -1
View File
@@ -1,7 +1,8 @@
""" """
Добавляет в настройки ссылки и доавляет текст к версии приложения Добавляет в настройки ссылки и добвляет текст к версии приложения
"settings_urls": { "settings_urls": {
"enabled": true,
"menu": { "menu": {
"Раздел": [ "Раздел": [
{ {
@@ -19,11 +20,14 @@
"version": " by wowlikon" "version": " by wowlikon"
} }
""" """
priority = 0 priority = 0
# imports
from lxml import etree from lxml import etree
# Patch
def make_category(ns, name, items): def make_category(ns, name, items):
cat = etree.Element("PreferenceCategory", nsmap=ns) cat = etree.Element("PreferenceCategory", nsmap=ns)
cat.set(f"{{{ns['android']}}}title", name) cat.set(f"{{{ns['android']}}}title", name)
-6
View File
@@ -1,6 +0,0 @@
"""Change application icon"""
priority = 0
def apply(config: dict) -> bool:
return False
+5
View File
@@ -2,17 +2,22 @@
Добавляет пользовательские скорости воспроизведения видео Добавляет пользовательские скорости воспроизведения видео
"custom_speed": { "custom_speed": {
"enabled": true,
"speeds": [9.0] "speeds": [9.0]
} }
""" """
priority = 0 priority = 0
# imports
from utils.smali_parser import float_to_hex from utils.smali_parser import float_to_hex
from utils.public import ( from utils.public import (
insert_after_public, insert_after_public,
insert_after_id, insert_after_id,
) )
# Patch
def apply(config: dict) -> bool: def apply(config: dict) -> bool:
assert float_to_hex(1.5) == "0x3fc00000" assert float_to_hex(1.5) == "0x3fc00000"
+38
View File
@@ -0,0 +1,38 @@
"""
Шаблон патча
Здесь вы можете добавить описание патча, его назначение и другие детали.
Каждый патч должен независеть от других патчей и проверять себя при применении. Он не должен вернуть True, если есть проблемы.
На данный момент каждый патч должен иметь функцию `apply`, которая принимает на вход конфигурацию и возвращает True или False.
При успешном применении патча, функция apply должна вернуть True, иначе False.
Ошибка будет интерпретирована как False. С выводом ошибки в консоль.
Ещё патч должен иметь переменную `priority`, которая указывает приоритет патча, чем выше, тем раньше он будет применен.
Коротко о конфигурации. Она получается из `config.json` из config["patches"][patch_name].
Дополнительно в основном методе `apply` кроме этого есть "verbose" и содержимое "base".
Эти поля передаются всем патчам. `verbose` может быть указан так-же как флаг запуска:
```
python ./main.py build --verbose
```
В конце файла должно быть описание конфигурации патча.
Это может быть как короткий фрагмент из названия патча и одной опции "enabled", которая обрабатывается в коде патчера.
"todo_template": {
"enabled": true // Пример описания тк этот текст просто пример
}
"""
priority = 0 # Приоритет патча, чем выше, тем раньше он будет применен
# imports
from tqdm import tqdm
# Patch
def apply(config: dict) -> bool: # Анотации типов для удобства, читаемости и поддержки IDE
tqdm.write("Вывод информации через tqdm, чтобы не мешать прогресс-бару")
if config["verbose"]:
tqdm.write("Для вывода подробной и отладочной информации используйте флаг --verbose")
return True