Files
2026-03-21_why-manifest/monitor.py

127 lines
4.7 KiB
Python

import os
import shutil
import subprocess
import time
import sys
from pathlib import Path
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
from logger import get_system_logger, log_exception
# ==========================================
# 接口配置
# ==========================================
STAGE_DIR = r'./stage'
BACKUP_DIR = r'./backup'
SESSION_DIR = r'./session'
MIN_DURATION_SECONDS = 15 * 60
VIDEO_EXTS = {'.mp4', '.mkv', '.avi', '.mov', '.flv', '.wmv'}
# 初始化日志
logger = get_system_logger('monitor')
# ==========================================
def get_video_duration(file_path):
try:
cmd = ['ffprobe', '-v', 'error', '-show_entries', 'format=duration', '-of', 'default=noprint_wrappers=1:nokey=1', str(file_path)]
result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
duration = float(result.stdout)
logger.debug(f"获取视频时长: {file_path.name} = {duration}")
return duration
except Exception as e:
log_exception(logger, e, f"获取视频时长失败: {file_path}")
return 0
class VideoHandler(FileSystemEventHandler):
def on_created(self, event):
if not event.is_directory:
# 兼容处理 watchdog 路径编码问题
src_path = event.src_path
if isinstance(src_path, bytes):
src_path = src_path.decode('utf-8')
logger.debug(f"检测到文件创建事件: {src_path}")
self.handle_file(Path(src_path))
def handle_file(self, file_path):
if file_path.suffix.lower() not in VIDEO_EXTS:
logger.debug(f"跳过非视频文件: {file_path.name}")
return
logger.info(f"发现新视频文件: {file_path.name},正在检查写入状态...")
# 改进:通过检查文件大小变化来判断是否写入完成
last_size = -1
while True:
try:
if not file_path.exists():
logger.warning(f"文件在检查期间消失: {file_path}")
return
current_size = file_path.stat().st_size
if current_size == last_size and current_size > 0:
break
last_size = current_size
time.sleep(5) # 每5秒检查一次大小
except Exception as e:
logger.error(f"检查文件状态异常: {e}")
break
try:
duration = get_video_duration(file_path)
logger.info(f"视频时长: {file_path.name} = {duration/60:.1f} 分钟")
if duration < MIN_DURATION_SECONDS:
logger.info(f"时长不足 {MIN_DURATION_SECONDS/60:.0f} 分钟,移动到备份区")
dst = Path(BACKUP_DIR) / file_path.name
shutil.move(str(file_path), str(dst))
logger.info(f"已移动至备份: {dst}")
else:
# 核心联动:创建专属工作区
session_folder = Path(SESSION_DIR) / file_path.stem
session_folder.mkdir(parents=True, exist_ok=True)
logger.info(f"创建工作区: {session_folder}")
logger.info(f"派发转录任务: {file_path.name}")
# 改进:使用 sys.executable 保证环境一致性
process = subprocess.Popen([
sys.executable, 'video2srt.py',
str(file_path),
str(session_folder)
])
logger.info(f"转录进程已启动 (PID: {process.pid})")
except Exception as e:
log_exception(logger, e, "监控处理异常")
if __name__ == "__main__":
logger.info("="*50)
logger.info("视频监控模块启动")
logger.info("="*50)
for d in [STAGE_DIR, BACKUP_DIR, SESSION_DIR]:
Path(d).mkdir(parents=True, exist_ok=True)
logger.info(f"监控目录: {STAGE_DIR}")
logger.info(f"备份目录: {BACKUP_DIR}")
logger.info(f"工作目录: {SESSION_DIR}")
handler = VideoHandler()
# 启动时扫描已有文件
logger.info("正在扫描 stage 目录下的存量视频...")
for f in Path(STAGE_DIR).iterdir():
if f.is_file():
handler.handle_file(f)
observer = Observer()
observer.schedule(handler, STAGE_DIR, recursive=False)
observer.start()
logger.info("文件监控已启动")
try:
while True: time.sleep(1)
except KeyboardInterrupt:
logger.info("接收到停止信号,正在关闭...")
observer.stop()
observer.join()
logger.info("视频监控模块已停止")