import argparse import glob import json import shutil import subprocess import logging from logging.handlers import RotatingFileHandler import os import stat import threading import time import cv2 from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor, as_completed from datetime import datetime from config.config_utils import config from tool.process import process_single_video, derive_video_name from tool.log.log_manager import logger as steplog from tool import job_cos logging.basicConfig( level=logging.INFO, format='%(asctime)s [%(levelname)s] %(message)s', handlers=[ # 常驻worker一跑就是数天/数周, 普通FileHandler会无限增长; 100M/份, 最多留5份(共500M封顶) RotatingFileHandler('batch_processor.log', maxBytes=100 * 1024 * 1024, backupCount=5, encoding='utf-8'), logging.StreamHandler() ] ) JSONL_PATH = "数据.jsonl" # ==================== 工具函数 ==================== def parse_cos_path(cos_path: str): """cos://bucket/path/video.mp4 → ('cos://bucket/path/', 'video.mp4')""" cos_path = cos_path.strip() idx = cos_path.rfind('/') return cos_path[:idx + 1], cos_path[idx + 1:] def get_video_duration(local_path: str) -> float: if not os.path.exists(local_path): return 0.0 cap = cv2.VideoCapture(local_path) if not cap.isOpened(): return 0.0 frames = cap.get(cv2.CAP_PROP_FRAME_COUNT) fps = cap.get(cv2.CAP_PROP_FPS) cap.release() return round(frames / fps, 2) if fps > 0 else 0.0 def _read_log_stats(video_name: str): """从 log/