Files
xwlb/scripts/run_daily.sh
T
simon 60f8c263f2 《新闻联播》每日抓取入库:全链路 + 可移植化 + 定时任务
从 CCTV 主页抓取《新闻联播》,下载 → 转 MP3/WAV → 静音切分 → ASR 识别
→ LLM 校对 → 切分为单条新闻 → 入库 MySQL。

主要内容:
- 全链路:getVideo5 抓取下载、audioRead 转写、deepseek 校对与切分、newsProcess 入库
- 可移植化:配置分层,.env 只放密钥、config.yml 放模型/接入点/路由/参数
- 可换供应商:endpoints(kind/base_url/api_key_env/extra_body)+ routes 按环节选路
- 数据保真:数值事实守卫,校对改动数字/年份/届次则整片回退 ASR 原文;
  识别不完整不发布该日精编,避免半天内容被当成完整一天
- 定时任务:systemd 每天 21:00,失败 21:30 / 22:00 重试;
  只缺切分时只重跑切分(省掉全部 ASR),用 state/.asr_complete_* 标记判定阶段
- 隧道自愈:13306 不通时自动执行 autossh.sh(所有入口共用,systemd 托管时只等待)
- 中间产物每日清理;97 项离线自检(配置/清理/事实守卫/解析/隧道)
2026-09-25 11:17:46 +08:00

124 lines
4.6 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# 每日全链路任务(由 systemd 定时器调用,也可手动执行)
#
# scripts/run_daily.sh # 抓取"今天"
# scripts/run_daily.sh 20260924 # 指定日期
#
# 重试策略:首次失败后,在 21:30、22:00 各再执行一次(可用 XWLB_RETRY_SLOTS 覆盖)。
# · 若当天分片已入库、只缺精编(切分失败)→ 重试只重跑切分,省掉全部 ASR 调用
# · 若分片都没入库(下载/识别失败) → 重试重跑全链路
#
# 环境变量(一般不用改):
# XWLB_RETRY_SLOTS 重试时刻,默认 "21:30 22:00"
# XWLB_ATTEMPT_TIMEOUT 单次尝试上限秒数,默认 2400(40 分钟)
# XWLB_ENV_FILE / XWLB_CONFIG_FILE 透传给 python 的配置覆盖
# XWLB_FORCE=1 即使当天已入库也强制重跑
#
# 退出码:0=成功 1=重试用尽仍失败 2=已有实例在运行(跳过)
set -uo pipefail
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$DIR" || exit 1
PY="$DIR/.venv/bin/python"
LOG="$DIR/main.log"
RETRY_SLOTS="${XWLB_RETRY_SLOTS:-21:30 22:00}"
ATTEMPT_TIMEOUT="${XWLB_ATTEMPT_TIMEOUT:-2400}"
DB_PORT="${XWLB_DB_PORT:-13306}"
log() { printf '%s [run_daily] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*"; }
# ---- 防重入:定时器与手动执行、或两次重试叠在一起时,避免重复烧 ASR ----
exec 9>"$DIR/.run_daily.lock"
if ! flock -n 9; then
log "已有 run_daily 实例在运行,本次跳过"
exit 2
fi
# ---- 日期 ----
D8="${1:-$(date +%Y%m%d)}"
D10="${D8:0:4}-${D8:4:2}-${D8:6:2}"
# ---- 隧道自愈:数据库走 autossh 本地端口,隧道断了当天必失败 ----
# 先用 ss 快速判断(零成本);不通则交给 tunnel.py —— 那里是唯一的自愈实现,
# 所有 Python 入口(main/getVideo5/newsProcess/...)走的是同一套逻辑。
ensure_tunnel() {
ss -ltn 2>/dev/null | grep -q "127.0.0.1:${DB_PORT}" && return 0
log "数据库端口 ${DB_PORT} 不通,调用 tunnel.py 自愈(必要时执行 autossh.sh)"
if "$PY" "$DIR/tunnel.py" >>"$LOG" 2>&1; then
log "隧道已就绪"
return 0
fi
log "⚠️ 隧道仍不通(端口 ${DB_PORT}),本次很可能失败"
return 1
}
# 等到 HH:MM;若已过该时刻则立即返回(例如首次尝试本来就跑过了 21:30)
wait_until() {
local target="$1" now target_sec
now=$(date +%s)
target_sec=$(date -d "today ${target}" +%s 2>/dev/null) || { log "无法解析重试时刻 ${target},立即执行"; return 0; }
if [ "$target_sec" -gt "$now" ]; then
log "等待到 ${target} 再重试(约 $(( (target_sec - now + 59) / 60 )) 分钟)"
sleep $(( target_sec - now ))
else
log "已过 ${target},立即重试"
fi
}
# 失败重试时:只缺精编就走切分环节(快、便宜),否则重跑全链路
build_cmd() {
local stage="$1"
if [ "$stage" = "retry" ]; then
"$PY" "$DIR/scripts/day_status.py" "$D10" >/dev/null 2>&1
case $? in
1) log "当天分片已入库、只缺精编 → 重试仅重跑切分环节"
CMD=("$PY" "$DIR/newsProcess.py" "$D10" --force); return 0 ;;
0) log "当天数据已完整(可能上次重试已完成),无需再跑"
CMD=(); return 0 ;;
*) log "当天分片未入库 → 重试重跑全链路" ;;
esac
fi
CMD=("$PY" "$DIR/getVideo5.py" "$D8" "$D8")
}
run_attempt() {
local label="$1" stage="$2" rc
build_cmd "$stage"
if [ "${#CMD[@]}" -eq 0 ]; then
return 0
fi
log "===== ${label} 开始(${CMD[*]},上限 ${ATTEMPT_TIMEOUT}s)====="
timeout "$ATTEMPT_TIMEOUT" "${CMD[@]}" 2>&1 | tee -a "$LOG"
rc=${PIPESTATUS[0]}
if [ "$rc" -eq 0 ]; then
log "===== ${label} 成功 ====="
return 0
fi
log "===== ${label} 失败(退出码 ${rc})====="
return 1
}
ensure_tunnel || true
# ---- 当天已完成则跳过:ext 有数据即视为完成(识别不完整的日子不会写出 ext)----
# 手动强制重跑:XWLB_FORCE=1 bash scripts/run_daily.sh <日期>
if [ "${XWLB_FORCE:-0}" != "1" ]; then
if "$PY" "$DIR/scripts/day_status.py" "$D10" >/dev/null 2>&1; then
log "$D10 的数据已完整入库,跳过本次运行(强制重跑用 XWLB_FORCE=1 或 newsRedo.py)"
exit 0
fi
fi
if run_attempt "首次尝试($(date +%H:%M))" full; then
exit 0
fi
for slot in $RETRY_SLOTS; do
wait_until "$slot"
if run_attempt "重试(${slot})" retry; then
exit 0
fi
done
log "❌ 全部重试失败,${D8} 数据未完成"
exit 1