项目文件夹

文件
wehub-resource-sync 41b710f9c7
CI / Backend tests (push) Failing after 1s
CI / Frontend checks (push) Failing after 0s
I18n Documentation Sync / sync-docs (push) Failing after 0s
chore: import upstream snapshot with attribution
2026-07-13 12:28:40 +08:00

592 行
16 KiB
Bash
可执行文件

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
#!/bin/bash
# AutoClip 一键启动脚本
# 版本: 2.0
# 功能: 启动完整的AutoClip系统(后端API + Celery Worker + 前端界面)
set -euo pipefail
# =============================================================================
# 配置区域
# =============================================================================
# 服务端口配置
BACKEND_PORT=8000
FRONTEND_PORT=3000
REDIS_PORT=6379
# 服务超时配置
BACKEND_STARTUP_TIMEOUT=60
FRONTEND_STARTUP_TIMEOUT=90
HEALTH_CHECK_TIMEOUT=10
# 日志配置
LOG_DIR="logs"
BACKEND_LOG="$LOG_DIR/backend.log"
FRONTEND_LOG="$LOG_DIR/frontend.log"
CELERY_LOG="$LOG_DIR/celery.log"
# PID文件
BACKEND_PID_FILE="backend.pid"
FRONTEND_PID_FILE="frontend.pid"
CELERY_PID_FILE="celery.pid"
# =============================================================================
# 颜色和样式定义
# =============================================================================
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
WHITE='\033[1;37m'
NC='\033[0m' # No Color
# 图标定义
ICON_SUCCESS="✅"
ICON_ERROR="❌"
ICON_WARNING="⚠️"
ICON_INFO="️"
ICON_ROCKET="🚀"
ICON_GEAR="⚙️"
ICON_DATABASE="🗄️"
ICON_WORKER="👷"
ICON_WEB="🌐"
ICON_HEALTH="💚"
# =============================================================================
# 工具函数
# =============================================================================
log_info() {
echo -e "${BLUE}${ICON_INFO} $1${NC}"
}
log_success() {
echo -e "${GREEN}${ICON_SUCCESS} $1${NC}"
}
log_warning() {
echo -e "${YELLOW}${ICON_WARNING} $1${NC}"
}
log_error() {
echo -e "${RED}${ICON_ERROR} $1${NC}"
}
log_header() {
echo -e "\n${PURPLE}${ICON_ROCKET} $1${NC}"
echo -e "${PURPLE}$(printf '=%.0s' {1..50})${NC}"
}
log_step() {
echo -e "\n${CYAN}${ICON_GEAR} $1${NC}"
}
# 检查命令是否存在
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# 检查端口是否被占用
port_in_use() {
lsof -i ":$1" >/dev/null 2>&1
}
# 等待服务启动
wait_for_service() {
local url="$1"
local timeout="$2"
local service_name="$3"
log_info "等待 $service_name 启动..."
for i in $(seq 1 "$timeout"); do
if curl -fsS "$url" >/dev/null 2>&1; then
log_success "$service_name 已启动"
return 0
fi
sleep 1
done
log_error "$service_name 启动超时"
return 1
}
# 检查进程是否运行
process_running() {
local pid_file="$1"
if [[ -f "$pid_file" ]]; then
local pid=$(cat "$pid_file")
if kill -0 "$pid" 2>/dev/null; then
return 0
else
rm -f "$pid_file"
fi
fi
return 1
}
# 停止进程
stop_process() {
local pid_file="$1"
local service_name="$2"
if [[ -f "$pid_file" ]]; then
local pid=$(cat "$pid_file")
if kill -0 "$pid" 2>/dev/null; then
log_info "停止 $service_name (PID: $pid)..."
kill "$pid" 2>/dev/null || true
sleep 2
if kill -0 "$pid" 2>/dev/null; then
log_warning "强制停止 $service_name..."
kill -9 "$pid" 2>/dev/null || true
fi
fi
rm -f "$pid_file"
fi
}
# =============================================================================
# 环境检查函数
# =============================================================================
check_environment() {
log_header "环境检查"
# 检查操作系统
if [[ "$OSTYPE" == "darwin"* ]]; then
log_success "检测到 macOS 系统"
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
log_success "检测到 Linux 系统"
else
log_warning "未识别的操作系统: $OSTYPE"
fi
# 检查必要的命令(在Docker环境中跳过node检查)
if [[ "${ENVIRONMENT:-}" != "production" ]] || [[ ! -f "/.dockerenv" ]]; then
local required_commands=("python3" "node" "npm" "redis-cli")
for cmd in "${required_commands[@]}"; do
if command_exists "$cmd"; then
log_success "$cmd 已安装"
else
log_error "$cmd 未安装,请先安装"
exit 1
fi
done
else
log_info "Docker环境检测到,跳过node环境检查"
local required_commands=("python3" "redis-cli")
for cmd in "${required_commands[@]}"; do
if command_exists "$cmd"; then
log_success "$cmd 已安装"
else
log_error "$cmd 未安装,请先安装"
exit 1
fi
done
fi
# 检查Python版本
local python_version=$(python3 --version 2>&1 | cut -d' ' -f2)
log_info "Python 版本: $python_version"
# 检查Node.js版本
local node_version=$(node --version)
log_info "Node.js 版本: $node_version"
# 检查虚拟环境
if [[ ! -d "venv" ]]; then
log_error "虚拟环境不存在,请先创建: python3 -m venv venv"
exit 1
fi
log_success "虚拟环境存在"
# 检查项目结构
local required_dirs=("backend" "frontend" "data")
for dir in "${required_dirs[@]}"; do
if [[ -d "$dir" ]]; then
log_success "目录 $dir 存在"
else
log_error "目录 $dir 不存在"
exit 1
fi
done
}
# =============================================================================
# 服务启动函数
# =============================================================================
start_redis() {
log_step "启动 Redis 服务"
if redis-cli ping >/dev/null 2>&1; then
log_success "Redis 服务已运行"
return 0
fi
log_info "启动 Redis 服务..."
if [[ "$OSTYPE" == "darwin"* ]]; then
if command_exists brew; then
brew services start redis
sleep 3
else
log_error "请手动启动 Redis 服务"
exit 1
fi
else
systemctl start redis-server 2>/dev/null || service redis-server start 2>/dev/null || {
log_error "无法启动 Redis 服务,请手动启动"
exit 1
}
fi
if redis-cli ping >/dev/null 2>&1; then
log_success "Redis 服务启动成功"
else
log_error "Redis 服务启动失败"
exit 1
fi
}
setup_environment() {
log_step "设置环境"
# 创建日志目录
mkdir -p "$LOG_DIR"
# 激活虚拟环境
log_info "激活虚拟环境..."
source venv/bin/activate
# 设置Python路径
: "${PYTHONPATH:=}"
export PYTHONPATH="${PWD}:${PYTHONPATH}"
log_info "设置 Python 路径: $PYTHONPATH"
# 加载环境变量
if [[ -f ".env" ]]; then
log_info "加载环境变量..."
set -a
source .env
set +a
log_success "环境变量加载成功"
else
log_warning ".env 文件不存在,使用默认配置"
# 创建默认环境变量文件
if [[ ! -f ".env" ]]; then
log_info "创建默认 .env 文件..."
cp env.example .env 2>/dev/null || {
cat > .env << EOF
# AutoClip 环境配置
DATABASE_URL=sqlite:///./data/autoclip.db
REDIS_URL=redis://localhost:6379/0
API_DASHSCOPE_API_KEY=
API_MODEL_NAME=qwen-plus
LOG_LEVEL=INFO
ENVIRONMENT=development
DEBUG=true
EOF
log_success "已创建默认 .env 文件"
}
fi
fi
# 检查Python依赖
log_info "检查 Python 依赖..."
if ! python -c "import fastapi, celery, sqlalchemy" 2>/dev/null; then
log_warning "缺少依赖,正在安装..."
pip install -r requirements.txt
fi
log_success "Python 依赖检查完成"
}
init_database() {
log_step "初始化数据库"
# 确保数据目录存在
mkdir -p data
# 初始化数据库
log_info "创建数据库表..."
if python -c "
import sys
sys.path.insert(0, '.')
from backend.core.database import engine, Base
from backend.models import project, task, clip, collection, bilibili
try:
Base.metadata.create_all(bind=engine)
print('数据库表创建成功')
except Exception as e:
print(f'数据库初始化失败: {e}')
sys.exit(1)
" 2>/dev/null; then
log_success "数据库初始化成功"
else
log_error "数据库初始化失败"
exit 1
fi
}
start_celery() {
log_step "启动 Celery Worker"
# 停止现有的Celery进程
pkill -f "celery.*worker" 2>/dev/null || true
sleep 2
# 清理可能存在的重复Whisper进程
log_info "检查并清理重复的Whisper进程..."
python scripts/monitor_whisper.py --kill-duplicates 2>/dev/null || true
log_info "启动 Celery Worker..."
nohup celery -A backend.core.celery_app worker \
--loglevel=info \
--concurrency=1 \
--prefetch-multiplier=1 \
-Q processing,upload,notification,maintenance \
--hostname=worker@%h \
> "$CELERY_LOG" 2>&1 &
local celery_pid=$!
echo "$celery_pid" > "$CELERY_PID_FILE"
# 等待Worker启动
sleep 5
if pgrep -f "celery.*worker" >/dev/null; then
log_success "Celery Worker 已启动 (PID: $celery_pid)"
else
log_error "Celery Worker 启动失败"
log_info "查看日志: tail -f $CELERY_LOG"
exit 1
fi
}
start_backend() {
log_step "启动后端 API 服务"
# 检查端口是否被占用
if port_in_use "$BACKEND_PORT"; then
log_warning "端口 $BACKEND_PORT 已被占用,尝试停止现有服务..."
stop_process "$BACKEND_PID_FILE" "后端服务"
fi
log_info "启动后端服务 (端口: $BACKEND_PORT)..."
nohup python -m uvicorn backend.main:app \
--host 0.0.0.0 \
--port "$BACKEND_PORT" \
--reload \
--reload-dir backend \
--reload-include '*.py' \
--reload-exclude 'data/*' \
--reload-exclude 'logs/*' \
--reload-exclude 'uploads/*' \
--reload-exclude '*.log' \
> "$BACKEND_LOG" 2>&1 &
local backend_pid=$!
echo "$backend_pid" > "$BACKEND_PID_FILE"
# 等待后端启动
if wait_for_service "http://localhost:$BACKEND_PORT/api/v1/health/" "$BACKEND_STARTUP_TIMEOUT" "后端服务"; then
log_success "后端服务已启动 (PID: $backend_pid)"
else
log_error "后端服务启动失败"
log_info "查看日志: tail -f $BACKEND_LOG"
exit 1
fi
}
start_frontend() {
log_step "启动前端服务"
# 检查端口是否被占用
if port_in_use "$FRONTEND_PORT"; then
log_warning "端口 $FRONTEND_PORT 已被占用,尝试停止现有服务..."
stop_process "$FRONTEND_PID_FILE" "前端服务"
fi
# 进入前端目录
cd frontend || {
log_error "无法进入前端目录"
exit 1
}
# 检查前端依赖
if [[ ! -d "node_modules" ]]; then
log_info "安装前端依赖..."
npm install
fi
log_info "启动前端服务 (端口: $FRONTEND_PORT)..."
nohup npm run dev -- --host 0.0.0.0 --port "$FRONTEND_PORT" \
> "../$FRONTEND_LOG" 2>&1 &
local frontend_pid=$!
echo "$frontend_pid" > "../$FRONTEND_PID_FILE"
# 返回项目根目录
cd ..
# 等待前端启动
if wait_for_service "http://localhost:$FRONTEND_PORT/" "$FRONTEND_STARTUP_TIMEOUT" "前端服务"; then
log_success "前端服务已启动 (PID: $frontend_pid)"
else
log_error "前端服务启动失败"
log_info "查看日志: tail -f $FRONTEND_LOG"
exit 1
fi
}
# =============================================================================
# 健康检查函数
# =============================================================================
health_check() {
log_header "系统健康检查"
local all_healthy=true
# 检查后端
log_info "检查后端服务..."
if curl -fsS "http://localhost:$BACKEND_PORT/api/v1/health/" >/dev/null 2>&1; then
log_success "后端服务健康"
else
log_error "后端服务不健康"
all_healthy=false
fi
# 检查前端
log_info "检查前端服务..."
if curl -fsS "http://localhost:$FRONTEND_PORT/" >/dev/null 2>&1; then
log_success "前端服务健康"
else
log_error "前端服务不健康"
all_healthy=false
fi
# 检查Redis
log_info "检查 Redis 服务..."
if redis-cli ping >/dev/null 2>&1; then
log_success "Redis 服务健康"
else
log_error "Redis 服务不健康"
all_healthy=false
fi
# 检查Celery Worker
log_info "检查 Celery Worker..."
if pgrep -f "celery.*worker" >/dev/null; then
log_success "Celery Worker 健康"
else
log_error "Celery Worker 不健康"
all_healthy=false
fi
if [[ "$all_healthy" == true ]]; then
log_success "所有服务健康检查通过"
return 0
else
log_error "部分服务健康检查失败"
return 1
fi
}
# =============================================================================
# 清理函数
# =============================================================================
cleanup() {
log_header "清理服务"
stop_process "$BACKEND_PID_FILE" "后端服务"
stop_process "$FRONTEND_PID_FILE" "前端服务"
stop_process "$CELERY_PID_FILE" "Celery Worker"
# 停止所有相关进程
pkill -f "celery.*worker" 2>/dev/null || true
pkill -f "uvicorn.*backend.main:app" 2>/dev/null || true
pkill -f "npm.*dev" 2>/dev/null || true
log_success "清理完成"
}
# =============================================================================
# 显示系统信息
# =============================================================================
show_system_info() {
log_header "系统启动完成"
echo -e "${WHITE}🎉 AutoClip 系统已成功启动!${NC}"
echo ""
echo -e "${CYAN}📊 服务状态:${NC}"
echo -e " ${ICON_WEB} 后端 API: http://localhost:$BACKEND_PORT"
echo -e " ${ICON_WEB} 前端界面: http://localhost:$FRONTEND_PORT"
echo -e " ${ICON_WEB} API 文档: http://localhost:$BACKEND_PORT/docs"
echo -e " ${ICON_HEALTH} 健康检查: http://localhost:$BACKEND_PORT/api/v1/health/"
echo ""
echo -e "${CYAN}📝 日志文件:${NC}"
echo -e " 后端日志: tail -f $BACKEND_LOG"
echo -e " 前端日志: tail -f $FRONTEND_LOG"
echo -e " Celery日志: tail -f $CELERY_LOG"
echo ""
echo -e "${CYAN}🛑 停止系统:${NC}"
echo -e " ./stop_autoclip.sh 或按 Ctrl+C"
echo ""
echo -e "${YELLOW}💡 使用说明:${NC}"
echo -e " 1. 访问 http://localhost:$FRONTEND_PORT 使用前端界面"
echo -e " 2. 上传视频文件或输入B站链接"
echo -e " 3. 系统将自动启动AI处理流水线"
echo -e " 4. 实时查看处理进度和结果"
echo ""
}
# =============================================================================
# 信号处理
# =============================================================================
trap cleanup EXIT INT TERM
# =============================================================================
# 主函数
# =============================================================================
main() {
log_header "AutoClip 系统启动器 v2.0"
# 环境检查
check_environment
# 启动服务
start_redis
setup_environment
init_database
start_celery
start_backend
start_frontend
# 健康检查
if health_check; then
show_system_info
# 保持脚本运行(不进行循环检查)
log_info "系统运行中... 按 Ctrl+C 停止"
log_info "如需检查系统状态,请运行: ./status_autoclip.sh"
while true; do
sleep 3600 # 每小时检查一次,减少频率
done
else
log_error "系统启动失败,请检查日志"
exit 1
fi
}
# 运行主函数
main "$@"