为什么选择 PyInstaller?

PyInstaller 是一个强大的 Python 打包工具,它可以将 Python 应用程序及其依赖项打包成独立的可执行文件,无需安装 Python 解释器即可运行。对于 MuseTalk 加密项目,使用 PyInstaller 打包有以下优势:

  1. 简化部署:打包后只需分发一个可执行文件,无需担心目标环境的 Python 版本和依赖问题
  2. 增强安全性:结合已有的 PyArmor 加密措施,进一步保护代码安全性
  3. 跨平台支持:可在 Windows、macOS 和 Linux 上使用
  4. 定制性强:通过 spec 文件可以精细控制打包过程

环境准备

1. 安装 PyInstaller

pip install pyinstaller

2. 确认项目依赖

确保目标环境已安装项目所有依赖:

pip install -r requirements.txt

项目结构分析

在开始打包前,我们需要了解项目的目录结构,这是已经加密后的项目目录:

navtalk-5090-windows/
├── .venv/              # 虚拟环境
├── musetalk/           # 加密后的核心代码
├── pyarmor_runtime_000000/  # PyArmor 运行时
├── scripts/            # 脚本文件
├── utils/              # 工具类
├── .env                # 环境配置文件
├── app.py              # 主应用入口
├── app_realtime.py     # 实时应用
├── app_service.py      # 服务应用
├── avatar_api.py       # 头像 API
├── engine.py           # 引擎
├── pytransform.py      # PyArmor 转换模块
└── 其他必要的模块文件

打包步骤

步骤 1:确认项目目录

确保在正确的项目目录中,例如:

cd C:\Users\Admin003\Desktop\pyinstaller\navtalk-5090-windows

步骤 2:创建 PyInstaller 配置文件

在项目根目录中创建 musetalk.spec 文件,这是 PyInstaller 的配置文件,用于控制打包过程:

# -*- mode: python ; coding: utf-8 -*-

import sys
# 增加递归深度限制,避免打包时出现递归错误
sys.setrecursionlimit(sys.getrecursionlimit() * 50)

import os
from PyInstaller.building.build_main import Analysis, PYZ, EXE, COLLECT

# 当前目录(项目根目录)
base_dir = os.path.abspath('.')

# 入口点文件
entry_point = 'app.py'

# 分析阶段
analysis = Analysis(
    [entry_point],
    pathex=[base_dir],
    binaries=[],
    datas=[
        # 包含.env文件
        ('.env', '.'),  # 当前目录的.env文件
        # 包含musetalk目录(加密后的核心代码)
        ('musetalk', 'musetalk'),
        # 包含PyArmor运行时
        ('pyarmor_runtime_000000', 'pyarmor_runtime_000000'),
        # 包含scripts目录
        ('scripts', 'scripts'),
        # 包含utils目录
        ('utils', 'utils'),
        # 包含其他必要的目录
        # ('models', 'models'),
        ('results', 'results'),
        ('uploads', 'uploads'),
        ('video', 'video'),
    ],
    hiddenimports=[
        'websocket',
        'gputil',
        'diffusers',
        'accelerate',
        'numpy',
        'tensorflow',
        'tensorboard',
        'cv2',
        'soundfile',
        'transformers',
        'huggingface_hub',
        'librosa',
        'einops',
        'gradio',
        'gdown',
        'requests',
        'imageio',
        'omegaconf',
        'ffmpeg',
        'moviepy',
        'aiohttp_socks',
        'webrtcvad',
        'scipy',
        'torch.testing',  # 必须包含,否则会导致运行时错误
        
        # Flask 相关依赖
        'flask',
        'flask_cors',
        
        # 其他可能需要的依赖
        'websocket_client',
        'ffmpeg_python',
        'imageio_ffmpeg',
    ],
    hookspath=[],
    runtime_hooks=[],
    excludes=[],
    win_no_prefer_redirects=False,
    win_private_assemblies=False,
    cipher=None,
    noarchive=False,
)

