feat: 重构 Magisk 类,添加下载、提取和复制功能,优化主机架构检测,确保与最新版本一致,并创建 bootanim.rc 文件以支持完整构建流程。

This commit is contained in:
2025-09-18 11:13:07 +08:00
parent a0007649e9
commit 42f1af5d5a

200
magisk.py
View File

@@ -1,21 +1,66 @@
#!/usr/bin/env python3
import gzip import gzip
import hashlib
import os import os
import shutil import platform
import re import re
from stuff.general import General import shutil
from tools.helper import bcolors, download_file, host, print_color, run, get_download_dir import subprocess
import urllib.request
import zipfile
class Magisk(General):
download_loc = get_download_dir() def calculate_md5(file_path):
dl_link = "https://github.com/topjohnwu/Magisk/releases/download/v30.2/Magisk-v30.2.apk" """计算文件的 MD5 值"""
dl_file_name = os.path.join(download_loc, "magisk.apk") hash_md5 = hashlib.md5()
act_md5 = "2691c30ccf059af2536cb0af803c787c" with open(file_path, "rb") as f:
extract_to = "/tmp/magisk_unpack" for chunk in iter(lambda: f.read(4096), b""):
copy_dir = "./magisk" hash_md5.update(chunk)
magisk_dir = os.path.join(copy_dir, "system", "etc", "init", "magisk") return hash_md5.hexdigest()
machine = host()
oringinal_bootanim = """
service bootanim /system/bin/bootanimation def download_file(url, file_path):
"""下载文件"""
print(f"==> Downloading from {url}")
urllib.request.urlretrieve(url, file_path)
def get_host_arch():
"""获取主机架构"""
machine = platform.machine().lower()
if machine in ['amd64', 'x86_64']:
return 'arm64' # 默认构建 arm64
elif machine in ['i386', 'i686', 'x86']:
return 'arm64' # 默认构建 arm64
elif machine in ['aarch64', 'arm64']:
return 'arm64'
elif machine.startswith('arm'):
return 'arm'
else:
return 'arm64' # 默认返回 arm64
def run_command(cmd):
"""运行命令"""
try:
subprocess.run(cmd, check=True)
except subprocess.CalledProcessError as e:
print(f"Command failed: {' '.join(cmd)}")
raise e
class Magisk:
def __init__(self):
self.download_loc = os.path.join(os.path.dirname(os.path.abspath(__file__)), "downloads")
self.dl_link = "https://github.com/topjohnwu/Magisk/releases/download/v30.2/Magisk-v30.2.apk"
self.dl_file_name = os.path.join(self.download_loc, "magisk.apk")
self.act_md5 = "2691c30ccf059af2536cb0af803c787c"
self.extract_to = os.path.join(os.path.dirname(os.path.abspath(__file__)), "temp")
self.copy_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "magisk")
self.magisk_dir = os.path.join(self.copy_dir, "system", "etc", "init", "magisk")
self.machine_arch = get_host_arch()
self.original_bootanim = """service bootanim /system/bin/bootanimation
class core animation class core animation
user graphics user graphics
group graphics audio group graphics audio
@@ -25,18 +70,21 @@ service bootanim /system/bin/bootanimation
task_profiles MaxPerformance task_profiles MaxPerformance
""" """
bootanim_component = """
on post-fs-data self.bootanim_component = """on post-fs-data
start logd start logd
exec u:r:su:s0 root root -- {MAGISKSYSTEMDIR}/magiskpolicy --live --magisk exec u:r:su:s0 root root -- {MAGISKSYSTEMDIR}/magiskpolicy --live --magisk
exec u:r:magisk:s0 root root -- {MAGISKSYSTEMDIR}/magiskpolicy --live --magisk exec u:r:magisk:s0 root root -- {MAGISKSYSTEMDIR}/magiskpolicy --live --magisk
exec u:r:update_engine:s0 root root -- {MAGISKSYSTEMDIR}/magiskpolicy --live --magisk exec u:r:update_engine:s0 root root -- {MAGISKSYSTEMDIR}/magiskpolicy --live --magisk
exec u:r:su:s0 root root -- {MAGISKSYSTEMDIR}/{magisk_name} --auto-selinux --setup-sbin {MAGISKSYSTEMDIR} {MAGISKTMP} exec u:r:su:s0 root root -- {MAGISKSYSTEMDIR}/{magisk_name} --auto-selinux --setup-sbin {MAGISKSYSTEMDIR} {MAGISKTMP}
exec u:r:su:s0 root root -- {MAGISKTMP}/magisk --auto-selinux --post-fs-data exec u:r:su:s0 root root -- {MAGISKTMP}/magisk --auto-selinux --post-fs-data
on nonencrypted on nonencrypted
exec u:r:su:s0 root root -- {MAGISKTMP}/magisk --auto-selinux --service exec u:r:su:s0 root root -- {MAGISKTMP}/magisk --auto-selinux --service
on property:vold.decrypt=trigger_restart_framework on property:vold.decrypt=trigger_restart_framework
exec u:r:su:s0 root root -- {MAGISKTMP}/magisk --auto-selinux --service exec u:r:su:s0 root root -- {MAGISKTMP}/magisk --auto-selinux --service
on property:sys.boot_completed=1 on property:sys.boot_completed=1
mkdir /data/adb/magisk 755 mkdir /data/adb/magisk 755
exec u:r:su:s0 root root -- {MAGISKTMP}/magisk --auto-selinux --boot-complete exec u:r:su:s0 root root -- {MAGISKTMP}/magisk --auto-selinux --boot-complete
@@ -47,60 +95,134 @@ on property:init.svc.zygote=restarting
on property:init.svc.zygote=stopped on property:init.svc.zygote=stopped
exec u:r:su:s0 root root -- {MAGISKTMP}/magisk --auto-selinux --zygote-restart exec u:r:su:s0 root root -- {MAGISKTMP}/magisk --auto-selinux --zygote-restart
""".format(MAGISKSYSTEMDIR="/system/etc/init/magisk", MAGISKTMP="/sbin", magisk_name="magisk") """.format(MAGISKSYSTEMDIR="/system/etc/init/magisk", MAGISKTMP="/sbin", magisk_name="magisk")
def download(self): def download(self):
print_color("Downloading latest Magisk-Delta now .....", bcolors.GREEN) """下载 Magisk APK"""
super().download() print("==> Downloading latest Magisk now .....")
# 确保下载目录存在
os.makedirs(self.download_loc, exist_ok=True)
# 检查并下载 Magisk APK
need_download = True
if os.path.exists(self.dl_file_name):
print("==> Checking existing Magisk APK...")
actual_md5 = calculate_md5(self.dl_file_name)
if actual_md5 == self.act_md5:
print("==> Magisk APK already exists and verified!")
need_download = False
else:
print(f"==> MD5 mismatch. Expected: {self.act_md5}, Got: {actual_md5}")
if need_download:
download_file(self.dl_link, self.dl_file_name)
actual_md5 = calculate_md5(self.dl_file_name)
if actual_md5 != self.act_md5:
raise Exception(f"Downloaded file MD5 verification failed! Expected: {self.act_md5}, Got: {actual_md5}")
print("==> Download completed and verified!")
def extract(self):
"""解压 APK 文件"""
print("==> Extracting Magisk APK...")
# 清理并创建解压目录
shutil.rmtree(self.extract_to, ignore_errors=True)
os.makedirs(self.extract_to, exist_ok=True)
# 解压 APK
with zipfile.ZipFile(self.dl_file_name) as z:
z.extractall(self.extract_to)
def copy(self): def copy(self):
"""复制文件到目标目录"""
print("==> Copying magisk files now ...")
# 清理并创建目标目录
if os.path.exists(self.copy_dir): if os.path.exists(self.copy_dir):
shutil.rmtree(self.copy_dir) shutil.rmtree(self.copy_dir)
if not os.path.exists(self.magisk_dir): os.makedirs(self.magisk_dir, exist_ok=True)
os.makedirs(self.magisk_dir, exist_ok=True) os.makedirs(os.path.join(self.copy_dir, "sbin"), exist_ok=True)
if not os.path.exists(os.path.join(self.copy_dir, "sbin")):
os.makedirs(os.path.join(self.copy_dir, "sbin"), exist_ok=True)
print_color("Copying magisk libs now ...", bcolors.GREEN)
# 架构映射
arch_map = { arch_map = {
"x86": "x86", "x86": "x86",
"x86_64": "x86_64", "x86_64": "x86_64",
"arm": "armeabi-v7a", "arm": "armeabi-v7a",
"arm64": "arm64-v8a" "arm64": "arm64-v8a"
} }
lib_dir = os.path.join(self.extract_to, "lib", arch_map[self.machine[0]])
# 复制主要架构的库文件
lib_dir = os.path.join(self.extract_to, "lib", arch_map[self.machine_arch])
if os.path.exists(lib_dir): if os.path.exists(lib_dir):
for parent, dirnames, filenames in os.walk(lib_dir): for parent, dirnames, filenames in os.walk(lib_dir):
for filename in filenames: for filename in filenames:
o_path = os.path.join(lib_dir, filename) o_path = os.path.join(lib_dir, filename)
so_name = re.search(r'lib(.*)\.so', filename) so_name = re.search(r'lib(.*)\.so', filename)
if so_name: if so_name:
n_path = os.path.join(self.magisk_dir, so_name.group(1)) n_path = os.path.join(self.magisk_dir, so_name.group(1))
shutil.copyfile(o_path, n_path) shutil.copyfile(o_path, n_path)
run(["chmod", "+x", n_path]) run_command(["chmod", "+x", n_path])
# 同时复制 arm32 的 magisk 二进制文件(如果存在) # 复制 arm32 的 magisk 二进制文件(如果存在)
lib32_path = os.path.join(self.extract_to, "lib", "armeabi-v7a") lib32_path = os.path.join(self.extract_to, "lib", "armeabi-v7a")
magisk32_src = os.path.join(lib32_path, "libmagisk32.so") magisk32_src = os.path.join(lib32_path, "libmagisk32.so")
magisk32_dst = os.path.join(self.magisk_dir, "magisk32") magisk32_dst = os.path.join(self.magisk_dir, "magisk32")
if os.path.exists(magisk32_src): if os.path.exists(magisk32_src):
shutil.copyfile(magisk32_src, magisk32_dst) shutil.copyfile(magisk32_src, magisk32_dst)
run(["chmod", "+x", magisk32_dst]) run_command(["chmod", "+x", magisk32_dst])
# Copy magisk.apk to target directory # 复制 magisk.apk 到目标目录
apk_dst = os.path.join(self.magisk_dir, "magisk.apk") apk_dst = os.path.join(self.magisk_dir, "magisk.apk")
if os.path.exists(self.dl_file_name): if os.path.exists(self.dl_file_name):
shutil.copyfile(self.dl_file_name, apk_dst) shutil.copyfile(self.dl_file_name, apk_dst)
# 生成 bootanim.rc 文件
self._create_bootanim_rc()
print("==> Magisk installation completed!")
# Updating Magisk from Magisk manager will modify bootanim.rc, def _create_bootanim_rc(self):
# So it is necessary to backup the original bootanim.rc. """创建 bootanim.rc 文件"""
# 创建备份的 gzip 文件
bootanim_path = os.path.join(self.copy_dir, "system", "etc", "init", "bootanim.rc") bootanim_path = os.path.join(self.copy_dir, "system", "etc", "init", "bootanim.rc")
gz_filename = os.path.join(bootanim_path)+".gz" gz_filename = bootanim_path + ".gz"
with gzip.open(gz_filename,'wb') as f_gz:
f_gz.write(self.oringinal_bootanim.encode('utf-8')) # 确保目录存在
os.makedirs(os.path.dirname(bootanim_path), exist_ok=True)
# 创建 gzip 备份
with gzip.open(gz_filename, 'wb') as f_gz:
f_gz.write(self.original_bootanim.encode('utf-8'))
# 创建完整的 bootanim.rc
with open(bootanim_path, "w") as initfile: with open(bootanim_path, "w") as initfile:
initfile.write(self.oringinal_bootanim+self.bootanim_component) initfile.write(self.original_bootanim + self.bootanim_component)
os.chmod(bootanim_path, 0o644)
os.chmod(bootanim_path, 0o644) def run(self):
"""执行完整的构建流程"""
try:
self.download()
self.extract()
self.copy()
except Exception as e:
print(f"Error: {e}")
return False
return True
def main():
"""主函数"""
magisk = Magisk()
success = magisk.run()
if success:
print("==> Magisk vendor package created successfully!")
else:
print("==> Failed to create Magisk vendor package!")
exit(1)
if __name__ == '__main__':
main()