first
This commit is contained in:
+135
@@ -0,0 +1,135 @@
|
||||
"""
|
||||
FreePEP 一键打包分发脚本 (build_exe.py)
|
||||
功能:
|
||||
1. 检查并自动安装 PyInstaller
|
||||
2. 打包 WebUI 为独立可执行文件 FreePEP.exe
|
||||
3. 自动抓取并内嵌 Chromium 便携版浏览器内核至 dist/FreePEP/browsers/
|
||||
4. 生成开箱即用的完全独立便携版压缩包 (FreePEP-Windows-x64.zip)
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import zipfile
|
||||
import subprocess
|
||||
|
||||
if hasattr(sys.stdout, "reconfigure"):
|
||||
sys.stdout.reconfigure(encoding="utf-8")
|
||||
|
||||
|
||||
def check_and_install_pyinstaller():
|
||||
"""检查 PyInstaller 是否已安装,未安装则自动安装"""
|
||||
try:
|
||||
import PyInstaller
|
||||
print("[+] 检测到 PyInstaller 已安装。")
|
||||
except ImportError:
|
||||
print("[*] 未检测到 PyInstaller,正在通过 pip 自动安装...")
|
||||
subprocess.check_call([sys.executable, "-m", "pip", "install", "pyinstaller"])
|
||||
print("[+] PyInstaller 安装完成!")
|
||||
|
||||
|
||||
def find_playwright_browsers_dir():
|
||||
"""定位本机 Playwright 下载的浏览器内核路径"""
|
||||
local_app_data = os.environ.get("LOCALAPPDATA", "")
|
||||
ms_playwright = os.path.join(local_app_data, "ms-playwright")
|
||||
if os.path.exists(ms_playwright):
|
||||
return ms_playwright
|
||||
return None
|
||||
|
||||
|
||||
def build_package():
|
||||
print("=" * 65)
|
||||
print(" 🚀 FreePEP 独立 EXE 便携发行包打包脚本")
|
||||
print("=" * 65)
|
||||
|
||||
check_and_install_pyinstaller()
|
||||
|
||||
dist_dir = os.path.abspath("./dist")
|
||||
build_dir = os.path.abspath("./build")
|
||||
out_app_dir = os.path.join(dist_dir, "FreePEP")
|
||||
|
||||
# 清理旧的编译产物
|
||||
if os.path.exists(out_app_dir):
|
||||
print(f"[*] 清理旧输出目录: {out_app_dir}")
|
||||
shutil.rmtree(out_app_dir, ignore_errors=True)
|
||||
|
||||
# 1. 组装 PyInstaller 命令
|
||||
print("\n[1/3] 正在使用 PyInstaller 编译 Python 代码与依赖...")
|
||||
cmd = [
|
||||
sys.executable, "-m", "PyInstaller",
|
||||
"--name=FreePEP",
|
||||
"--onedir", # 目录模式(比单文件解压更快、更利于内嵌浏览器)
|
||||
"--collect-all=playwright", # 打包 playwright driver 与所有依赖
|
||||
"--hidden-import=uvicorn.logging",
|
||||
"--hidden-import=uvicorn.loops",
|
||||
"--hidden-import=uvicorn.loops.auto",
|
||||
"--hidden-import=uvicorn.protocols",
|
||||
"--hidden-import=uvicorn.protocols.http",
|
||||
"--hidden-import=uvicorn.protocols.http.auto",
|
||||
"--hidden-import=uvicorn.protocols.websockets",
|
||||
"--hidden-import=uvicorn.protocols.websockets.auto",
|
||||
"--hidden-import=uvicorn.lifespans",
|
||||
"--hidden-import=uvicorn.lifespans.on",
|
||||
"--clean",
|
||||
"-y",
|
||||
"webui.py"
|
||||
]
|
||||
|
||||
subprocess.check_call(cmd)
|
||||
print("[+] 核心程序 FreePEP.exe 编译成功!")
|
||||
|
||||
# 2. 拷贝 Playwright Chromium 绿色便携浏览器到目标目录
|
||||
print("\n[2/3] 正在打包绿色便携版 Chromium 浏览器内核...")
|
||||
pw_src = find_playwright_browsers_dir()
|
||||
target_browsers_dir = os.path.join(out_app_dir, "_internal", "browsers")
|
||||
target_browsers_dir2 = os.path.join(out_app_dir, "browsers")
|
||||
|
||||
if pw_src:
|
||||
print(f"[*] 从本机提取 Chromium 内核: {pw_src}")
|
||||
os.makedirs(target_browsers_dir2, exist_ok=True)
|
||||
for item in os.listdir(pw_src):
|
||||
if "chromium" in item.lower() or "ffmpeg" in item.lower():
|
||||
s_path = os.path.join(pw_src, item)
|
||||
d_path = os.path.join(target_browsers_dir2, item)
|
||||
if not os.path.exists(d_path):
|
||||
print(f" -> 拷贝内核组件: {item}")
|
||||
if os.path.isdir(s_path):
|
||||
shutil.copytree(s_path, d_path)
|
||||
else:
|
||||
shutil.copy2(s_path, d_path)
|
||||
print("[+] 便携浏览器内核内嵌完成!分发给用户无需安装任何浏览器或 Python。")
|
||||
else:
|
||||
print("[!] 警告: 本机未找到 Playwright 缓存的 Chromium,建议先运行 'playwright install chromium'。")
|
||||
|
||||
# 拷贝默认说明和依赖
|
||||
for f in ["README.md", "pep_catalog.json"]:
|
||||
if os.path.exists(f):
|
||||
shutil.copy2(f, os.path.join(out_app_dir, f))
|
||||
|
||||
# 3. 压缩为发布 ZIP 包
|
||||
print("\n[3/3] 正在生成发布压缩包...")
|
||||
zip_name = "FreePEP-Windows-x64.zip"
|
||||
zip_path = os.path.join(dist_dir, zip_name)
|
||||
if os.path.exists(zip_path):
|
||||
os.remove(zip_path)
|
||||
|
||||
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zipf:
|
||||
for root, dirs, files in os.walk(out_app_dir):
|
||||
for file in files:
|
||||
abs_f = os.path.join(root, file)
|
||||
rel_f = os.path.relpath(abs_f, dist_dir)
|
||||
zipf.write(abs_f, rel_f)
|
||||
|
||||
zip_size_mb = round(os.path.getsize(zip_path) / (1024 * 1024), 2)
|
||||
print("\n" + "=" * 65)
|
||||
print(f"🎉 打包全部完成!")
|
||||
print(f"📁 绿色便携文件夹: {out_app_dir}")
|
||||
print(f"📦 最终分发压缩包: {zip_path} ({zip_size_mb} MB)")
|
||||
print("=" * 65)
|
||||
print("\n👉 分发使用说明:")
|
||||
print(" 直接将 FreePEP-Windows-x64.zip 发送给用户即可。")
|
||||
print(" 用户解压后双击 FreePEP.exe,无需配置 Python,会自动弹出浏览器打开页面!\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
build_package()
|
||||
Reference in New Issue
Block a user