#!/usr/bin/env bash # ============================================================================= # 离线部署打包脚本(在【能联网】的机器上跑) # # 两种模式自动识别: # A) 自包含模式(已跑过 make_portable.sh,项目内有 ./python + ./models) # → 打包里只含项目这一个文件夹,离线机解压到【任意路径/任意用户】即可。 # B) venv 模式(只有 .venv) # → 打项目 + /root 下的 uv Python + HF 缓存,离线机要解压到相同路径。 # # 用法: bash pack.sh # 产物: /tmp/ocr_offline_bundle.tar.gz # ============================================================================= set -euo pipefail cd "$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" PROJECT_DIR="$(pwd)" OUT="${1:-/tmp/ocr_offline_bundle.tar.gz}" 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; } PBASE="$(basename "$PROJECT_DIR")" PPARENT="$(dirname "$PROJECT_DIR")" # 运行时数据排除(部署不需要,打进去又慢又大)。用项目目录名作前缀,两种模式通用。 RT_DIRS=(split video composite coscli_output srt info info_error log) EXCLUDES=(--exclude='*.log' --exclude='__pycache__' --exclude='*.pyc' --exclude="$PBASE/数据.jsonl") for d in "${RT_DIRS[@]}"; do EXCLUDES+=(--exclude="$PBASE/$d"); done # 选最快的压缩器:pigz 多线程(输出仍是标准 gzip,离线机普通 tar xzf 就能解) if have pigz; then COMPRESS="pigz"; log "用 pigz 多线程压缩(快)" else COMPRESS="gzip -1"; warn "未装 pigz,用 gzip -1;想更快可先 yum install -y pigz" fi if [ -x "$PROJECT_DIR/python/bin/python3" ]; then # ---------- A) 自包含:只打项目目录(用相对名),可解压到任意路径 ---------- log "自包含模式(项目内含 ./python + ./models)" [ -d "$PROJECT_DIR/models" ] || warn "没看到 ./models,确认 make_portable.sh 跑完整了" tar "${EXCLUDES[@]}" --use-compress-program="$COMPRESS" -cf "$OUT" \ -C "$PPARENT" "$PBASE" MODE="自包含(解压到任意路径/用户)" else # ---------- B) venv:打项目(绝对路径) + 外部 Python + HF 缓存 ---------- [ -d .venv ] || { echo "既没有 ./python 也没有 .venv,请先 setup_server.sh 或 make_portable.sh"; exit 1; } # shellcheck disable=SC1091 source .venv/bin/activate log "venv 模式:确保 headless opencv + 预下载模型" uv pip uninstall opencv-python 2>/dev/null || true uv pip install opencv-python-headless \ --index-url "${PIP_MIRROR:-https://repo.huaweicloud.com/repository/pypi/simple}" 2>/dev/null \ || echo "(headless 可能已装,忽略)" MODEL="$(python -c "import json;print(json.load(open('config/config.json'))['audio'].get('model_size','small'))")" HF_HUB_OFFLINE=0 TRANSFORMERS_OFFLINE=0 HF_ENDPOINT="${HF_ENDPOINT:-https://hf-mirror.com}" \ python -c "from faster_whisper import WhisperModel; WhisperModel('$MODEL',device='cpu',compute_type='int8'); print('模型已缓存')" PYDIR="$(dirname "$(dirname "$(readlink -f .venv/bin/python)")")" HFCACHE="${HF_HOME:-$HOME/.cache/huggingface}" echo " uv Python: $PYDIR 模型缓存: $HFCACHE" log "打包项目(绝对路径) + Python + 模型缓存" # 项目用绝对路径(还原到原位),外部两目录同理 EXABS=(--exclude='*.log' --exclude='__pycache__' --exclude='*.pyc' --exclude="${PROJECT_DIR#/}/数据.jsonl") for d in "${RT_DIRS[@]}"; do EXABS+=(--exclude="${PROJECT_DIR#/}/$d"); done tar "${EXABS[@]}" --use-compress-program="$COMPRESS" -cf "$OUT" \ -C / "${PROJECT_DIR#/}" -C / "${PYDIR#/}" -C / "${HFCACHE#/}" MODE="venv(离线机需解压到相同路径/用户)" fi echo log "完成 ✅ $OUT ($(du -h "$OUT" | cut -f1))" echo "模式: $MODE" cat <