首次提交
This commit is contained in:
244
scripts/setup_server.sh
Normal file
244
scripts/setup_server.sh
Normal file
@@ -0,0 +1,244 @@
|
||||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# 视频字幕提取系统 — 服务器一键环境配置脚本
|
||||
#
|
||||
# 安装/配置:gcc 编译工具链、ffmpeg(含 AV1/dav1d 解码)、uv、Python 虚拟环境、
|
||||
# 项目依赖,并处理常见环境坑(opencv libGL、UTF-8 locale、HF/pip 镜像、
|
||||
# OMP 线程、文件句柄上限)。
|
||||
#
|
||||
# 用法(在项目根目录):
|
||||
# bash setup_server.sh
|
||||
# 跑完后:
|
||||
# source activate.sh # 激活环境(设置 venv + 各种环境变量)
|
||||
# python main.py --source-list ./text2 --max-workers 12
|
||||
#
|
||||
# 幂等:可重复运行,已装的会跳过。
|
||||
# =============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
# ----------------------------- 可调参数 --------------------------------------
|
||||
PYTHON_VERSION="${PYTHON_VERSION:-3.11}"
|
||||
PIP_MIRROR="${PIP_MIRROR:-https://repo.huaweicloud.com/repository/pypi/simple}"
|
||||
HF_ENDPOINT_URL="${HF_ENDPOINT_URL:-https://hf-mirror.com}"
|
||||
FFMPEG_STATIC_URL="${FFMPEG_STATIC_URL:-https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz}"
|
||||
PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
VENV_DIR="$PROJECT_DIR/.venv"
|
||||
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
# 让本脚本(及自检)优先用项目 bin/ 里的 ffmpeg(我们装的静态版)
|
||||
export PATH="$PROJECT_DIR/bin:$PATH"
|
||||
|
||||
# ----------------------------- 工具函数 --------------------------------------
|
||||
log() { echo -e "\n\033[1;32m==> $*\033[0m"; }
|
||||
warn() { echo -e "\033[1;33m[警告] $*\033[0m"; }
|
||||
err() { echo -e "\033[1;31m[错误] $*\033[0m" >&2; }
|
||||
have() { command -v "$1" >/dev/null 2>&1; }
|
||||
|
||||
# root 则不用 sudo,否则用 sudo
|
||||
if [ "$(id -u)" -eq 0 ]; then SUDO=""; else SUDO="sudo"; fi
|
||||
|
||||
# 检测包管理器
|
||||
PKG=""
|
||||
if have dnf; then PKG="dnf"
|
||||
elif have yum; then PKG="yum"
|
||||
elif have apt-get; then PKG="apt"
|
||||
else warn "未识别的包管理器,跳过系统包安装(请确保已手动装好 gcc/ffmpeg 等)"; fi
|
||||
|
||||
# 解压后 coscli/bin 里的二进制常丢失 +x,先无条件加执行权限(放最前,确保一定执行)
|
||||
for b in coscli-linux-amd64 coscli bin/ffmpeg bin/ffprobe; do
|
||||
[ -f "$PROJECT_DIR/$b" ] && chmod +x "$PROJECT_DIR/$b" 2>/dev/null || true
|
||||
done
|
||||
log "已给 coscli / bin 二进制加执行权限"
|
||||
|
||||
# =============================================================================
|
||||
log "[1/7] 安装系统依赖(gcc 工具链 + opencv/运行库)"
|
||||
# =============================================================================
|
||||
# 关键环境坑:
|
||||
# - opencv-python 在无图形界面的服务器上 import 会报 libGL.so.1 缺失 → 装 mesa-libGL/libgl1
|
||||
# - 还需要 glib2(libgthread-2.0) 否则 opencv 也可能报错
|
||||
# - gcc/gcc-c++/make:少数包没有 wheel 时要本地编译
|
||||
case "$PKG" in
|
||||
dnf|yum)
|
||||
# openblas-devel:万一 scipy/numpy 走源码编译时需要(优先还是用 wheel)
|
||||
$SUDO $PKG install -y gcc gcc-c++ gcc-gfortran make git tar xz which \
|
||||
mesa-libGL glib2 openblas-devel || warn "部分系统包安装失败,继续"
|
||||
;;
|
||||
apt)
|
||||
$SUDO apt-get update -y || true
|
||||
$SUDO apt-get install -y build-essential gfortran git tar xz-utils curl \
|
||||
libgl1 libglib2.0-0 libopenblas-dev || warn "部分系统包安装失败,继续"
|
||||
;;
|
||||
esac
|
||||
|
||||
# =============================================================================
|
||||
log "[2/7] 安装 ffmpeg(含 AV1/dav1d 解码) → 项目目录 bin/"
|
||||
# =============================================================================
|
||||
# 直接下载静态构建(johnvansickle,自带 dav1d)到项目的 bin/,代码会自动优先用它
|
||||
# (config_utils 启动时把 bin/ 提到 PATH 最前)。不依赖系统 ffmpeg,也不用 sudo。
|
||||
# 注意:CPU 版(无 NVDEC);要 GPU 硬解需另装 cuda 版 ffmpeg。
|
||||
FFMPEG_DIR="$PROJECT_DIR/bin"
|
||||
LOCAL_FFMPEG="$FFMPEG_DIR/ffmpeg"
|
||||
need_ffmpeg=1
|
||||
if [ -x "$LOCAL_FFMPEG" ] && "$LOCAL_FFMPEG" -hide_banner -decoders 2>/dev/null | grep -qiE 'dav1d|av1'; then
|
||||
log "项目自带 ffmpeg 已存在且支持 AV1: $LOCAL_FFMPEG,跳过下载"
|
||||
need_ffmpeg=0
|
||||
fi
|
||||
if [ "$need_ffmpeg" -eq 1 ]; then
|
||||
if have curl || have wget; then
|
||||
mkdir -p "$FFMPEG_DIR"
|
||||
tmp="$(mktemp -d)"
|
||||
log "下载 ffmpeg 静态包到 $FFMPEG_DIR ..."
|
||||
if have curl; then curl -L "$FFMPEG_STATIC_URL" -o "$tmp/ff.tar.xz"
|
||||
else wget -O "$tmp/ff.tar.xz" "$FFMPEG_STATIC_URL"; fi
|
||||
tar -xf "$tmp/ff.tar.xz" -C "$tmp"
|
||||
ffdir="$(find "$tmp" -maxdepth 1 -type d -name 'ffmpeg-*' | head -1)"
|
||||
if [ -n "$ffdir" ]; then
|
||||
cp "$ffdir/ffmpeg" "$ffdir/ffprobe" "$FFMPEG_DIR/"
|
||||
chmod +x "$FFMPEG_DIR/ffmpeg" "$FFMPEG_DIR/ffprobe"
|
||||
log "ffmpeg/ffprobe 已装到 $FFMPEG_DIR"
|
||||
else
|
||||
err "解压后未找到 ffmpeg 目录,请手动安装 ffmpeg"
|
||||
fi
|
||||
rm -rf "$tmp"
|
||||
else
|
||||
err "没有 curl/wget,无法下载 ffmpeg,请手动安装"
|
||||
fi
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
log "[3/7] 安装 uv"
|
||||
# =============================================================================
|
||||
if ! have uv; then
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
# uv 默认装到 ~/.local/bin 或 ~/.cargo/bin
|
||||
export PATH="$HOME/.local/bin:$HOME/.cargo/bin:$PATH"
|
||||
fi
|
||||
have uv || { err "uv 安装失败,请检查网络"; exit 1; }
|
||||
log "uv 版本: $(uv --version)"
|
||||
|
||||
# =============================================================================
|
||||
log "[4/7] 用 uv 安装 Python ${PYTHON_VERSION}"
|
||||
# =============================================================================
|
||||
uv python install "$PYTHON_VERSION" || warn "uv python install 失败(可能已装)"
|
||||
|
||||
# =============================================================================
|
||||
log "[5/7] 创建虚拟环境并安装依赖"
|
||||
# =============================================================================
|
||||
# 优先用 pyproject.toml + uv.lock(规范、可复现);没有则回退 requirements.txt。
|
||||
# pyproject 的 [tool.uv] 已配置:禁止 av/numpy/sklearn 本地编译 + 清华镜像。
|
||||
# 用 uv venv + uv pip install(现代解析器)。--only-binary 让它自动挑有 wheel 的版本,
|
||||
# 避免某些受限镜像把新版本只给源码包导致本地编译/报错(不像 uv sync 的 no-build 会硬失败)。
|
||||
[ -d "$VENV_DIR" ] || uv venv --python "$PYTHON_VERSION" "$VENV_DIR"
|
||||
# shellcheck disable=SC1091
|
||||
source "$VENV_DIR/bin/activate"
|
||||
if [ -f requirements.txt ]; then
|
||||
uv pip install -r requirements.txt --index-url "$PIP_MIRROR" \
|
||||
--only-binary numpy,scipy,scikit-learn,opencv-python-headless,Pillow,av \
|
||||
|| uv pip install -r requirements.txt --index-url "$PIP_MIRROR"
|
||||
else
|
||||
warn "未找到 requirements.txt,跳过依赖安装"
|
||||
fi
|
||||
|
||||
# 激活 venv 供后续自检使用
|
||||
# shellcheck disable=SC1091
|
||||
[ -f "$VENV_DIR/bin/activate" ] && source "$VENV_DIR/bin/activate"
|
||||
log "Python: $(python --version 2>&1) 位置: $(command -v python)"
|
||||
|
||||
# =============================================================================
|
||||
log "[6/7] 写出环境变量脚本 activate.sh"
|
||||
# =============================================================================
|
||||
# 常见环境坑统一在这里处理,跑项目前 source 一下即可
|
||||
# 用单引号 heredoc 让脚本体保持字面(不在生成时展开),实现"自动定位所在目录",
|
||||
# 这样整个项目可以搬到任意路径,activate.sh 仍能找到自己的 .venv / bin。
|
||||
cat > "$PROJECT_DIR/activate.sh" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
# 跑项目前先: source activate.sh —— 自动定位项目目录,项目可放任意路径
|
||||
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
# Python:优先项目内自包含的 ./python(make_portable.sh 产物,可随便放);否则用 .venv
|
||||
if [ -x "$HERE/python/bin/python3" ]; then
|
||||
export PATH="$HERE/bin:$HERE/python/bin:$PATH"
|
||||
else
|
||||
export VIRTUAL_ENV="$HERE/.venv"
|
||||
export PATH="$HERE/bin:$VIRTUAL_ENV/bin:$HOME/.local/bin:$HOME/.cargo/bin:$PATH"
|
||||
fi
|
||||
|
||||
# UTF-8:项目有中文文件名(数据.jsonl)。优先选系统存在的 UTF-8 locale,避免 setlocale 警告;
|
||||
# 并强制 Python 用 UTF-8(即使系统没有 UTF-8 locale 也能正确处理中文文件名)。
|
||||
if locale -a 2>/dev/null | grep -qiE '^C\.(utf-?8|UTF-?8)$'; then
|
||||
export LANG=C.UTF-8 LC_ALL=C.UTF-8
|
||||
elif locale -a 2>/dev/null | grep -qiE 'en_US\.(utf-?8|UTF-?8)'; then
|
||||
export LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8
|
||||
fi
|
||||
export PYTHONUTF8=1
|
||||
export PYTHONIOENCODING=utf-8
|
||||
|
||||
# HuggingFace 镜像(联网下模型时用)
|
||||
export HF_ENDPOINT=__HF_ENDPOINT__
|
||||
# 模型缓存:项目内有 ./models 就用它(自包含包),否则用默认 ~/.cache/huggingface
|
||||
[ -d "$HERE/models" ] && export HF_HOME="$HERE/models"
|
||||
# 只在模型【确实已缓存】时才强制离线,否则允许联网下载(避免"没模型又禁网"的死锁)
|
||||
if ls "${HF_HOME:-$HOME/.cache/huggingface}"/hub/models--*faster-whisper* >/dev/null 2>&1; then
|
||||
export HF_HUB_OFFLINE=1
|
||||
export TRANSFORMERS_OFFLINE=1
|
||||
fi
|
||||
|
||||
# 若项目自带 CUDA 库(WHISPER_GPU=1 打包),加进 LD_LIBRARY_PATH,让 ctranslate2 找到(whisper GPU)
|
||||
_nvlibs="$(ls -d "$HERE"/python/lib/python*/site-packages/nvidia/*/lib 2>/dev/null | tr '\n' ':')"
|
||||
[ -n "$_nvlibs" ] && export LD_LIBRARY_PATH="${_nvlibs}${LD_LIBRARY_PATH:-}"
|
||||
|
||||
# 限制数值库线程:防止 whisper/numpy/openblas 每个都抢满核(多视频并发时互相踩)
|
||||
export OMP_NUM_THREADS=2
|
||||
export OPENBLAS_NUM_THREADS=2
|
||||
export MKL_NUM_THREADS=2
|
||||
|
||||
echo "环境已激活: $(python --version 2>&1) | $HERE"
|
||||
EOF
|
||||
sed -i "s|__HF_ENDPOINT__|$HF_ENDPOINT_URL|" "$PROJECT_DIR/activate.sh"
|
||||
chmod +x "$PROJECT_DIR/activate.sh"
|
||||
|
||||
# 提高文件句柄上限(高并发 + 海量小帧文件),当前会话生效;永久需改 /etc/security/limits.conf
|
||||
ulimit -n 65535 2>/dev/null || warn "ulimit 提升失败(非致命),如需永久生效请改 /etc/security/limits.conf"
|
||||
|
||||
|
||||
# =============================================================================
|
||||
log "[7/7] 环境自检"
|
||||
# =============================================================================
|
||||
ok=1
|
||||
check() { if eval "$2" >/dev/null 2>&1; then echo " ✓ $1"; else echo " ✗ $1"; ok=0; fi; }
|
||||
|
||||
check "gcc" "gcc --version"
|
||||
check "ffmpeg" "ffmpeg -version"
|
||||
check "ffprobe" "ffprobe -version"
|
||||
check "ffmpeg 支持 AV1" "ffmpeg -hide_banner -decoders | grep -iE 'dav1d|av1'"
|
||||
check "uv" "uv --version"
|
||||
check "python" "python --version"
|
||||
check "import cv2" "python -c 'import cv2'"
|
||||
check "import numpy" "python -c 'import numpy'"
|
||||
check "import sklearn" "python -c 'import sklearn'"
|
||||
check "import av(PyAV)" "python -c 'import av'"
|
||||
check "import faster_whisper" "python -c 'import faster_whisper'"
|
||||
check "import qcloud_cos" "python -c 'import qcloud_cos'"
|
||||
|
||||
# GPU 检测(可选,有则抽帧能硬解加速)
|
||||
if have nvidia-smi && nvidia-smi >/dev/null 2>&1; then
|
||||
echo " ✓ 检测到 NVIDIA GPU(抽帧可硬解加速)"
|
||||
else
|
||||
echo " · 未检测到 GPU(抽帧用 CPU,正常)"
|
||||
fi
|
||||
|
||||
echo
|
||||
if [ "$ok" -eq 1 ]; then
|
||||
log "全部就绪 ✅"
|
||||
else
|
||||
warn "有项目未通过(见上面 ✗),请按提示排查"
|
||||
fi
|
||||
|
||||
cat <<EOF
|
||||
|
||||
下一步:
|
||||
source activate.sh
|
||||
python main.py --source-list ./text2 --max-workers 12
|
||||
|
||||
EOF
|
||||
Reference in New Issue
Block a user