# PYZ阶段
pyz = PYZ(analysis.pure, analysis.zipped_data, cipher=None)

# EXE阶段
executable = EXE(
    pyz,
    analysis.scripts,
    analysis.binaries,
    analysis.zipfiles,
    analysis.datas,
    [],
    name='musetalk',
    debug=False,
    bootloader_ignore_signals=False,
    strip=False,
    upx=False,  # 禁用UPX压缩,避免struct.error错误
    upx_exclude=[],
    runtime_tmpdir=None,
    console=True,
    disable_windowed_traceback=False,
    argv_emulation=False,
    target_arch=None,
    codesign_identity=None,
    entitlements_file=None,
)

# COLLECT阶段(创建单文件夹部署)
collect = COLLECT(
    executable,
    analysis.binaries,
    analysis.zipfiles,
    analysis.datas,
    strip=False,
    upx=False,  # 禁用UPX压缩,避免struct.error错误
    upx_exclude=[],
    name='musetalk',
)

步骤 3:执行打包命令

在项目根目录中执行以下命令开始打包:

# 使用虚拟环境中的PyInstaller
.venv\Scripts\pyinstaller.exe musetalk.spec

# 或使用系统安装的PyInstaller
pyinstaller musetalk.spec

打包过程可能需要几分钟时间,取决于项目大小和系统性能。

步骤 4:验证打包结果

打包完成后,PyInstaller 会在项目目录中生成一个新的 dist 目录,包含:

  • musetalk/ 目录:包含打包后的应用
    • musetalk.exe:主可执行文件(Windows)
    • 相关依赖文件和目录

步骤 5:外部挂载模型文件

如果排除了 models 目录(因为它太大),可以通过以下方法在外部挂载模型文件:

方法 1:修改 .env 文件配置

  1. 在打包后的 dist 目录中找到 .env 文件

  2. 添加或修改以下配置,指向外部模型目录:

    # 模型路径配置
    MODELS_DIR=D:/path/to/external/models
    
  3. 修改应用代码以使用这个环境变量(如果尚未支持):

    # 在 gpu_model_manager.py 中
    import os
    models_dir = os.getenv('MODELS_DIR', './models')
    unet_model_path = os.path.join(models_dir, 'musetalkV15', 'unet.pth')
    unet_config = os.path.join(models_dir, 'musetalkV15', 'musetalk.json')
    

方法 2:创建符号链接

在 Windows 上,使用管理员权限运行命令提示符:

mklink /D "dist\musetalk\models" "D:\path\to\external\models"

在 Linux/macOS 上:

ln -s "D:/path/to/external/models" "dist/musetalk/models"

方法 3:直接复制模型文件

将外部模型文件直接复制到打包后的 dist/musetalk/models 目录中。

步骤 6:启动应用

在目标系统上,运行以下命令启动服务:

# Windows
dist\musetalk\musetalk.exe

# Linux/macOS
./dist/musetalk/musetalk

配置说明

spec 文件关键参数解析

  1. datas:指定需要包含的非代码文件,格式为 (源路径, 目标路径)

    • 例如:('.env', '.') 表示将当前目录的 .env 文件复制到打包后的根目录
    • 确保包含 pyarmor_runtime_000000 目录,这是 PyArmor 加密运行时所必需的
  2. hiddenimports:指定需要包含的隐藏导入模块

    • 这些模块可能没有被直接导入,但在运行时需要使用
  3. upx:是否使用 UPX 压缩可执行文件

    • 设置为 True 可以减小可执行文件大小
  4. console:是否显示控制台窗口

    • 设置为 True 可以查看运行时输出和错误信息

常见问题与解决方案

问题 1:找不到 .env 文件

错误信息

ERROR: Unable to find 'C:\Users\Admin003\Desktop\pyinstaller\.env' when adding binary and data files.

解决方案
确保 .env 文件存在于正确的位置,并且在 datas 配置中使用正确的相对路径:

