# -*- coding: utf-8 -*-
"""
CCA 部署脚本 — 通过 paramiko 上传文件到腾讯云服务器
"""
import sys
sys.stdout.reconfigure(encoding='utf-8')
import os
import paramiko
from pathlib import Path
HOST = '101.42.29.217'
PORT = 22
USER = 'root'
PASS = 'liutong65'
CCA_ROOT = Path(__file__).resolve().parent.parent
SRC_DIR = CCA_ROOT / 'src'
DEPLOY_DIR = CCA_ROOT / 'deploy'
# 服务器目标路径
SERVER_CCA_SRC = '/workspace/military_tech_voice/backend/cca_src'
SERVER_FRONTEND = '/workspace/military_tech_voice/frontend'
SERVER_WWW = '/var/www/voice'
SERVER_BACKEND = '/workspace/military_tech_voice/backend'
SERVER_ROUTES = f'{SERVER_BACKEND}/app/routes'
# 需要上传的 src 模块
SRC_MODULES = [
'asr_client.py',
'line_breaker.py',
'ai_line_breaker.py',
'ai_proofreader.py',
'term_normalizer.py',
'hotword_extractor.py',
'srt_writer.py',
'segment_splitter.py',
]
def connect():
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(HOST, PORT, USER, PASS)
sftp = ssh.open_sftp()
return ssh, sftp
def run(ssh, cmd):
print(f' $ {cmd}')
_, stdout, stderr = ssh.exec_command(cmd)
out = stdout.read().decode('utf-8', errors='replace').strip()
err = stderr.read().decode('utf-8', errors='replace').strip()
if out:
print(f' {out[:500]}')
if err:
print(f' [stderr] {err[:500]}')
return out
def upload(sftp, local_path, remote_path):
print(f' ↑ {Path(local_path).name} → {remote_path}')
sftp.put(str(local_path), remote_path)
def main():
print('=== CCA 部署开始 ===\n')
ssh, sftp = connect()
print('[1/7] 连接成功\n')
# Step 2: 创建 cca_src 目录 + 上传源码
print('[2/7] 上传 CCA 源码模块...')
run(ssh, f'mkdir -p {SERVER_CCA_SRC}')
for module in SRC_MODULES:
local = SRC_DIR / module
if local.exists():
upload(sftp, local, f'{SERVER_CCA_SRC}/{module}')
else:
print(f' ⚠ 跳过(不存在): {module}')
print()
# Step 3: 上传 cca_route.py 到 app/routes/
print('[3/7] 上传 cca_route.py...')
run(ssh, f'mkdir -p {SERVER_ROUTES}')
upload(sftp, DEPLOY_DIR / 'cca_route.py', f'{SERVER_ROUTES}/cca.py')
# 确保 __init__.py 存在
run(ssh, f'touch {SERVER_ROUTES}/__init__.py')
print()
# Step 4: 上传 cca.html 到前端目录
print('[4/7] 上传 cca.html...')
upload(sftp, DEPLOY_DIR / 'cca.html', f'{SERVER_FRONTEND}/cca.html')
upload(sftp, DEPLOY_DIR / 'cca.html', f'{SERVER_WWW}/cca.html')
print()
# Step 5: 配置 .env(追加 CCA 相关变量)
print('[5/7] 配置 .env...')
env_path = f'{SERVER_BACKEND}/.env'
existing_env = ''
try:
with sftp.open(env_path, 'r') as f:
existing_env = f.read().decode('utf-8', errors='replace')
except FileNotFoundError:
pass
env_additions = []
if 'XFYUN_APP_ID' not in existing_env:
env_additions.append('# === CCA 唱词助手凭证 ===')
env_additions.append('# 讯飞录音文件转写(账号1-默认)')
env_additions.append('XFYUN_APP_ID=4c423e35')
env_additions.append('XFYUN_SECRET_KEY=b9e0b97d5dda072c9b4b8fb59e7e3d22')
env_additions.append('# 讯飞备用账号2')
env_additions.append('# XFYUN_APP_ID=52ae3024')
env_additions.append('# XFYUN_SECRET_KEY=d65de0eb282a4339e2b1e14fd119e42e')
if 'DEEPSEEK_API_KEY' not in existing_env:
env_additions.append('# DeepSeek(校对+折行+热词)')
env_additions.append('DEEPSEEK_API_KEY=sk-01a7868a88a04ab494e4f05c1f3f06e2')
if env_additions:
with sftp.open(env_path, 'a') as f:
f.write('\n' + '\n'.join(env_additions) + '\n')
print(' .env 已追加 CCA 凭证')
else:
print(' .env 已有 CCA 凭证,跳过')
print()
# Step 6: 注册 CCA 蓝图到 Flask main.py
print('[6/7] 注册 CCA 蓝图...')
main_py_path = f'{SERVER_BACKEND}/app/main.py'
with sftp.open(main_py_path, 'r') as f:
main_content = f.read().decode('utf-8', errors='replace')
if 'cca' not in main_content.lower():
# 找到最后一个 register_blueprint 的位置,在其后追加
lines = main_content.split('\n')
insert_idx = -1
for i, line in enumerate(lines):
if 'register_blueprint' in line:
insert_idx = i
if insert_idx >= 0:
indent = ' ' # 匹配现有缩进
# 检查现有蓝图注册的缩进
existing_line = lines[insert_idx]
indent = existing_line[:len(existing_line) - len(existing_line.lstrip())]
cca_lines = [
'',
f'{indent}# CCA 唱词助手',
f'{indent}from app.routes.cca import bp as cca_bp',
f'{indent}app.register_blueprint(cca_bp)',
]
for j, cca_line in enumerate(cca_lines):
lines.insert(insert_idx + 1 + j, cca_line)
new_content = '\n'.join(lines)
with sftp.open(main_py_path, 'w') as f:
f.write(new_content)
print(' 已注册 CCA 蓝图')
else:
print(' ⚠ 未找到 register_blueprint,请手动注册')
else:
print(' CCA 蓝图已注册,跳过')
print()
# Step 7: 安装依赖 + 修改 index.html + 重启
print('[7/7] 安装依赖、修改首页、重启服务...')
# 安装 Python 依赖
run(ssh, f'{SERVER_BACKEND}/venv/bin/pip install openai python-docx 2>&1 | tail -5')
# 修改 index.html 添加 CCA 入口按钮
index_path = f'{SERVER_FRONTEND}/index.html'
with sftp.open(index_path, 'r') as f:
index_content = f.read().decode('utf-8', errors='replace')
if '唱词助手' not in index_content and 'cca.html' not in index_content:
# 在导航栏中找到合适位置插入按钮
# 典型位置:header 或 nav 区域的最后一个链接之后
if '