首次提交
This commit is contained in:
127
scripts/make_portable.sh
Normal file
127
scripts/make_portable.sh
Normal file
@@ -0,0 +1,127 @@
|
||||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# 把环境改造成"项目内自包含"——Python、第三方库、模型全放进项目目录,
|
||||
# 之后整个项目文件夹可以【随便放到任意路径、任意用户】,不再依赖 /root 或固定路径。
|
||||
#
|
||||
# 在【能联网】的机器上跑一次(已 setup_server.sh 装好 .venv 后):
|
||||
# bash make_portable.sh # 默认 ffmpeg 用 GPU 版(BtbN,含 cuda/nvdec,也兼容 CPU)
|
||||
# # 需运行机 glibc>=2.28;GPU 机器自动硬解、CPU 机器软解
|
||||
# FFMPEG_VARIANT=cpu bash make_portable.sh # ffmpeg 用 CPU 版(johnvansickle,全静态,老 glibc 也能跑)
|
||||
# 之后:
|
||||
# bash pack.sh # 打包(此时只需打项目目录,已自包含)
|
||||
# # 离线机解压到【任意路径】即可: source activate.sh && python main.py ...
|
||||
# =============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PROJ="$(pwd)"
|
||||
PIP_MIRROR="${PIP_MIRROR:-https://repo.huaweicloud.com/repository/pypi/simple}"
|
||||
|
||||
log() { echo -e "\n\033[1;32m==> $*\033[0m"; }
|
||||
warn() { echo -e "\033[1;33m[警告] $*\033[0m"; }
|
||||
have() { command -v "$1" >/dev/null 2>&1; }
|
||||
|
||||
[ -d .venv ] || { echo "没有 .venv,请先 bash setup_server.sh"; exit 1; }
|
||||
# shellcheck disable=SC1091
|
||||
source .venv/bin/activate
|
||||
have uv || { echo "需要 uv(本机改造用,离线机不需要)"; exit 1; }
|
||||
|
||||
# 0) 确保 bin/ffmpeg 可用。两种变体(用环境变量 FFMPEG_VARIANT 选):
|
||||
# cpu (默认) = johnvansickle 静态版,含 dav1d(CPU 软解 AV1),兼容老 glibc,哪都能跑。
|
||||
# gpu = BtbN 版,含 cuda/nvdec(GPU 硬解),给 Ampere+ 卡的机器用;需较新 glibc(>=2.28)。
|
||||
# 用法: FFMPEG_VARIANT=gpu bash make_portable.sh
|
||||
# 默认下 GPU 版(含 cuda/nvdec,也兼容 CPU 软解)。要纯 CPU 版用 FFMPEG_VARIANT=cpu。
|
||||
FFMPEG_VARIANT="${FFMPEG_VARIANT:-gpu}"
|
||||
FF_NEED=1
|
||||
if [ "$FFMPEG_VARIANT" = "gpu" ]; then
|
||||
FF_URL="${FFMPEG_GPU_URL:-https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-linux64-gpl.tar.xz}"
|
||||
# GPU 版(BtbN)要 glibc>=2.28 才能运行;本机若太老仅警告(可能在给别的新机器打包)
|
||||
# 注意:管道里 head 会让前面命令 SIGPIPE,配合 set -e/pipefail 会误退出,故加 || true
|
||||
_glibc="$(ldd --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+' | head -1 || true)"
|
||||
if [ -n "$_glibc" ] && [ "$(printf '%s\n2.28\n' "$_glibc" | sort -V | head -1)" != "2.28" ]; then
|
||||
warn "本机 glibc=$_glibc < 2.28,GPU 版 ffmpeg 在本机跑不了(GLIBC_2.28 not found)。"
|
||||
warn "仅当打包给 glibc>=2.28 的机器时才用 GPU 版;本机自用请: FFMPEG_VARIANT=cpu bash make_portable.sh"
|
||||
fi
|
||||
# 用 grep(不带 -q)读完整输出再判,避免 grep -q 提前关管道触发 SIGPIPE+pipefail 误判
|
||||
if [ -x "$PROJ/bin/ffmpeg" ] && "$PROJ/bin/ffmpeg" -hide_banner -hwaccels 2>/dev/null | grep -i cuda >/dev/null; then
|
||||
log "[0/4] bin/ffmpeg 已是 GPU 版(支持 cuda),跳过"; FF_NEED=0
|
||||
else
|
||||
log "[0/4] 下载 GPU 版 ffmpeg (BtbN,含 cuda/nvdec) → ./bin"
|
||||
fi
|
||||
else
|
||||
FF_URL="${FFMPEG_STATIC_URL:-https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz}"
|
||||
if [ -x "$PROJ/bin/ffmpeg" ] && "$PROJ/bin/ffmpeg" -hide_banner -decoders 2>/dev/null | grep -iE 'dav1d|av1' >/dev/null; then
|
||||
log "[0/4] bin/ffmpeg 已支持 AV1(CPU 版),跳过"; FF_NEED=0
|
||||
else
|
||||
log "[0/4] 下载 CPU 版 ffmpeg (johnvansickle,含 dav1d) → ./bin"
|
||||
fi
|
||||
fi
|
||||
if [ "$FF_NEED" -eq 1 ]; then
|
||||
mkdir -p "$PROJ/bin"; tmp="$(mktemp -d)"
|
||||
if have curl; then curl -L "$FF_URL" -o "$tmp/ff.tar.xz"; else wget -O "$tmp/ff.tar.xz" "$FF_URL"; fi
|
||||
tar xf "$tmp/ff.tar.xz" -C "$tmp"
|
||||
# johnvansickle 在 <dir>/ffmpeg,BtbN 在 <dir>/bin/ffmpeg —— 直接全树找,两种都兼容
|
||||
ffb="$(find "$tmp" -type f -name ffmpeg | head -1)"
|
||||
ffp="$(find "$tmp" -type f -name ffprobe | head -1)"
|
||||
if [ -n "$ffb" ] && [ -n "$ffp" ]; then
|
||||
cp "$ffb" "$ffp" "$PROJ/bin/" && chmod +x "$PROJ/bin/ffmpeg" "$PROJ/bin/ffprobe"
|
||||
log "ffmpeg/ffprobe 已装到 ./bin($FFMPEG_VARIANT 版)"
|
||||
else
|
||||
echo "解压后没找到 ffmpeg/ffprobe,请手动放入 ./bin"; exit 1
|
||||
fi
|
||||
rm -rf "$tmp"
|
||||
fi
|
||||
|
||||
# 1) 把 uv 的独立 Python 复制进项目 ./python(python-build-standalone 本身可重定位)
|
||||
log "[1/4] 复制独立 Python 到项目内 ./python"
|
||||
PYSRC="$(dirname "$(dirname "$(readlink -f .venv/bin/python)")")"
|
||||
echo " 源: $PYSRC"
|
||||
rm -rf "$PROJ/python"
|
||||
cp -a "$PYSRC" "$PROJ/python"
|
||||
PYBIN="$PROJ/python/bin/python3"
|
||||
[ -x "$PYBIN" ] || PYBIN="$(ls "$PROJ"/python/bin/python3.* | head -1)"
|
||||
echo " 项目内 Python: $PYBIN"
|
||||
# 去掉 uv 的"外部管理(EXTERNALLY-MANAGED)"标记,否则 uv 拒绝往这个 Python 里装包
|
||||
rm -f "$PROJ"/python/lib/python3.*/EXTERNALLY-MANAGED
|
||||
|
||||
# 2) 把依赖装进这个项目内 Python(脱离 .venv,用 headless opencv)
|
||||
log "[2/4] 安装依赖到项目内 Python"
|
||||
uv pip uninstall --python "$PYBIN" --break-system-packages opencv-python 2>/dev/null || true
|
||||
uv pip install --python "$PYBIN" --break-system-packages -r requirements.txt --index-url "$PIP_MIRROR" \
|
||||
--only-binary numpy,scipy,scikit-learn,opencv-python-headless,Pillow,av \
|
||||
|| uv pip install --python "$PYBIN" --break-system-packages -r requirements.txt --index-url "$PIP_MIRROR"
|
||||
|
||||
# WHISPER_GPU=1:把 CUDA 库(cublas/cudnn)也装进项目 Python,这样目标机【无需系统 CUDA】
|
||||
# 也能让 whisper 跑 GPU(ctranslate2 从 venv 里找库;activate.sh 会把它加进 LD_LIBRARY_PATH)。
|
||||
if [ "${WHISPER_GPU:-0}" = "1" ]; then
|
||||
log " 额外装 CUDA 库(nvidia-cublas/cudnn-cu12)给 whisper GPU 用"
|
||||
uv pip install --python "$PYBIN" --break-system-packages \
|
||||
nvidia-cublas-cu12 nvidia-cudnn-cu12 --index-url "$PIP_MIRROR" \
|
||||
|| warn "CUDA 库安装失败;目标机若无系统 CUDA,whisper 将回退 CPU"
|
||||
fi
|
||||
|
||||
# 3) 把 whisper 模型缓存搬进项目 ./models(用 HF_HOME 指过去)
|
||||
log "[3/4] 预下载 whisper 模型到项目内 ./models"
|
||||
MODEL="$(python -c "import json;print(json.load(open('config/config.json'))['audio'].get('model_size','small'))")"
|
||||
mkdir -p "$PROJ/models"
|
||||
HF_HOME="$PROJ/models" HF_HUB_OFFLINE=0 TRANSFORMERS_OFFLINE=0 \
|
||||
HF_ENDPOINT="${HF_ENDPOINT:-https://hf-mirror.com}" \
|
||||
HF_HUB_DISABLE_XET=1 \
|
||||
"$PYBIN" -c "from faster_whisper import WhisperModel; WhisperModel('$MODEL',device='cpu',compute_type='int8'); print('模型已放进 ./models')"
|
||||
|
||||
# 4) 删掉旧 .venv(不再需要,已被项目内 Python 取代)
|
||||
log "[4/4] 清理旧 .venv"
|
||||
deactivate 2>/dev/null || true
|
||||
rm -rf "$PROJ/.venv"
|
||||
|
||||
cat <<EOF
|
||||
|
||||
完成 ✅ 现在项目已自包含,可随便放任意路径/用户:
|
||||
./python/ 独立 Python + 所有第三方库
|
||||
./bin/ ffmpeg / ffprobe
|
||||
./models/ whisper 模型
|
||||
coscli-linux-amd64
|
||||
|
||||
验证: source activate.sh && python -c "import cv2,faster_whisper;print('OK')"
|
||||
打包: bash pack.sh
|
||||
EOF
|
||||
Reference in New Issue
Block a user