Files
xwlb/cleanup.py
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

119 lines
4.4 KiB
Python
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.
"""中间产物清理
用法:
python cleanup.py # 按 config.yml 的 cleanup 配置清理
python cleanup.py --dry-run # 只列出将删除的文件,不真删
清理范围(由 config.yml 的 cleanup 段控制):
paths.video_dir 下所有 *.mp3 / *.mp4
paths.audio_dir 下所有 *.wav
当日流程(getVideo5.process_videos)在一天的任务成功结束后会自动调用本模块;
失败时默认保留文件以便重跑(cleanup.only_on_success: 1)。
"""
import argparse
import logging
import config
logger = logging.getLogger(__name__)
def _list_files(directory, patterns):
if not directory.is_dir():
return []
found = []
for pattern in patterns:
found.extend(sorted(p for p in directory.glob(pattern) if p.is_file()))
return found
def collect_targets():
"""返回 [(路径, 目录角色)],受 cleanup.remove_video / remove_audio 控制"""
targets = []
if config.get_bool('cleanup.remove_video', True):
targets += [(p, 'video') for p in _list_files(config.video_dir(), ('*.mp3', '*.mp4'))]
if config.get_bool('cleanup.remove_audio', True):
# 只删 wav。识别完成标记在 state/ 下,是长期凭证,**不能删**
# (删了就分不清"已完成"与"识别只跑了一半",见 scripts/day_status.py)
targets += [(p, 'audio') for p in _list_files(config.audio_dir(), ('*.wav',))]
return targets
def cleanup_intermediates(dry_run=False, force=False, reason=''):
"""
删除中间产物;返回 {'removed': n, 'bytes': n, 'skipped': bool}
参数:
dry_run: 只统计不删除
force: 忽略 cleanup.after_daily_run 开关(供手动调用)
reason: 日志说明(一般是日期)
"""
if not force and not config.get_bool('cleanup.after_daily_run', True):
logger.info("cleanup.after_daily_run 已关闭,跳过清理")
return {'removed': 0, 'bytes': 0, 'skipped': True}
targets = collect_targets()
if not targets:
logger.info("没有需要清理的中间产物(%s)", reason or '手动清理')
return {'removed': 0, 'bytes': 0, 'skipped': False}
removed = 0
freed = 0
for path, role in targets:
try:
size = path.stat().st_size
except OSError:
size = 0
if dry_run:
logger.info("[dry-run] 将删除 %s(%.1f MB)", path, size / 1024 / 1024)
removed += 1
freed += size
continue
try:
path.unlink()
removed += 1
freed += size
logger.debug("已删除 %s", path)
except OSError as e:
logger.warning("删除失败 %s: %s", path, e)
action = '将清理' if dry_run else '已清理'
logger.info("%s中间产物 %d 个文件(释放 %.1f MB)%s",
action, removed, freed / 1024 / 1024, f" | {reason}" if reason else '')
return {'removed': removed, 'bytes': freed, 'skipped': False}
def maybe_cleanup_after_run(date_str, day_ok):
"""
当天流程结束后按 config.yml 的 cleanup 段决定是否清理
参数:
date_str: 日期(仅用于日志)
day_ok: 当天是否全部成功(识别分片全成功 + 切分已就绪)
"""
if not config.get_bool('cleanup.after_daily_run', True):
logger.info("cleanup.after_daily_run 已关闭,跳过清理")
return {'removed': 0, 'bytes': 0, 'skipped': True}
if not day_ok and config.get_bool('cleanup.only_on_success', True):
logger.warning("⚠️ %s 未全部成功,保留中间产物以便重跑"
"(cleanup.only_on_success=1;改为 0 则无论成败都清理)", date_str)
return {'removed': 0, 'bytes': 0, 'skipped': True}
reason = f'{date_str} 任务完成' if day_ok else f'{date_str} 任务未全部成功(only_on_success=0)'
return cleanup_intermediates(reason=reason)
def main():
parser = argparse.ArgumentParser(description='清理 xwlb 中间产物(mp3/mp4/wav)')
parser.add_argument('--dry-run', action='store_true', help='只列出将删除的文件')
args = parser.parse_args()
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
config.log_summary()
result = cleanup_intermediates(dry_run=args.dry_run, force=True, reason='手动执行')
return 0 if result['removed'] >= 0 else 1
if __name__ == '__main__':
raise SystemExit(main())