datas=[
    ('.env', '.'),  # 当前目录的.env文件
    # 其他文件...
]

问题 2:PyArmor 运行时错误

错误信息

ModuleNotFoundError: No module named 'pyarmor_runtime_000000'

解决方案
确保在 datas 配置中包含了 pyarmor_runtime_000000 目录:

datas=[
    ('pyarmor_runtime_000000', 'pyarmor_runtime_000000'),
    # 其他文件...
]

问题 3:打包后缺少依赖模块

错误信息

ModuleNotFoundError: No module named 'xxx'

解决方案
hiddenimports 列表中添加缺少的模块。

问题 4:PyInstaller 命令未找到

错误信息

'pyinstaller' 不是内部或外部命令,也不是可运行的程序或批处理文件。

解决方案
使用完整路径运行 PyInstaller:

.venv\Scripts\pyinstaller.exe musetalk.spec

问题 5:打包后可执行文件过大

解决方案

  1. 确保使用了 upx=True 启用压缩
  2. 检查是否包含了不必要的文件和目录
  3. 考虑使用 --onefile 选项创建单个可执行文件

问题 6:目录不存在错误

错误信息

ERROR: Unable to find 'path/to/directory' when adding binary and data files.

解决方案
检查 datas 配置中指定的目录是否存在,如果不存在,移除或注释掉相关配置:

datas=[
    ('.env', '.'),
    ('musetalk', 'musetalk'),
    ('pyarmor_runtime_000000', 'pyarmor_runtime_000000'),
    # 只添加实际存在的目录
    # ('models', 'models'),  # 如果不存在,注释掉
    # ('results', 'results'),  # 如果不存在,注释掉
]

问题 7:递归深度超出错误

错误信息

A RecursionError (maximum recursion depth exceeded) occurred.

解决方案

  1. 在 spec 文件顶部添加以下代码,增加递归深度限制:

    import sys
    sys.setrecursionlimit(sys.getrecursionlimit() * 5)
    
  2. 重新执行打包命令:

    .venv\Scripts\pyinstaller.exe musetalk.spec
    
  3. 如果仍然失败,尝试排除不必要的测试模块:

    excludes=['torch.testing', 'expecttest'],  # 排除测试模块
    

问题 8:struct.error: argument out of range 错误

错误信息

struct.error: argument out of range

解决方案

  1. 禁用 UPX 压缩:修改 spec 文件,禁用 UPX 压缩:
    # EXE阶段
    

executable = EXE(
pyz,
analysis.scripts,
analysis.binaries,
analysis.zipfiles,
analysis.datas,
[],
name='musetalk',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=False, # 禁用UPX压缩
upx_exclude=[],
runtime_tmpdir=None,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)

COLLECT阶段

collect = COLLECT(
executable,
analysis.binaries,
analysis.zipfiles,
analysis.datas,
strip=False,
upx=False, # 禁用UPX压缩
upx_exclude=[],
name='musetalk',
)


