""" 故事提取工具 - 打包构建脚本 使用方法: python build.py """ import os import sys import shutil import subprocess BASE_DIR = os.path.dirname(os.path.abspath(__file__)) BACKEND_DIR = os.path.join(BASE_DIR, "backend") FRONTEND_DIR = os.path.join(BASE_DIR, "frontend") DIST_DIR = os.path.join(BASE_DIR, "dist") BUILD_DIR = os.path.join(BASE_DIR, "build") def step(msg: str): print(f"\n{'='*50}") print(f" {msg}") print(f"{'='*50}") def build_frontend(): """构建前端""" step("1/4 构建前端...") os.chdir(FRONTEND_DIR) subprocess.run([sys.executable, "-m", "npm", "run", "build"], check=True, shell=False) os.chdir(BASE_DIR) print("前端构建完成!") def create_package_structure(): """创建打包目录结构""" step("2/4 准备打包文件...") # 清理旧的 dist if os.path.exists(DIST_DIR): shutil.rmtree(DIST_DIR) os.makedirs(DIST_DIR, exist_ok=True) # 复制 backend 目录 pkg_backend = os.path.join(DIST_DIR, "backend") if os.path.exists(pkg_backend): shutil.rmtree(pkg_backend) shutil.copytree(BACKEND_DIR, pkg_backend) # 复制 frontend/dist pkg_frontend = os.path.join(DIST_DIR, "frontend", "dist") os.makedirs(pkg_frontend, exist_ok=True) src_dist = os.path.join(FRONTEND_DIR, "dist") if os.path.exists(src_dist): for item in os.listdir(src_dist): s = os.path.join(src_dist, item) d = os.path.join(pkg_frontend, item) if os.path.isdir(s): shutil.copytree(s, d) else: shutil.copy2(s, d) # 复制 run.py shutil.copy2(os.path.join(BASE_DIR, "run.py"), os.path.join(DIST_DIR, "run.py")) print("打包文件准备完成!") def build_exe(): """使用 PyInstaller 打包""" step("3/4 打包成 exe...") os.chdir(DIST_DIR) cmd = [ sys.executable, "-m", "PyInstaller", "--name=故事提取工具", "--onefile", "--windowed", "--noconfirm", "--clean", # 隐藏导入 "--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.lifespan", "--hidden-import=uvicorn.lifespan.on", "--hidden-import=multipart", # 收集数据文件 f"--add-data=backend;backend", f"--add-data=frontend;frontend", # 入口 "run.py", ] subprocess.run(cmd, check=True, shell=False) os.chdir(BASE_DIR) print("打包完成!") def copy_output(): """复制最终输出""" step("4/4 整理输出...") exe_name = "故事提取工具.exe" src = os.path.join(DIST_DIR, "dist", exe_name) dst = os.path.join(BASE_DIR, exe_name) if os.path.exists(src): shutil.copy2(src, dst) size_mb = os.path.getsize(dst) / (1024 * 1024) print(f"\n✅ 打包成功!") print(f" 输出文件: {dst}") print(f" 文件大小: {size_mb:.1f} MB") print(f"\n 使用方法:") print(f" 1. 将 {exe_name} 发送给用户") print(f" 2. 用户双击运行,自动打开浏览器") print(f" 3. 首次使用需要在设置页面配置 API Key") else: print(f"\n❌ 打包失败,未找到输出文件: {src}") def main(): print("故事提取工具 - 打包构建") print("=" * 50) # 检查 PyInstaller try: import PyInstaller except ImportError: print("正在安装 PyInstaller...") subprocess.run([sys.executable, "-m", "pip", "install", "pyinstaller", "--break-system-packages"], check=True, shell=False) build_frontend() create_package_structure() build_exe() copy_output() if __name__ == "__main__": main()