
GitHub 项目地址
vocal-separate 官方仓库:
https://github.com/jianchang512/vocal-separate这是一个基于 Spleeter 的人声分离工具,提供本地网页界面,支持 2stems/4stems/5stems 模型 。
打包成 exe 的完整方案
方案一:使用 PyInstaller 打包(推荐)
1. 环境准备
# 1. 克隆项目
git clone https://github.com/jianchang512/vocal-separate.git
cd vocal-separate
# 2. 创建虚拟环境
python -m venv venv
# Windows 激活虚拟环境
venv\Scripts\activate
# 3. 安装依赖
pip install --upgrade pip
pip install pyinstaller
pip install -r requirements.txt2. 创建打包脚本 build_exe.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
vocal-separate v0.0.4 EXE 打包脚本
使用 PyInstaller 将项目打包成单个可执行文件
"""
import os
import sys
import shutil
import site
import subprocess
from pathlib import Path
def clean_build_dirs():
"""清理之前的构建目录"""
dirs_to_clean = ['build', 'dist']
for dir_name in dirs_to_clean:
if os.path.exists(dir_name):
shutil.rmtree(dir_name)
print(f"已清理 {dir_name} 目录")
# 删除 spec 文件
spec_files = [f for f in os.listdir('.') if f.endswith('.spec')]
for spec in spec_files:
os.remove(spec)
print(f"已删除 {spec}")
def get_site_packages_path():
"""获取 site-packages 路径"""
for path in site.getsitepackages():
if 'site-packages' in path:
return path
return None
def find_model_path():
"""查找预训练模型路径"""
# 可能的模型存放位置
possible_paths = [
'./models',
'./pretrained_models',
os.path.join(os.path.dirname(sys.executable), 'models'),
os.path.join(os.getcwd(), 'models'),
]
# 检查 site-packages 中的 spleeter 模型
site_packages = get_site_packages_path()
if site_packages:
spleeter_path = os.path.join(site_packages, 'spleeter')
if os.path.exists(spleeter_path):
pretrained_path = os.path.join(spleeter_path, 'pretrained')
if os.path.exists(pretrained_path):
possible_paths.append(pretrained_path)
for path in possible_paths:
if os.path.exists(path):
print(f"找到模型目录: {path}")
return path
print("警告:未找到预训练模型目录,打包后可能需要联网下载")
return None
def create_spec_file(project_root, model_path):
"""创建 PyInstaller spec 文件"""
spec_content = f"""# -*- mode: python ; coding: utf-8 -*-
import sys
from PyInstaller.utils.hooks import collect_data_files, collect_submodules
a = Analysis(
['start.py'],
pathex=['{project_root}'],
binaries=[],
datas=[
# 包含静态文件
('./static', 'static'),
('./templates', 'templates'),
# 包含配置文件
('./config.json', '.'),
('./config.yaml', '.'),
# 包含模型文件
('{model_path}', 'models'),
],
hiddenimports=[
'spleeter',
'spleeter.*',
'tensorflow',
'tensorflow.*',
'librosa',
'librosa.*',
'ffmpeg',
'ffmpeg.*',
'numpy',
'scipy',
'sklearn',
'sklearn.*',
'joblib',
'joblib.*',
'pydub',
'pydub.*',
'flask',
'flask.*',
'werkzeug',
'werkzeug.*',
'jinja2',
'jinja2.*',
'markupsafe',
'markupsafe.*',
'itsdangerous',
'click',
'threading',
'multiprocessing',
],
hookspath=[],
hooksconfig={{}},
runtime_hooks=[],
excludes=[],
noarchive=False,
)
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
name='vocal-separate',
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,
icon='icon.ico' if os.path.exists('icon.ico') else None,
)
# 创建快捷启动脚本
with open('start_exe.py', 'w') as f:
f.write('''
import os
import sys
import webbrowser
import time
from threading import Timer
def open_browser():
webbrowser.open('http://127.0.0.1:5000')
if __name__ == '__main__':
# 切换到程序所在目录
os.chdir(os.path.dirname(sys.executable))
# 启动主程序
from start import app
# 延时打开浏览器
Timer(1.5, open_browser).start()
# 运行 Flask 应用
app.run(host='127.0.0.1', port=5000, debug=False, threaded=True)
''')
"""
with open('vocal-separate.spec', 'w', encoding='utf-8') as f:
f.write(spec_content)
print("已生成 spec 文件")
def check_ffmpeg():
"""检查 ffmpeg 是否可用"""
try:
subprocess.run(['ffmpeg', '-version'], capture_output=True)
return True
except FileNotFoundError:
print("警告:未找到 ffmpeg,程序可能无法处理视频文件")
return False
def download_ffmpeg():
"""下载 ffmpeg(Windows 系统)"""
if sys.platform == 'win32':
import urllib.request
import zipfile
ffmpeg_url = "https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-essentials.zip"
print("正在下载 ffmpeg...")
try:
urllib.request.urlretrieve(ffmpeg_url, "ffmpeg.zip")
with zipfile.ZipFile("ffmpeg.zip", 'r') as zip_ref:
zip_ref.extractall("ffmpeg_temp")
# 复制 ffmpeg.exe 到项目目录
for root, dirs, files in os.walk("ffmpeg_temp"):
for file in files:
if file == "ffmpeg.exe":
shutil.copy(os.path.join(root, file), "ffmpeg.exe")
print("ffmpeg 下载完成")
break
# 清理临时文件
shutil.rmtree("ffmpeg_temp")
os.remove("ffmpeg.zip")
except Exception as e:
print(f"下载 ffmpeg 失败: {e}")
def main():
"""主打包函数"""
print("=" * 50)
print("vocal-separate v0.0.4 EXE 打包工具")
print("=" * 50)
# 检查 ffmpeg
if not check_ffmpeg() and sys.platform == 'win32':
choice = input("未找到 ffmpeg,是否自动下载?(y/n): ")
if choice.lower() == 'y':
download_ffmpeg()
# 查找模型路径
model_path = find_model_path()
if not model_path:
print("提示:模型将在首次运行时下载")
# 清理旧文件
clean_build_dirs()
# 获取项目根目录
project_root = os.getcwd()
# 创建 spec 文件
create_spec_file(project_root, model_path or './models')
# 执行 PyInstaller
print("\n开始打包,这可能需要几分钟时间...")
cmd = [
'pyinstaller',
'vocal-separate.spec',
'--clean',
'--noconfirm',
]
try:
subprocess.run(cmd, check=True)
print("\n✅ 打包完成!")
print(f"可执行文件位于: {os.path.join(project_root, 'dist', 'vocal-separate')}")
print("\n使用方法:")
print("1. 进入 dist/vocal-separate 目录")
print("2. 双击运行 vocal-separate.exe")
print("3. 浏览器会自动打开 http://127.0.0.1:5000")
print("4. 拖拽音视频文件进行分离")
except subprocess.CalledProcessError as e:
print(f"\n❌ 打包失败: {e}")
return 1
return 0
if __name__ == '__main__':
sys.exit(main())3. 创建启动脚本 start_exe.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
vocal-separate 启动脚本(用于 exe 打包)
"""
import os
import sys
import webbrowser
import time
import logging
from threading import Timer
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('vocal-separate.log', encoding='utf-8'),
logging.StreamHandler()
]
)
logger = logging.getLogger('vocal-separate')
def open_browser():
"""延时打开浏览器"""
time.sleep(1.5)
url = 'http://127.0.0.1:5000'
try:
webbrowser.open(url)
logger.info(f"已打开浏览器访问: {url}")
except Exception as e:
logger.error(f"打开浏览器失败: {e}")
def check_environment():
"""检查运行环境"""
# 切换到程序所在目录
os.chdir(os.path.dirname(sys.executable))
# 检查 ffmpeg
ffmpeg_paths = [
'ffmpeg.exe',
os.path.join(os.path.dirname(sys.executable), 'ffmpeg.exe'),
]
ffmpeg_found = False
for path in ffmpeg_paths:
if os.path.exists(path):
os.environ['PATH'] = os.path.dirname(path) + os.pathsep + os.environ['PATH']
ffmpeg_found = True
logger.info(f"找到 ffmpeg: {path}")
break
if not ffmpeg_found:
logger.warning("未找到 ffmpeg,视频处理功能可能受限")
# 检查模型目录
model_dirs = [
'models',
'pretrained_models',
os.path.join(os.path.dirname(sys.executable), 'models'),
]
for model_dir in model_dirs:
if os.path.exists(model_dir):
logger.info(f"找到模型目录: {model_dir}")
break
else:
logger.info("未找到本地模型,将在首次使用时下载")
def main():
"""主函数"""
print("=" * 50)
print("vocal-separate v0.0.4 人声分离工具")
print("=" * 50)
# 检查环境
check_environment()
# 启动浏览器线程
Timer(1.5, open_browser).start()
# 导入并启动 Flask 应用
try:
# 动态导入 start 模块
sys.path.insert(0, os.path.dirname(sys.executable))
# 这里需要根据实际的项目入口文件调整
# 假设主文件是 start.py
import importlib.util
spec = importlib.util.spec_from_file_location(
"start",
os.path.join(os.path.dirname(sys.executable), "start.py")
)
start_module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(start_module)
# 获取 Flask app
if hasattr(start_module, 'app'):
app = start_module.app
else:
# 尝试获取其他可能的变量名
for attr in ['application', 'server', 'webapp']:
if hasattr(start_module, attr):
app = getattr(start_module, attr)
break
else:
raise AttributeError("找不到 Flask 应用实例")
logger.info("启动服务器 http://127.0.0.1:5000")
app.run(
host='127.0.0.1',
port=5000,
debug=False,
threaded=True
)
except Exception as e:
logger.error(f"启动失败: {e}")
print(f"\n❌ 启动失败: {e}")
print("请查看 vocal-separate.log 获取详细信息")
input("按回车键退出...")
sys.exit(1)
if __name__ == '__main__':
main()4. 运行打包
# 执行打包脚本
python build_exe.py方案二:使用 auto-py-to-exe(可视化界面)
# 安装 auto-py-to-exe
pip install auto-py-to-exe
# 启动可视化界面
auto-py-to-exe在可视化界面中配置:
- Script Location: 选择
start.py - Onefile: 选择 One Directory(推荐,便于包含模型文件)
- Console Window: 勾选(显示控制台便于调试)
- Icon: 可选,设置程序图标
Additional Files: 添加以下文件和目录
./static/目录./templates/目录- 预训练模型目录(如
./models/) ffmpeg.exe(Windows 系统)
方案三:创建安装包(使用 Inno Setup)
创建 setup_script.iss:
[Setup]
AppName=vocal-separate
AppVersion=0.0.4
DefaultDirName={pf}\vocal-separate
DefaultGroupName=vocal-separate
UninstallDisplayIcon={app}\vocal-separate.exe
Compression=lzma2
SolidCompression=yes
OutputDir=installer
OutputBaseFilename=vocal-separate-0.0.4-setup
[Files]
Source: "dist\vocal-separate\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "ffmpeg.exe"; DestDir: "{app}"; Flags: ignoreversion
[Icons]
Name: "{group}\vocal-separate"; Filename: "{app}\vocal-separate.exe"
Name: "{group}\Uninstall vocal-separate"; Filename: "{uninstallexe}"
Name: "{commondesktop}\vocal-separate"; Filename: "{app}\vocal-separate.exe"
[Run]
Filename: "{app}\vocal-separate.exe"; Description: "启动 vocal-separate"; Flags: postinstall nowait skipifsilent注意事项
1. 模型文件处理
- 项目内置了 Spleeter 预训练模型,打包时需要包含这些模型文件
- 模型文件通常较大(几百MB),建议采用外置模型的方式,首次运行时下载
2. 依赖项说明
- ffmpeg:必须包含,用于处理视频文件的音轨提取
- TensorFlow:如果支持 GPU 加速,需要额外打包 CUDA 库
3. 性能优化建议
- 对于无 NVIDIA GPU 的电脑,建议默认使用 2stems 模型,避免内存溢出
- 打包时可以包含 CPU 版本的 TensorFlow,减小体积
4. 打包后的文件结构
vocal-separate/
├── vocal-separate.exe # 主程序
├── ffmpeg.exe # ffmpeg 工具
├── models/ # 预训练模型
├── static/ # 静态资源
├── templates/ # HTML模板
├── config.json # 配置文件
└── vocal-separate.log # 运行日志使用说明
打包完成后,用户只需:
- 解压或安装程序
- 双击运行
vocal-separate.exe - 等待浏览器自动打开
- 拖拽音视频文件到网页界面
- 点击"立即分离"等待处理完成