refactor(docker): 完整重構 Dockerfile v0.1.16
BREAKING CHANGE: Dockerfile 結構重新設計 ## 重構內容 ### Multi-Stage Build 結構 - Stage 1 [base]: Bun runtime 基礎 - Stage 2 [install]: Node 依賴安裝 - Stage 3 [prerelease]: 應用程式建構 - Stage 4 [system-tools]: APT 系統工具(拆分為 14 個獨立 RUN) - Stage 5 [fonts]: 字型安裝 - Stage 6 [python-tools]: Python CLI 工具(拆分為 9 個獨立 RUN) - Stage 7 [models]: 模型下載 - Stage 8 [release]: 最終 Image ### 新增 Scripts - install-system-tools.sh: 系統工具安裝 - install-fonts.sh: 字型安裝 - install-python-tools.sh: Python 工具安裝 - download-models.sh: 模型下載 - verify-installation.sh: 完整安裝驗證 ### 刪除舊檔案 - Dockerfile.backup - Dockerfile.offline - Dockerfile.old ### 路徑更新 - MinerU 模型: /opt/convertx/models/mineru/PDF-Extract-Kit-1.0 - BabelDOC 快取: /root/.cache/babeldoc ### Multi-Arch 支援 - linux/amd64: 功能完整 - linux/arm64: 安全降級(MinerU/resvg 跳過) Closes #docker-refactor
This commit is contained in:
parent
e43649bc96
commit
55b0f519ab
10 changed files with 1797 additions and 2008 deletions
306
scripts/download-models.sh
Normal file
306
scripts/download-models.sh
Normal file
|
|
@ -0,0 +1,306 @@
|
|||
#!/usr/bin/env bash
|
||||
# ==============================================================================
|
||||
# ConvertX-CN 模型下載腳本
|
||||
# ==============================================================================
|
||||
# 用途:下載所有 AI 模型(MinerU, BabelDOC, PDFMathTranslate)
|
||||
# 執行環境:Docker build stage (models-download)
|
||||
#
|
||||
# ⚠️ 重要:此腳本在 build time 執行,下載所有模型到固定目錄
|
||||
# Runtime 完全離線,不得有任何下載行為
|
||||
# ==============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# 顏色輸出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${BLUE}📥 [INFO]${NC} $1"; }
|
||||
log_success() { echo -e "${GREEN}✅ [SUCCESS]${NC} $1"; }
|
||||
log_warn() { echo -e "${YELLOW}⚠️ [WARN]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}❌ [ERROR]${NC} $1"; }
|
||||
|
||||
# 架構檢測
|
||||
ARCH=$(uname -m)
|
||||
log_info "檢測架構: ${ARCH}"
|
||||
|
||||
# ==============================================================================
|
||||
# 目錄定義
|
||||
# ==============================================================================
|
||||
MODELS_BASE_DIR="/opt/convertx/models"
|
||||
MINERU_MODELS_DIR="${MODELS_BASE_DIR}/mineru"
|
||||
BABELDOC_CACHE_DIR="/root/.cache/babeldoc"
|
||||
BABELDOC_MODELS_DIR="${BABELDOC_CACHE_DIR}/models"
|
||||
BABELDOC_FONTS_DIR="${BABELDOC_CACHE_DIR}/fonts"
|
||||
BABELDOC_CMAP_DIR="${BABELDOC_CACHE_DIR}/cmap"
|
||||
BABELDOC_TIKTOKEN_DIR="${BABELDOC_CACHE_DIR}/tiktoken"
|
||||
|
||||
# ==============================================================================
|
||||
# 創建目錄結構
|
||||
# ==============================================================================
|
||||
create_directories() {
|
||||
log_info "創建模型目錄結構..."
|
||||
|
||||
mkdir -p "${MINERU_MODELS_DIR}"
|
||||
mkdir -p "${BABELDOC_MODELS_DIR}"
|
||||
mkdir -p "${BABELDOC_FONTS_DIR}"
|
||||
mkdir -p "${BABELDOC_CMAP_DIR}"
|
||||
mkdir -p "${BABELDOC_TIKTOKEN_DIR}"
|
||||
|
||||
log_success "目錄結構創建完成"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# 下載 MinerU Pipeline 模型
|
||||
# ==============================================================================
|
||||
download_mineru_models() {
|
||||
log_info "下載 MinerU Pipeline 模型..."
|
||||
|
||||
if [ "${ARCH}" = "aarch64" ]; then
|
||||
log_warn "ARM64:跳過 MinerU 模型下載(不支援)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
python3 << 'PYTHON'
|
||||
from huggingface_hub import snapshot_download
|
||||
import os
|
||||
|
||||
models_dir = os.environ.get('MINERU_MODELS_DIR', '/opt/convertx/models/mineru')
|
||||
print(f'下載 PDF-Extract-Kit-1.0 到 {models_dir}...')
|
||||
|
||||
try:
|
||||
snapshot_download(
|
||||
repo_id='opendatalab/PDF-Extract-Kit-1.0',
|
||||
local_dir=f'{models_dir}/PDF-Extract-Kit-1.0',
|
||||
local_dir_use_symlinks=False,
|
||||
resume_download=True
|
||||
)
|
||||
print('✅ PDF-Extract-Kit-1.0 下載完成')
|
||||
except Exception as e:
|
||||
print(f'❌ 下載失敗: {e}')
|
||||
raise
|
||||
PYTHON
|
||||
|
||||
# 顯示模型大小
|
||||
log_info "MinerU 模型目錄內容:"
|
||||
du -sh "${MINERU_MODELS_DIR}/PDF-Extract-Kit-1.0" 2>/dev/null || true
|
||||
ls -la "${MINERU_MODELS_DIR}/" 2>/dev/null || true
|
||||
|
||||
log_success "MinerU 模型下載完成"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# 產生 MinerU 配置檔
|
||||
# ==============================================================================
|
||||
generate_mineru_config() {
|
||||
log_info "產生 MinerU 配置檔..."
|
||||
|
||||
if [ "${ARCH}" = "aarch64" ]; then
|
||||
log_warn "ARM64:跳過 MinerU 配置"
|
||||
return 0
|
||||
fi
|
||||
|
||||
python3 << 'PYTHON'
|
||||
import json
|
||||
import os
|
||||
|
||||
mineru_models_dir = os.environ.get('MINERU_MODELS_DIR', '/opt/convertx/models/mineru')
|
||||
|
||||
config = {
|
||||
'models-dir': {
|
||||
'pipeline': f'{mineru_models_dir}/PDF-Extract-Kit-1.0',
|
||||
'vlm': ''
|
||||
},
|
||||
'model-source': 'local',
|
||||
'latex-delimiter-config': {
|
||||
'display': {'left': '@@', 'right': '@@'},
|
||||
'inline': {'left': '@', 'right': '@'}
|
||||
}
|
||||
}
|
||||
|
||||
# 寫入多個位置
|
||||
config_paths = [
|
||||
'/opt/convertx/mineru.json',
|
||||
'/root/mineru.json'
|
||||
]
|
||||
|
||||
for path in config_paths:
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
with open(path, 'w') as f:
|
||||
json.dump(config, f, indent=2)
|
||||
print(f'✅ 已產生: {path}')
|
||||
|
||||
print(json.dumps(config, indent=2))
|
||||
PYTHON
|
||||
|
||||
log_success "MinerU 配置檔產生完成"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# BabelDOC warmup(下載所有資源)
|
||||
# ==============================================================================
|
||||
babeldoc_warmup() {
|
||||
log_info "執行 BabelDOC warmup..."
|
||||
|
||||
export BABELDOC_CACHE_PATH="${BABELDOC_CACHE_DIR}"
|
||||
|
||||
if command -v babeldoc >/dev/null 2>&1; then
|
||||
babeldoc --warmup 2>&1 || {
|
||||
log_warn "BabelDOC warmup 可能有警告,但繼續"
|
||||
}
|
||||
|
||||
log_info "BabelDOC 快取內容:"
|
||||
ls -la "${BABELDOC_CACHE_DIR}/" 2>/dev/null || true
|
||||
du -sh "${BABELDOC_CACHE_DIR}/" 2>/dev/null || true
|
||||
else
|
||||
log_warn "babeldoc 不可用,跳過 warmup"
|
||||
fi
|
||||
|
||||
log_success "BabelDOC warmup 完成"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# 下載 tiktoken 編碼
|
||||
# ==============================================================================
|
||||
download_tiktoken() {
|
||||
log_info "下載 tiktoken 編碼..."
|
||||
|
||||
python3 << 'PYTHON'
|
||||
try:
|
||||
import tiktoken
|
||||
|
||||
encodings = ['cl100k_base', 'p50k_base', 'r50k_base']
|
||||
|
||||
for enc_name in encodings:
|
||||
try:
|
||||
enc = tiktoken.get_encoding(enc_name)
|
||||
print(f'✅ tiktoken {enc_name} 已下載')
|
||||
except Exception as e:
|
||||
print(f'⚠️ tiktoken {enc_name} 下載失敗: {e}')
|
||||
|
||||
except ImportError:
|
||||
print('⚠️ tiktoken 未安裝,跳過')
|
||||
PYTHON
|
||||
|
||||
log_success "tiktoken 編碼下載完成"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# 複製預下載的 ONNX 模型(從 COPY 複製)
|
||||
# ==============================================================================
|
||||
setup_onnx_models() {
|
||||
log_info "設定 ONNX 模型..."
|
||||
|
||||
# 如果有預下載的模型,複製到正確位置
|
||||
local ONNX_MODEL="doclayout_yolo_docstructbench_imgsz1024.onnx"
|
||||
local SRC_PATHS=(
|
||||
"/app/models/${ONNX_MODEL}"
|
||||
"/tmp/models/${ONNX_MODEL}"
|
||||
)
|
||||
|
||||
for src in "${SRC_PATHS[@]}"; do
|
||||
if [ -f "${src}" ]; then
|
||||
cp "${src}" "${BABELDOC_MODELS_DIR}/"
|
||||
log_success "已複製 ONNX 模型: ${src} -> ${BABELDOC_MODELS_DIR}/"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
log_warn "未找到預下載的 ONNX 模型,將依賴 babeldoc --warmup"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# 驗證下載結果
|
||||
# ==============================================================================
|
||||
verify_downloads() {
|
||||
log_info "驗證模型下載..."
|
||||
|
||||
echo ""
|
||||
echo "模型目錄統計:"
|
||||
echo "=============================================="
|
||||
|
||||
# MinerU
|
||||
if [ "${ARCH}" != "aarch64" ]; then
|
||||
if [ -d "${MINERU_MODELS_DIR}/PDF-Extract-Kit-1.0" ]; then
|
||||
local size
|
||||
size=$(du -sh "${MINERU_MODELS_DIR}/PDF-Extract-Kit-1.0" 2>/dev/null | awk '{print $1}')
|
||||
echo " ✅ MinerU Pipeline: ${size}"
|
||||
else
|
||||
echo " ❌ MinerU Pipeline: 不存在"
|
||||
fi
|
||||
|
||||
if [ -f "/root/mineru.json" ]; then
|
||||
echo " ✅ MinerU 配置檔: 已產生"
|
||||
else
|
||||
echo " ❌ MinerU 配置檔: 不存在"
|
||||
fi
|
||||
else
|
||||
echo " ⚠️ MinerU: ARM64 跳過"
|
||||
fi
|
||||
|
||||
# BabelDOC
|
||||
if [ -d "${BABELDOC_CACHE_DIR}" ]; then
|
||||
local size
|
||||
size=$(du -sh "${BABELDOC_CACHE_DIR}" 2>/dev/null | awk '{print $1}')
|
||||
echo " ✅ BabelDOC 快取: ${size}"
|
||||
else
|
||||
echo " ⚠️ BabelDOC 快取: 不存在"
|
||||
fi
|
||||
|
||||
# ONNX 模型
|
||||
if [ -f "${BABELDOC_MODELS_DIR}/doclayout_yolo_docstructbench_imgsz1024.onnx" ]; then
|
||||
local size
|
||||
size=$(ls -lh "${BABELDOC_MODELS_DIR}/doclayout_yolo_docstructbench_imgsz1024.onnx" | awk '{print $5}')
|
||||
echo " ✅ DocLayout-YOLO ONNX: ${size}"
|
||||
else
|
||||
echo " ⚠️ DocLayout-YOLO ONNX: 不存在"
|
||||
fi
|
||||
|
||||
echo "=============================================="
|
||||
log_success "驗證完成"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# 清理下載快取
|
||||
# ==============================================================================
|
||||
cleanup_cache() {
|
||||
log_info "清理下載快取..."
|
||||
|
||||
rm -rf /tmp/hf_download_cache 2>/dev/null || true
|
||||
rm -rf /root/.cache/huggingface 2>/dev/null || true
|
||||
rm -rf /root/.cache/pip 2>/dev/null || true
|
||||
rm -rf /root/.cache/uv 2>/dev/null || true
|
||||
|
||||
# 清理 Python cache
|
||||
find /usr -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
|
||||
|
||||
log_success "快取清理完成"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# 主程式
|
||||
# ==============================================================================
|
||||
main() {
|
||||
log_info "=========================================="
|
||||
log_info "開始下載模型 (${ARCH})"
|
||||
log_info "=========================================="
|
||||
|
||||
create_directories
|
||||
download_mineru_models
|
||||
generate_mineru_config
|
||||
setup_onnx_models
|
||||
babeldoc_warmup
|
||||
download_tiktoken
|
||||
verify_downloads
|
||||
cleanup_cache
|
||||
|
||||
log_success "=========================================="
|
||||
log_success "所有模型下載完成!"
|
||||
log_success "=========================================="
|
||||
}
|
||||
|
||||
# 執行
|
||||
main "$@"
|
||||
174
scripts/install-fonts.sh
Normal file
174
scripts/install-fonts.sh
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
#!/usr/bin/env bash
|
||||
# ==============================================================================
|
||||
# ConvertX-CN 字型安裝腳本
|
||||
# ==============================================================================
|
||||
# 用途:安裝所有系統字型和自訂字型
|
||||
# 執行環境:Docker build stage (fonts)
|
||||
# ==============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# 顏色輸出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${BLUE}🔤 [INFO]${NC} $1"; }
|
||||
log_success() { echo -e "${GREEN}✅ [SUCCESS]${NC} $1"; }
|
||||
log_warn() { echo -e "${YELLOW}⚠️ [WARN]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}❌ [ERROR]${NC} $1"; }
|
||||
|
||||
# 目錄定義
|
||||
CUSTOM_FONTS_DIR="/usr/share/fonts/truetype/custom"
|
||||
BABELDOC_FONTS_DIR="/root/.cache/babeldoc/fonts"
|
||||
|
||||
# ==============================================================================
|
||||
# 系統字型(APT)
|
||||
# ==============================================================================
|
||||
install_system_fonts() {
|
||||
log_info "安裝系統字型..."
|
||||
apt-get update --fix-missing
|
||||
apt-get install -y --no-install-recommends \
|
||||
fonts-noto-cjk \
|
||||
fonts-noto-cjk-extra \
|
||||
fonts-noto-core \
|
||||
fonts-noto-color-emoji \
|
||||
fonts-liberation \
|
||||
fonts-dejavu-core \
|
||||
fonts-dejavu-extra \
|
||||
fonts-freefont-ttf \
|
||||
fonts-droid-fallback
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
log_success "系統字型安裝完成"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# 複製自訂字型
|
||||
# ==============================================================================
|
||||
install_custom_fonts() {
|
||||
log_info "安裝自訂字型..."
|
||||
|
||||
# 創建目錄
|
||||
mkdir -p "${CUSTOM_FONTS_DIR}"
|
||||
mkdir -p "${BABELDOC_FONTS_DIR}"
|
||||
|
||||
# 檢查並複製字型
|
||||
local FONTS_SRC="${1:-/app/fonts}"
|
||||
|
||||
if [ -d "${FONTS_SRC}" ]; then
|
||||
log_info "複製字型從 ${FONTS_SRC}..."
|
||||
|
||||
# 複製所有 TTF 字型
|
||||
find "${FONTS_SRC}" -type f \( -name "*.ttf" -o -name "*.TTF" -o -name "*.otf" -o -name "*.OTF" \) \
|
||||
-exec cp -v {} "${CUSTOM_FONTS_DIR}/" \; 2>/dev/null || true
|
||||
|
||||
# 設定權限
|
||||
chmod 644 "${CUSTOM_FONTS_DIR}"/* 2>/dev/null || true
|
||||
|
||||
# 統計
|
||||
local COUNT
|
||||
COUNT=$(find "${CUSTOM_FONTS_DIR}" -type f | wc -l)
|
||||
log_success "已複製 ${COUNT} 個自訂字型"
|
||||
else
|
||||
log_warn "字型來源目錄 ${FONTS_SRC} 不存在"
|
||||
fi
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# 同步字型到 BabelDOC 目錄
|
||||
# ==============================================================================
|
||||
sync_babeldoc_fonts() {
|
||||
log_info "同步字型到 BabelDOC 目錄..."
|
||||
|
||||
mkdir -p "${BABELDOC_FONTS_DIR}"
|
||||
|
||||
# 複製關鍵字型到 BabelDOC
|
||||
local fonts=(
|
||||
"GoNotoKurrent-Regular.ttf"
|
||||
"SourceHanSerifCN-Regular.ttf"
|
||||
"SourceHanSerifTW-Regular.ttf"
|
||||
"SourceHanSerifJP-Regular.ttf"
|
||||
"SourceHanSerifKR-Regular.ttf"
|
||||
"BiauKai.ttf"
|
||||
)
|
||||
|
||||
for font in "${fonts[@]}"; do
|
||||
if [ -f "${CUSTOM_FONTS_DIR}/${font}" ]; then
|
||||
cp "${CUSTOM_FONTS_DIR}/${font}" "${BABELDOC_FONTS_DIR}/"
|
||||
log_info " 已同步: ${font}"
|
||||
fi
|
||||
done
|
||||
|
||||
log_success "BabelDOC 字型同步完成"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# 更新字型快取
|
||||
# ==============================================================================
|
||||
update_font_cache() {
|
||||
log_info "更新字型快取..."
|
||||
fc-cache -fv
|
||||
log_success "字型快取更新完成"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# 驗證字型安裝
|
||||
# ==============================================================================
|
||||
verify_fonts() {
|
||||
log_info "驗證字型安裝..."
|
||||
|
||||
echo "系統字型目錄統計:"
|
||||
echo " /usr/share/fonts: $(find /usr/share/fonts -type f \( -name '*.ttf' -o -name '*.otf' \) | wc -l) 個字型"
|
||||
echo " ${CUSTOM_FONTS_DIR}: $(find "${CUSTOM_FONTS_DIR}" -type f 2>/dev/null | wc -l) 個字型"
|
||||
echo " ${BABELDOC_FONTS_DIR}: $(find "${BABELDOC_FONTS_DIR}" -type f 2>/dev/null | wc -l) 個字型"
|
||||
|
||||
# 列出自訂字型
|
||||
if [ -d "${CUSTOM_FONTS_DIR}" ]; then
|
||||
echo ""
|
||||
echo "自訂字型清單:"
|
||||
ls -la "${CUSTOM_FONTS_DIR}/" 2>/dev/null || echo " (無)"
|
||||
fi
|
||||
|
||||
# 驗證關鍵字型
|
||||
echo ""
|
||||
echo "關鍵字型驗證:"
|
||||
local required_fonts=(
|
||||
"Noto Sans CJK"
|
||||
"Source Han Serif"
|
||||
"Liberation"
|
||||
)
|
||||
|
||||
for font in "${required_fonts[@]}"; do
|
||||
if fc-list | grep -qi "${font}"; then
|
||||
echo " ✅ ${font}: 已安裝"
|
||||
else
|
||||
echo " ⚠️ ${font}: 未找到"
|
||||
fi
|
||||
done
|
||||
|
||||
log_success "字型驗證完成"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# 主程式
|
||||
# ==============================================================================
|
||||
main() {
|
||||
log_info "=========================================="
|
||||
log_info "開始安裝字型"
|
||||
log_info "=========================================="
|
||||
|
||||
install_system_fonts
|
||||
install_custom_fonts "${1:-/app/fonts}"
|
||||
sync_babeldoc_fonts
|
||||
update_font_cache
|
||||
verify_fonts
|
||||
|
||||
log_success "=========================================="
|
||||
log_success "所有字型安裝完成!"
|
||||
log_success "=========================================="
|
||||
}
|
||||
|
||||
# 執行(可接收字型來源目錄參數)
|
||||
main "$@"
|
||||
219
scripts/install-python-tools.sh
Normal file
219
scripts/install-python-tools.sh
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
#!/usr/bin/env bash
|
||||
# ==============================================================================
|
||||
# ConvertX-CN Python 工具安裝腳本
|
||||
# ==============================================================================
|
||||
# 用途:安裝所有 Python CLI 工具
|
||||
# 執行環境:Docker build stage (python-tools)
|
||||
# ==============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# 顏色輸出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${BLUE}🐍 [INFO]${NC} $1"; }
|
||||
log_success() { echo -e "${GREEN}✅ [SUCCESS]${NC} $1"; }
|
||||
log_warn() { echo -e "${YELLOW}⚠️ [WARN]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}❌ [ERROR]${NC} $1"; }
|
||||
|
||||
# 架構檢測
|
||||
ARCH=$(uname -m)
|
||||
log_info "檢測架構: ${ARCH}"
|
||||
|
||||
# ==============================================================================
|
||||
# Python 基礎環境
|
||||
# ==============================================================================
|
||||
install_python_base() {
|
||||
log_info "安裝 Python 基礎環境..."
|
||||
apt-get update --fix-missing
|
||||
apt-get install -y --no-install-recommends \
|
||||
python3 \
|
||||
python3-pip \
|
||||
python3-venv \
|
||||
python3-numpy \
|
||||
python3-tinycss2 \
|
||||
python3-opencv \
|
||||
python3-img2pdf
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
log_success "Python 基礎環境安裝完成: $(python3 --version)"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# uv 套件管理器
|
||||
# ==============================================================================
|
||||
install_uv() {
|
||||
log_info "安裝 uv 套件管理器..."
|
||||
pip3 install --no-cache-dir --break-system-packages uv
|
||||
log_success "uv 安裝完成: $(uv --version)"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# huggingface_hub(模型下載)
|
||||
# ==============================================================================
|
||||
install_huggingface_hub() {
|
||||
log_info "安裝 huggingface_hub..."
|
||||
uv pip install --system --break-system-packages --no-cache huggingface_hub
|
||||
log_success "huggingface_hub 安裝完成"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# endesive(PDF 簽章)
|
||||
# ==============================================================================
|
||||
install_endesive() {
|
||||
log_info "安裝 endesive(PDF 簽章)..."
|
||||
|
||||
# endesive 需要編譯依賴
|
||||
apt-get update --fix-missing
|
||||
apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
swig \
|
||||
libpcsclite-dev \
|
||||
python3-dev
|
||||
|
||||
uv pip install --system --break-system-packages --no-cache endesive
|
||||
|
||||
# 清理編譯依賴
|
||||
apt-get remove -y build-essential swig python3-dev
|
||||
apt-get autoremove -y
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
log_success "endesive 安裝完成"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# markitdown
|
||||
# ==============================================================================
|
||||
install_markitdown() {
|
||||
log_info "安裝 markitdown..."
|
||||
uv pip install --system --break-system-packages --no-cache "markitdown[all]"
|
||||
log_success "markitdown 安裝完成"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# pdf2zh(PDFMathTranslate)
|
||||
# ==============================================================================
|
||||
install_pdf2zh() {
|
||||
log_info "安裝 pdf2zh..."
|
||||
uv pip install --system --break-system-packages --no-cache pdf2zh
|
||||
|
||||
if command -v pdf2zh >/dev/null 2>&1; then
|
||||
log_success "pdf2zh 安裝完成"
|
||||
else
|
||||
log_warn "pdf2zh 可能安裝到非標準路徑"
|
||||
fi
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# babeldoc
|
||||
# ==============================================================================
|
||||
install_babeldoc() {
|
||||
log_info "安裝 babeldoc..."
|
||||
uv pip install --system --break-system-packages --no-cache babeldoc || {
|
||||
log_warn "babeldoc 安裝可能有警告,但繼續"
|
||||
}
|
||||
|
||||
if command -v babeldoc >/dev/null 2>&1; then
|
||||
log_success "babeldoc 安裝完成"
|
||||
else
|
||||
log_warn "babeldoc 可能安裝到非標準路徑"
|
||||
fi
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# MinerU
|
||||
# ==============================================================================
|
||||
install_mineru() {
|
||||
log_info "安裝 MinerU..."
|
||||
|
||||
if [ "${ARCH}" = "aarch64" ]; then
|
||||
log_warn "ARM64:MinerU 不完全支援,跳過安裝"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# 使用 system-level 安裝
|
||||
uv pip install --system --break-system-packages --no-cache -U "mineru[all]"
|
||||
|
||||
# 驗證安裝
|
||||
if command -v mineru >/dev/null 2>&1; then
|
||||
log_success "MinerU 安裝完成: $(which mineru)"
|
||||
mineru --version 2>/dev/null || log_info "(版本資訊可能不可用)"
|
||||
else
|
||||
log_warn "MinerU 可能安裝到非標準路徑"
|
||||
fi
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# tiktoken(BabelDOC 依賴)
|
||||
# ==============================================================================
|
||||
install_tiktoken() {
|
||||
log_info "安裝 tiktoken..."
|
||||
uv pip install --system --break-system-packages --no-cache tiktoken
|
||||
log_success "tiktoken 安裝完成"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# 驗證安裝
|
||||
# ==============================================================================
|
||||
verify_installations() {
|
||||
log_info "驗證 Python 工具安裝..."
|
||||
|
||||
local tools=(
|
||||
"python3:Python"
|
||||
"pip3:pip"
|
||||
"uv:uv"
|
||||
"markitdown:markitdown"
|
||||
"pdf2zh:pdf2zh"
|
||||
"babeldoc:babeldoc"
|
||||
)
|
||||
|
||||
# MinerU 僅在 AMD64
|
||||
if [ "${ARCH}" != "aarch64" ]; then
|
||||
tools+=("mineru:MinerU")
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "工具驗證結果:"
|
||||
for tool_pair in "${tools[@]}"; do
|
||||
local cmd="${tool_pair%%:*}"
|
||||
local name="${tool_pair#*:}"
|
||||
|
||||
if command -v "${cmd}" >/dev/null 2>&1; then
|
||||
echo " ✅ ${name}: $(which "${cmd}")"
|
||||
else
|
||||
echo " ⚠️ ${name}: 未在 PATH 中找到"
|
||||
fi
|
||||
done
|
||||
|
||||
log_success "驗證完成"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# 主程式
|
||||
# ==============================================================================
|
||||
main() {
|
||||
log_info "=========================================="
|
||||
log_info "開始安裝 Python 工具 (${ARCH})"
|
||||
log_info "=========================================="
|
||||
|
||||
install_python_base
|
||||
install_uv
|
||||
install_huggingface_hub
|
||||
install_endesive
|
||||
install_markitdown
|
||||
install_pdf2zh
|
||||
install_babeldoc
|
||||
install_mineru
|
||||
install_tiktoken
|
||||
verify_installations
|
||||
|
||||
log_success "=========================================="
|
||||
log_success "所有 Python 工具安裝完成!"
|
||||
log_success "=========================================="
|
||||
}
|
||||
|
||||
# 執行
|
||||
main "$@"
|
||||
305
scripts/install-system-tools.sh
Normal file
305
scripts/install-system-tools.sh
Normal file
|
|
@ -0,0 +1,305 @@
|
|||
#!/usr/bin/env bash
|
||||
# ==============================================================================
|
||||
# ConvertX-CN 系統工具安裝腳本
|
||||
# ==============================================================================
|
||||
# 用途:安裝所有系統級轉換工具和依賴
|
||||
# 執行環境:Docker build stage (system-tools)
|
||||
# ==============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# 顏色輸出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
log_info() { echo -e "${BLUE}📦 [INFO]${NC} $1"; }
|
||||
log_success() { echo -e "${GREEN}✅ [SUCCESS]${NC} $1"; }
|
||||
log_warn() { echo -e "${YELLOW}⚠️ [WARN]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}❌ [ERROR]${NC} $1"; }
|
||||
|
||||
# 架構檢測
|
||||
ARCH=$(uname -m)
|
||||
log_info "檢測架構: ${ARCH}"
|
||||
|
||||
# ==============================================================================
|
||||
# APT 配置
|
||||
# ==============================================================================
|
||||
configure_apt() {
|
||||
log_info "配置 APT 重試機制..."
|
||||
cat > /etc/apt/apt.conf.d/80-retries <<EOF
|
||||
Acquire::Retries "5";
|
||||
Acquire::http::Timeout "120";
|
||||
Acquire::https::Timeout "120";
|
||||
Acquire::ftp::Timeout "120";
|
||||
APT::Get::Assume-Yes "true";
|
||||
DPkg::Lock::Timeout "120";
|
||||
EOF
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# 基礎系統工具
|
||||
# ==============================================================================
|
||||
install_base_tools() {
|
||||
log_info "安裝基礎系統工具..."
|
||||
apt-get update --fix-missing
|
||||
apt-get install -y --no-install-recommends \
|
||||
locales \
|
||||
ca-certificates \
|
||||
curl \
|
||||
wget \
|
||||
unzip \
|
||||
openssl \
|
||||
git \
|
||||
xz-utils
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
log_success "基礎系統工具安裝完成"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# 核心轉換工具(輕量)
|
||||
# ==============================================================================
|
||||
install_core_converters() {
|
||||
log_info "安裝核心轉換工具..."
|
||||
apt-get update --fix-missing
|
||||
apt-get install -y --no-install-recommends \
|
||||
assimp-utils \
|
||||
dcraw \
|
||||
dvisvgm \
|
||||
ghostscript \
|
||||
graphicsmagick \
|
||||
mupdf-tools \
|
||||
poppler-utils \
|
||||
potrace \
|
||||
qpdf
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
log_success "核心轉換工具安裝完成"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# dasel(JSON/YAML/TOML 轉換)
|
||||
# ==============================================================================
|
||||
install_dasel() {
|
||||
log_info "安裝 dasel..."
|
||||
local DASEL_VERSION="2.8.1"
|
||||
local DASEL_ARCH
|
||||
|
||||
if [ "${ARCH}" = "aarch64" ]; then
|
||||
DASEL_ARCH="linux_arm64"
|
||||
else
|
||||
DASEL_ARCH="linux_amd64"
|
||||
fi
|
||||
|
||||
curl -sSLf --retry 3 --retry-delay 5 --retry-all-errors \
|
||||
"https://github.com/TomWright/dasel/releases/download/v${DASEL_VERSION}/dasel_${DASEL_ARCH}" \
|
||||
-o /usr/local/bin/dasel
|
||||
chmod +x /usr/local/bin/dasel
|
||||
log_success "dasel 安裝完成: $(dasel --version)"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# resvg(SVG 渲染器)
|
||||
# ==============================================================================
|
||||
install_resvg() {
|
||||
log_info "安裝 resvg..."
|
||||
local RESVG_VERSION="0.44.0"
|
||||
|
||||
if [ "${ARCH}" = "aarch64" ]; then
|
||||
log_warn "resvg 無 ARM64 預編譯版本,跳過安裝"
|
||||
return 0
|
||||
fi
|
||||
|
||||
curl -sSLf --retry 3 --retry-delay 5 --retry-all-errors \
|
||||
"https://github.com/linebender/resvg/releases/download/v${RESVG_VERSION}/resvg-linux-x86_64.tar.gz" \
|
||||
-o /tmp/resvg.tar.gz
|
||||
tar -xzf /tmp/resvg.tar.gz -C /tmp/
|
||||
mv /tmp/resvg /usr/local/bin/resvg
|
||||
chmod +x /usr/local/bin/resvg
|
||||
rm -rf /tmp/resvg.tar.gz
|
||||
log_success "resvg 安裝完成"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# deark(檔案格式解碼器)
|
||||
# ==============================================================================
|
||||
install_deark() {
|
||||
log_info "編譯安裝 deark..."
|
||||
apt-get update --fix-missing
|
||||
apt-get install -y --no-install-recommends build-essential
|
||||
|
||||
cd /tmp
|
||||
git clone --depth 1 https://github.com/jsummers/deark.git
|
||||
cd deark
|
||||
make -j"$(nproc)"
|
||||
cp deark /usr/local/bin/deark
|
||||
chmod +x /usr/local/bin/deark
|
||||
|
||||
# 清理
|
||||
cd /
|
||||
rm -rf /tmp/deark
|
||||
apt-get remove -y build-essential
|
||||
apt-get autoremove -y
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
log_success "deark 安裝完成"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# vtracer(向量圖追蹤)
|
||||
# ==============================================================================
|
||||
install_vtracer() {
|
||||
log_info "安裝 vtracer..."
|
||||
local VTRACER_VERSION="0.6.4"
|
||||
local VTRACER_ASSET
|
||||
|
||||
if [ "${ARCH}" = "aarch64" ]; then
|
||||
VTRACER_ASSET="vtracer-aarch64-unknown-linux-musl.tar.gz"
|
||||
else
|
||||
VTRACER_ASSET="vtracer-x86_64-unknown-linux-musl.tar.gz"
|
||||
fi
|
||||
|
||||
curl -L --retry 3 --retry-delay 5 --retry-all-errors \
|
||||
-o /tmp/vtracer.tar.gz \
|
||||
"https://github.com/visioncortex/vtracer/releases/download/${VTRACER_VERSION}/${VTRACER_ASSET}"
|
||||
tar -xzf /tmp/vtracer.tar.gz -C /tmp/
|
||||
mv /tmp/vtracer /usr/local/bin/vtracer
|
||||
chmod +x /usr/local/bin/vtracer
|
||||
rm -rf /tmp/vtracer.tar.gz
|
||||
log_success "vtracer 安裝完成"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# FFmpeg(影音處理)
|
||||
# ==============================================================================
|
||||
install_ffmpeg() {
|
||||
log_info "安裝 FFmpeg..."
|
||||
apt-get update --fix-missing
|
||||
apt-get install -y --no-install-recommends \
|
||||
ffmpeg \
|
||||
libavcodec-extra \
|
||||
libva2
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
log_success "FFmpeg 安裝完成: $(ffmpeg -version | head -1)"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# 圖像處理工具(ImageMagick, Inkscape, vips 等)
|
||||
# ==============================================================================
|
||||
install_image_tools() {
|
||||
log_info "安裝圖像處理工具..."
|
||||
apt-get update --fix-missing
|
||||
apt-get install -y --no-install-recommends \
|
||||
imagemagick \
|
||||
inkscape \
|
||||
libheif-examples \
|
||||
libjxl-tools \
|
||||
libvips-tools \
|
||||
xauth \
|
||||
xvfb
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
log_success "圖像處理工具安裝完成"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# 文件處理工具(Calibre, Pandoc)
|
||||
# ==============================================================================
|
||||
install_document_tools() {
|
||||
log_info "安裝 Calibre + Pandoc..."
|
||||
apt-get update --fix-missing
|
||||
apt-get install -y --no-install-recommends \
|
||||
calibre \
|
||||
libemail-outlook-message-perl \
|
||||
pandoc
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
log_success "Calibre + Pandoc 安裝完成"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# LibreOffice
|
||||
# ==============================================================================
|
||||
install_libreoffice() {
|
||||
log_info "安裝 LibreOffice(需數分鐘)..."
|
||||
apt-get update --fix-missing
|
||||
apt-get install -y --no-install-recommends libreoffice
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
log_success "LibreOffice 安裝完成"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# TexLive(LaTeX)
|
||||
# ==============================================================================
|
||||
install_texlive() {
|
||||
log_info "安裝 TexLive 基礎..."
|
||||
apt-get update --fix-missing
|
||||
apt-get install -y --no-install-recommends \
|
||||
texlive-base \
|
||||
texlive-latex-base \
|
||||
texlive-latex-recommended \
|
||||
texlive-fonts-recommended \
|
||||
texlive-xetex \
|
||||
latexmk \
|
||||
lmodern
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
log_info "安裝 TexLive 語言包..."
|
||||
apt-get update --fix-missing
|
||||
apt-get install -y --no-install-recommends \
|
||||
texlive-lang-cjk \
|
||||
texlive-lang-german \
|
||||
texlive-lang-french \
|
||||
texlive-lang-arabic \
|
||||
texlive-lang-other
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
log_success "TexLive 安裝完成"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# Tesseract OCR
|
||||
# ==============================================================================
|
||||
install_tesseract() {
|
||||
log_info "安裝 Tesseract OCR..."
|
||||
apt-get update --fix-missing
|
||||
apt-get install -y --no-install-recommends \
|
||||
tesseract-ocr \
|
||||
tesseract-ocr-eng \
|
||||
tesseract-ocr-chi-tra \
|
||||
tesseract-ocr-chi-sim \
|
||||
tesseract-ocr-jpn \
|
||||
tesseract-ocr-kor \
|
||||
tesseract-ocr-deu \
|
||||
tesseract-ocr-fra \
|
||||
ocrmypdf
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
log_success "Tesseract OCR 安裝完成"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# 主程式
|
||||
# ==============================================================================
|
||||
main() {
|
||||
log_info "=========================================="
|
||||
log_info "開始安裝系統工具 (${ARCH})"
|
||||
log_info "=========================================="
|
||||
|
||||
configure_apt
|
||||
install_base_tools
|
||||
install_core_converters
|
||||
install_dasel
|
||||
install_resvg
|
||||
install_deark
|
||||
install_vtracer
|
||||
install_ffmpeg
|
||||
install_image_tools
|
||||
install_document_tools
|
||||
install_libreoffice
|
||||
install_texlive
|
||||
install_tesseract
|
||||
|
||||
log_success "=========================================="
|
||||
log_success "所有系統工具安裝完成!"
|
||||
log_success "=========================================="
|
||||
}
|
||||
|
||||
# 執行
|
||||
main "$@"
|
||||
341
scripts/verify-installation.sh
Normal file
341
scripts/verify-installation.sh
Normal file
|
|
@ -0,0 +1,341 @@
|
|||
#!/usr/bin/env bash
|
||||
# ==============================================================================
|
||||
# ConvertX-CN 安裝驗證腳本
|
||||
# ==============================================================================
|
||||
# 用途:在 Docker build 最終階段驗證所有工具和模型
|
||||
# 執行環境:Docker build stage (final) 或 runtime 驗證
|
||||
# ==============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# 顏色輸出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${BLUE}🔍 [INFO]${NC} $1"; }
|
||||
log_success() { echo -e "${GREEN}✅ [SUCCESS]${NC} $1"; }
|
||||
log_warn() { echo -e "${YELLOW}⚠️ [WARN]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}❌ [ERROR]${NC} $1"; }
|
||||
|
||||
# 架構檢測
|
||||
ARCH=$(uname -m)
|
||||
|
||||
# 計數器
|
||||
PASS=0
|
||||
FAIL=0
|
||||
WARN=0
|
||||
SKIP=0
|
||||
|
||||
# ==============================================================================
|
||||
# 檢查工具函數
|
||||
# ==============================================================================
|
||||
check_command() {
|
||||
local cmd="$1"
|
||||
local name="$2"
|
||||
local required="${3:-true}"
|
||||
local arch_only="${4:-all}"
|
||||
|
||||
# 架構檢查
|
||||
if [ "${arch_only}" != "all" ] && [ "${ARCH}" != "${arch_only}" ]; then
|
||||
echo " ⏭️ ${name}: 跳過(僅 ${arch_only})"
|
||||
((SKIP++))
|
||||
return 0
|
||||
fi
|
||||
|
||||
if command -v "${cmd}" >/dev/null 2>&1; then
|
||||
local path
|
||||
path=$(which "${cmd}")
|
||||
echo " ✅ ${name}: ${path}"
|
||||
((PASS++))
|
||||
else
|
||||
if [ "${required}" = "true" ]; then
|
||||
echo " ❌ ${name}: 未找到(必要)"
|
||||
((FAIL++))
|
||||
else
|
||||
echo " ⚠️ ${name}: 未找到(可選)"
|
||||
((WARN++))
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
check_file() {
|
||||
local path="$1"
|
||||
local name="$2"
|
||||
local required="${3:-true}"
|
||||
local arch_only="${4:-all}"
|
||||
|
||||
# 架構檢查
|
||||
if [ "${arch_only}" != "all" ] && [ "${ARCH}" != "${arch_only}" ]; then
|
||||
echo " ⏭️ ${name}: 跳過(僅 ${arch_only})"
|
||||
((SKIP++))
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ -f "${path}" ]; then
|
||||
local size
|
||||
size=$(ls -lh "${path}" 2>/dev/null | awk '{print $5}')
|
||||
echo " ✅ ${name}: ${size}"
|
||||
((PASS++))
|
||||
else
|
||||
if [ "${required}" = "true" ]; then
|
||||
echo " ❌ ${name}: 不存在"
|
||||
((FAIL++))
|
||||
else
|
||||
echo " ⚠️ ${name}: 不存在(可選)"
|
||||
((WARN++))
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
check_dir() {
|
||||
local path="$1"
|
||||
local name="$2"
|
||||
local required="${3:-true}"
|
||||
local arch_only="${4:-all}"
|
||||
|
||||
# 架構檢查
|
||||
if [ "${arch_only}" != "all" ] && [ "${ARCH}" != "${arch_only}" ]; then
|
||||
echo " ⏭️ ${name}: 跳過(僅 ${arch_only})"
|
||||
((SKIP++))
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ -d "${path}" ]; then
|
||||
local size
|
||||
size=$(du -sh "${path}" 2>/dev/null | awk '{print $1}')
|
||||
echo " ✅ ${name}: ${size}"
|
||||
((PASS++))
|
||||
else
|
||||
if [ "${required}" = "true" ]; then
|
||||
echo " ❌ ${name}: 不存在"
|
||||
((FAIL++))
|
||||
else
|
||||
echo " ⚠️ ${name}: 不存在(可選)"
|
||||
((WARN++))
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# 1. 核心系統工具
|
||||
# ==============================================================================
|
||||
verify_system_tools() {
|
||||
echo ""
|
||||
echo "📦 核心系統工具"
|
||||
echo "=============================================="
|
||||
|
||||
check_command "ffmpeg" "FFmpeg"
|
||||
check_command "convert" "ImageMagick"
|
||||
check_command "gm" "GraphicsMagick"
|
||||
check_command "vips" "libvips"
|
||||
check_command "inkscape" "Inkscape"
|
||||
check_command "resvg" "resvg" "false" "x86_64"
|
||||
check_command "dvisvgm" "dvisvgm"
|
||||
check_command "xelatex" "XeLaTeX"
|
||||
check_command "soffice" "LibreOffice"
|
||||
check_command "pandoc" "Pandoc"
|
||||
check_command "ebook-convert" "Calibre"
|
||||
check_command "assimp" "Assimp"
|
||||
check_command "dasel" "dasel"
|
||||
check_command "msgconvert" "msgconvert"
|
||||
check_command "deark" "deark"
|
||||
check_command "vtracer" "vtracer"
|
||||
check_command "heif-convert" "libheif"
|
||||
check_command "djxl" "libjxl"
|
||||
check_command "ocrmypdf" "OCRmyPDF"
|
||||
check_command "tesseract" "Tesseract"
|
||||
check_command "potrace" "Potrace"
|
||||
check_command "qpdf" "qpdf"
|
||||
check_command "gs" "Ghostscript"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# 2. Python 工具
|
||||
# ==============================================================================
|
||||
verify_python_tools() {
|
||||
echo ""
|
||||
echo "🐍 Python 工具"
|
||||
echo "=============================================="
|
||||
|
||||
check_command "python3" "Python"
|
||||
check_command "pip3" "pip"
|
||||
check_command "uv" "uv"
|
||||
check_command "markitdown" "markitdown"
|
||||
check_command "pdf2zh" "PDFMathTranslate"
|
||||
check_command "babeldoc" "BabelDOC"
|
||||
check_command "mineru" "MinerU" "true" "x86_64"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# 3. 模型檔案
|
||||
# ==============================================================================
|
||||
verify_models() {
|
||||
echo ""
|
||||
echo "🤖 AI 模型"
|
||||
echo "=============================================="
|
||||
|
||||
# MinerU 模型
|
||||
check_dir "/opt/convertx/models/mineru/PDF-Extract-Kit-1.0" "MinerU Pipeline" "true" "x86_64"
|
||||
check_file "/root/mineru.json" "MinerU 配置檔" "true" "x86_64"
|
||||
|
||||
# BabelDOC 模型
|
||||
check_file "/root/.cache/babeldoc/models/doclayout_yolo_docstructbench_imgsz1024.onnx" "DocLayout-YOLO ONNX" "false"
|
||||
check_dir "/root/.cache/babeldoc" "BabelDOC 快取" "false"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# 4. 字型
|
||||
# ==============================================================================
|
||||
verify_fonts() {
|
||||
echo ""
|
||||
echo "🔤 字型"
|
||||
echo "=============================================="
|
||||
|
||||
check_file "/usr/share/fonts/truetype/custom/GoNotoKurrent-Regular.ttf" "Noto 通用字型"
|
||||
check_file "/usr/share/fonts/truetype/custom/SourceHanSerifCN-Regular.ttf" "思源宋體(簡體)"
|
||||
check_file "/usr/share/fonts/truetype/custom/SourceHanSerifTW-Regular.ttf" "思源宋體(繁體)"
|
||||
check_file "/usr/share/fonts/truetype/custom/SourceHanSerifJP-Regular.ttf" "思源宋體(日文)"
|
||||
check_file "/usr/share/fonts/truetype/custom/SourceHanSerifKR-Regular.ttf" "思源宋體(韓文)"
|
||||
check_file "/usr/share/fonts/truetype/custom/BiauKai.ttf" "標楷體" "false"
|
||||
|
||||
# 系統字型
|
||||
echo ""
|
||||
echo " 系統字型統計:"
|
||||
local noto_count
|
||||
noto_count=$(fc-list | grep -ci "noto" || echo "0")
|
||||
echo " - Noto 字型: ${noto_count} 個"
|
||||
|
||||
local total_count
|
||||
total_count=$(fc-list | wc -l)
|
||||
echo " - 總字型數: ${total_count} 個"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# 5. OCR 語言
|
||||
# ==============================================================================
|
||||
verify_ocr_languages() {
|
||||
echo ""
|
||||
echo "📝 OCR 語言"
|
||||
echo "=============================================="
|
||||
|
||||
local languages=("eng" "chi_tra" "chi_sim" "jpn" "kor" "deu" "fra")
|
||||
|
||||
for lang in "${languages[@]}"; do
|
||||
if tesseract --list-langs 2>&1 | grep -q "^${lang}$"; then
|
||||
echo " ✅ ${lang}: 已安裝"
|
||||
((PASS++))
|
||||
else
|
||||
echo " ❌ ${lang}: 未安裝"
|
||||
((FAIL++))
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# 6. Locale 設定
|
||||
# ==============================================================================
|
||||
verify_locales() {
|
||||
echo ""
|
||||
echo "🌍 Locale 設定"
|
||||
echo "=============================================="
|
||||
|
||||
local locales=("en_US.UTF-8" "zh_TW.UTF-8" "zh_CN.UTF-8" "ja_JP.UTF-8" "ko_KR.UTF-8")
|
||||
|
||||
for locale in "${locales[@]}"; do
|
||||
if locale -a 2>/dev/null | grep -q "${locale}"; then
|
||||
echo " ✅ ${locale}: 已啟用"
|
||||
((PASS++))
|
||||
else
|
||||
echo " ⚠️ ${locale}: 未啟用"
|
||||
((WARN++))
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# 7. PDF 簽章
|
||||
# ==============================================================================
|
||||
verify_pdf_signing() {
|
||||
echo ""
|
||||
echo "🔐 PDF 簽章"
|
||||
echo "=============================================="
|
||||
|
||||
check_file "/app/certs/default.p12" "預設憑證" "false"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# 8. 離線模式驗證
|
||||
# ==============================================================================
|
||||
verify_offline_mode() {
|
||||
echo ""
|
||||
echo "🔒 離線模式環境變數"
|
||||
echo "=============================================="
|
||||
|
||||
local vars=(
|
||||
"HF_HUB_OFFLINE"
|
||||
"TRANSFORMERS_OFFLINE"
|
||||
"MINERU_MODEL_SOURCE"
|
||||
"BABELDOC_OFFLINE"
|
||||
)
|
||||
|
||||
for var in "${vars[@]}"; do
|
||||
local value="${!var:-}"
|
||||
if [ -n "${value}" ]; then
|
||||
echo " ✅ ${var}=${value}"
|
||||
((PASS++))
|
||||
else
|
||||
echo " ⚠️ ${var}: 未設定"
|
||||
((WARN++))
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# 總結
|
||||
# ==============================================================================
|
||||
print_summary() {
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "📊 驗證總結"
|
||||
echo "=============================================="
|
||||
echo " ✅ 通過: ${PASS}"
|
||||
echo " ❌ 失敗: ${FAIL}"
|
||||
echo " ⚠️ 警告: ${WARN}"
|
||||
echo " ⏭️ 跳過: ${SKIP}"
|
||||
echo "=============================================="
|
||||
|
||||
if [ "${FAIL}" -gt 0 ]; then
|
||||
log_error "驗證失敗!有 ${FAIL} 個必要項目未通過"
|
||||
return 1
|
||||
else
|
||||
log_success "驗證通過!"
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# 主程式
|
||||
# ==============================================================================
|
||||
main() {
|
||||
log_info "=========================================="
|
||||
log_info "ConvertX-CN 安裝驗證"
|
||||
log_info "架構: ${ARCH}"
|
||||
log_info "=========================================="
|
||||
|
||||
verify_system_tools
|
||||
verify_python_tools
|
||||
verify_models
|
||||
verify_fonts
|
||||
verify_ocr_languages
|
||||
verify_locales
|
||||
verify_pdf_signing
|
||||
verify_offline_mode
|
||||
|
||||
print_summary
|
||||
}
|
||||
|
||||
# 執行
|
||||
main "$@"
|
||||
|
|
@ -1,17 +1,18 @@
|
|||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
# ==============================================================================
|
||||
# ConvertX-CN 模型驗證腳本 v2.0
|
||||
# ConvertX-CN 模型驗證腳本 v3.0
|
||||
# ==============================================================================
|
||||
#
|
||||
# 用途:驗證 Docker Image 中的預下載模型是否完整
|
||||
# 執行:docker exec <container> /app/scripts/verify-models.sh
|
||||
#
|
||||
# 版本:v3.0 - 適配重構後的目錄結構
|
||||
# ==============================================================================
|
||||
|
||||
set -e
|
||||
set -euo pipefail
|
||||
|
||||
echo "========================================"
|
||||
echo "🔍 ConvertX-CN 模型驗證 v2.0"
|
||||
echo "🔍 ConvertX-CN 模型驗證 v3.0"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
|
||||
|
|
@ -21,12 +22,18 @@ FAIL=0
|
|||
WARN=0
|
||||
ARCH=$(uname -m)
|
||||
|
||||
# 新的模型目錄結構
|
||||
MODELS_BASE_DIR="/opt/convertx/models"
|
||||
MINERU_MODELS_DIR="${MODELS_BASE_DIR}/mineru"
|
||||
BABELDOC_CACHE_DIR="/root/.cache/babeldoc"
|
||||
|
||||
# 檢查函數
|
||||
check_file() {
|
||||
local path="$1"
|
||||
local name="$2"
|
||||
if [ -f "$path" ]; then
|
||||
local size=$(ls -lh "$path" | awk '{print $5}')
|
||||
local size
|
||||
size=$(ls -lh "$path" | awk '{print $5}')
|
||||
echo "✅ $name: $path ($size)"
|
||||
((PASS++))
|
||||
else
|
||||
|
|
@ -40,7 +47,8 @@ check_dir() {
|
|||
local name="$2"
|
||||
local required="${3:-false}"
|
||||
if [ -d "$path" ]; then
|
||||
local size=$(du -sh "$path" 2>/dev/null | awk '{print $1}')
|
||||
local size
|
||||
size=$(du -sh "$path" 2>/dev/null | awk '{print $1}')
|
||||
echo "✅ $name: $path ($size)"
|
||||
((PASS++))
|
||||
else
|
||||
|
|
@ -59,7 +67,7 @@ check_dir() {
|
|||
# ==============================================================================
|
||||
echo "📦 PDFMathTranslate / BabelDOC 模型"
|
||||
echo "----------------------------------------"
|
||||
check_file "/root/.cache/babeldoc/models/doclayout_yolo_docstructbench_imgsz1024.onnx" "DocLayout-YOLO ONNX"
|
||||
check_file "${BABELDOC_CACHE_DIR}/models/doclayout_yolo_docstructbench_imgsz1024.onnx" "DocLayout-YOLO ONNX"
|
||||
echo ""
|
||||
|
||||
# ==============================================================================
|
||||
|
|
@ -72,10 +80,11 @@ check_file "/usr/share/fonts/truetype/custom/SourceHanSerifCN-Regular.ttf" "思
|
|||
check_file "/usr/share/fonts/truetype/custom/SourceHanSerifTW-Regular.ttf" "思源宋體(繁體)"
|
||||
check_file "/usr/share/fonts/truetype/custom/SourceHanSerifJP-Regular.ttf" "思源宋體(日文)"
|
||||
check_file "/usr/share/fonts/truetype/custom/SourceHanSerifKR-Regular.ttf" "思源宋體(韓文)"
|
||||
check_file "/usr/share/fonts/truetype/custom/BiauKai.ttf" "標楷體"
|
||||
echo ""
|
||||
|
||||
# ==============================================================================
|
||||
# 3. MinerU 模型(新路徑:/opt/mineru)
|
||||
# 3. MinerU 模型(新路徑:/opt/convertx/models/mineru)
|
||||
# ==============================================================================
|
||||
echo "📦 MinerU 模型"
|
||||
echo "----------------------------------------"
|
||||
|
|
@ -83,8 +92,9 @@ if [ "$ARCH" = "aarch64" ]; then
|
|||
echo "⚠️ ARM64 架構:MinerU 不支援,跳過驗證"
|
||||
((WARN++))
|
||||
else
|
||||
check_dir "/opt/mineru/models/PDF-Extract-Kit-1.0" "PDF-Extract-Kit-1.0 Pipeline" "true"
|
||||
check_dir "${MINERU_MODELS_DIR}/PDF-Extract-Kit-1.0" "PDF-Extract-Kit-1.0 Pipeline" "true"
|
||||
check_file "/root/mineru.json" "MinerU 配置檔"
|
||||
check_file "/opt/convertx/mineru.json" "MinerU 配置檔(備份)"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
|
|
@ -93,9 +103,9 @@ echo ""
|
|||
# ==============================================================================
|
||||
echo "📦 BabelDOC 快取"
|
||||
echo "----------------------------------------"
|
||||
check_dir "/root/.cache/babeldoc" "BabelDOC 快取"
|
||||
check_dir "/root/.cache/babeldoc/models" "BabelDOC Models"
|
||||
check_dir "/root/.cache/babeldoc/fonts" "BabelDOC Fonts"
|
||||
check_dir "${BABELDOC_CACHE_DIR}" "BabelDOC 快取"
|
||||
check_dir "${BABELDOC_CACHE_DIR}/models" "BabelDOC Models"
|
||||
check_dir "${BABELDOC_CACHE_DIR}/fonts" "BabelDOC Fonts"
|
||||
echo ""
|
||||
|
||||
# ==============================================================================
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ else
|
|||
check_command "mineru" "MinerU CLI"
|
||||
|
||||
# 檢查模型目錄
|
||||
MINERU_MODELS="/opt/mineru/models/PDF-Extract-Kit-1.0"
|
||||
MINERU_MODELS="/opt/convertx/models/mineru/PDF-Extract-Kit-1.0"
|
||||
check_dir "$MINERU_MODELS" "MinerU Pipeline 模型" "true"
|
||||
|
||||
# 檢查配置檔
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue