《新闻联播》每日抓取入库:全链路 + 可移植化 + 定时任务

从 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 项离线自检(配置/清理/事实守卫/解析/隧道)
This commit is contained in:
2026-09-25 11:17:46 +08:00
commit 60f8c263f2
35 changed files with 5770 additions and 0 deletions
+62
View File
@@ -0,0 +1,62 @@
"""敏感配置加载器(.env)
职责边界(2026-09-25 重构):
- 本文件只负责**加载**敏感配置:数据库口令、各供应商 API Key。
- 校验(缺哪些、密钥变量叫什么)在 `config.required_secrets()` / `config.check_secrets()`,
因为密钥变量名由 config.yml 的 `endpoints.*.api_key_env` 决定(换供应商时会变)。
- 非敏感配置(数据库地址、目录、模型名、**base url 接入点**、参数、清理开关)见 config.yml / config.py。
`.env` 查找顺序:XWLB_ENV_FILE → 项目目录内 .env → 上一级 / 上两级 .env(兼容旧 djapi 布局)。
已存在的进程环境变量优先,不会被 .env 覆盖。
"""
import logging
import os
from pathlib import Path
logger = logging.getLogger(__name__)
# 本文件所在目录即项目根(xwlb/)
BASE_DIR = Path(__file__).resolve().parent
def _candidate_env_files():
"""按优先级返回候选 .env 路径"""
override = os.getenv('XWLB_ENV_FILE')
if override:
yield Path(override).expanduser()
return
yield BASE_DIR / '.env'
# 兼容旧布局:xwlb/ 的上一级或上两级
yield BASE_DIR.parent / '.env'
yield BASE_DIR.parent.parent / '.env'
def _load_dotenv():
"""从候选路径中第一个存在的 .env 加载环境变量(不覆盖已有)"""
for dotenv_path in _candidate_env_files():
if not dotenv_path.is_file():
continue
count = 0
with open(dotenv_path, encoding='utf-8') as f:
for line in f:
line = line.strip()
if not line or line.startswith('#') or '=' not in line:
continue
key, _, value = line.partition('=')
key = key.strip()
value = value.strip().strip('"').strip("'")
if key and key not in os.environ:
os.environ[key] = value
count += 1
logger.info("已加载敏感配置 %s(注入 %d 个变量)", dotenv_path, count)
return dotenv_path
logger.debug("未找到 .env 文件,仅使用进程环境变量")
return None
LOADED_ENV_FILE = _load_dotenv()
def env_file() -> Path:
"""当前使用的 .env 路径(不存在时返回期望路径)"""
return LOADED_ENV_FILE or (BASE_DIR / '.env')