2. **排除不必要的依赖**:在 spec 文件中添加 excludes 参数:
```python
analysis = Analysis(
 [entry_point],
 pathex=[base_dir],
 binaries=[],
 datas=[
     # 现有配置...
 ],
 hiddenimports=[
     # 现有配置...
 ],
 hookspath=[],
 runtime_hooks=[],
 excludes=[
     'torch.testing',
     'expecttest',
     'tensorflow',
     'tensorboard',
     'pytest',
     'unittest',
 ],
 win_no_prefer_redirects=False,
 win_private_assemblies=False,
 cipher=None,
 noarchive=False,
)
  1. 清理临时文件:删除 build 和 dist 目录,重新执行打包命令。

部署与使用

步骤 1:分发打包结果

dist/musetalk 目录复制到目标服务器。

步骤 2:配置环境

  1. 确保目标系统满足以下要求:

    • 与打包时相同的操作系统类型
    • 足够的磁盘空间和内存
  2. 检查 .env 文件配置,根据目标环境修改相关路径和参数。

  3. 配置外部模型路径(如果排除了 models 目录):

    • 参考前面的「步骤 5:外部挂载模型文件」部分
    • 选择适合您环境的方法来配置模型路径

步骤 3:启动服务

在目标系统上,运行以下命令启动服务:

# Windows
dist\musetalk\musetalk.exe

# Linux/macOS
./dist/musetalk/musetalk

步骤 4:验证服务

  1. 查看控制台输出,确认服务是否成功启动
  2. 通过配置的端口(默认 40001)访问服务
  3. 测试核心功能是否正常运行
  4. 验证模型是否正确加载(如果使用外部模型)

高级技巧

1. 创建单个可执行文件

如果希望生成单个可执行文件而不是目录,可以修改 spec 文件:

# 移除 COLLECT 阶段,直接使用 EXE 阶段
executable = EXE(
    pyz,
    analysis.scripts,
    analysis.binaries,
    analysis.zipfiles,
    analysis.datas,
    [],
    name='musetalk',
    debug=False,
    bootloader_ignore_signals=False,
    strip=False,
    upx=True,
    upx_exclude=[],
    runtime_tmpdir=None,
    console=True,
    disable_windowed_traceback=False,
    argv_emulation=False,
    target_arch=None,
    codesign_identity=None,
    entitlements_file=None,
    onefile=True,  # 添加这一行
)

2. 自定义图标

可以为可执行文件添加自定义图标:

executable = EXE(
    # 其他参数不变
    icon='path/to/icon.ico',  # 添加图标路径
)

3. 排除不必要的模块

可以通过 excludes 参数排除不必要的模块,减小可执行文件大小:

analysis = Analysis(
    # 其他参数不变
    excludes=['tkinter', 'unittest', 'pydoc'],  # 排除不需要的模块
)

4. 条件包含目录

修改 spec 文件,使用条件判断来包含存在的目录,避免因目录不存在而导致的错误:

import os

datas = [
    ('.env', '.'),
    ('pyarmor_runtime_000000', 'pyarmor_runtime_000000'),
]

# 条件包含存在的目录
directories = ['musetalk', 'scripts', 'utils', 'models', 'results', 'uploads', 'video']
for dir_name in directories:
    if os.path.exists(dir_name):
        datas.append((dir_name, dir_name))
        print(f"包含目录: {dir_name}")
    else:
        print(f"跳过不存在的目录: {dir_name}")

# 然后在 Analysis 中使用 datas 变量
analysis = Analysis(
    [entry_point],
    pathex=[base_dir],
    binaries=[],
    datas=datas,
    # 其他参数不变
)

总结

使用 PyInstaller 打包 MuseTalk 加密项目可以大大简化部署过程,确保项目在不同环境中稳定运行。通过本文的步骤,您可以:

  1. 成功打包已经加密后的 MuseTalk 项目
  2. 包含所有必要的文件和依赖,包括 PyArmor 运行时
  3. 解决打包过程中可能遇到的常见问题
  4. 有效地部署和使用打包后的应用

这种打包方式特别适合已经使用 PyArmor 加密的项目,可以在保护代码安全的同时,提供便捷的部署体验。

附录:完整命令清单

1. 确认项目目录并创建 spec 文件

cd C:\Users\Admin003\Desktop\pyinstaller\navtalk-5090-windows
# 创建 musetalk.spec 文件(内容如上)

2. 执行打包命令

# 使用虚拟环境中的 PyInstaller
.venv\Scripts\pyinstaller.exe musetalk.spec

# 或使用系统 PyInstaller
pyinstaller musetalk.spec

3. 启动命令

# Windows
dist\musetalk\musetalk.exe

# Linux/macOS
./dist/musetalk/musetalk

注意:本文档适用于 MuseTalk 加密项目的打包,其他使用 PyArmor 加密的项目可以参考此方法进行调整。打包过程中遇到的具体问题可能因环境差异而有所不同,请根据实际情况进行调整。