140 lines
4.5 KiB
Python
140 lines
4.5 KiB
Python
# hls_cleanup.py
|
||
# HLS文件夹清理进程
|
||
# 功能:定时清理过期的HLS会话文件夹,支持浮点数天数,不依赖摄像头配置
|
||
|
||
import os
|
||
import sys
|
||
import argparse
|
||
import signal
|
||
import shutil
|
||
import time
|
||
from datetime import datetime, timedelta
|
||
from typing import List
|
||
|
||
from utils.logger import get_logger
|
||
logger = get_logger(__name__)
|
||
|
||
running = True
|
||
|
||
|
||
def parse_args():
|
||
"""解析命令行参数"""
|
||
parser = argparse.ArgumentParser(description="HLS Folder Cleanup Service")
|
||
parser.add_argument("--hls-root-path", required=True, help="HLS root path")
|
||
parser.add_argument("--retention-days", required=True, type=float,
|
||
help="Retention days (supports float, e.g. 0.5 for 12 hours)")
|
||
parser.add_argument("--interval", required=True, type=int,
|
||
help="Cleanup interval in hours")
|
||
return parser.parse_args()
|
||
|
||
|
||
def parse_folder_time(folder_name: str) -> datetime:
|
||
"""
|
||
从文件夹名称解析时间
|
||
格式: yyyyMMdd_HHmmss
|
||
"""
|
||
try:
|
||
date_str, time_str = folder_name.split("_")
|
||
return datetime.strptime(f"{date_str}{time_str}", "%Y%m%d%H%M%S")
|
||
except (ValueError, IndexError) as e:
|
||
raise ValueError(f"Invalid folder name format: {folder_name}") from e
|
||
|
||
|
||
def cleanup_hls_folders(hls_root_path: str, retention_days: float) -> tuple:
|
||
"""
|
||
清理过期的HLS会话文件夹
|
||
|
||
Args:
|
||
hls_root_path: HLS根目录
|
||
retention_days: 保留天数(支持浮点数)
|
||
|
||
Returns:
|
||
(deleted_count, error_count) 删除数量和错误数量
|
||
"""
|
||
if not os.path.exists(hls_root_path):
|
||
logger.info(f"HLS root path does not exist: {hls_root_path}")
|
||
return 0, 0
|
||
|
||
cutoff_time = datetime.now() - timedelta(days=retention_days)
|
||
deleted_count = 0
|
||
error_count = 0
|
||
|
||
# 遍历所有摄像头目录
|
||
for camera_folder in os.listdir(hls_root_path):
|
||
camera_path = os.path.join(hls_root_path, camera_folder)
|
||
|
||
if not os.path.isdir(camera_path):
|
||
continue
|
||
|
||
# 遍历该摄像头下的所有会话文件夹
|
||
for session_folder in os.listdir(camera_path):
|
||
session_path = os.path.join(camera_path, session_folder)
|
||
|
||
if not os.path.isdir(session_path):
|
||
continue
|
||
|
||
try:
|
||
# 解析文件夹名称获取时间
|
||
folder_time = parse_folder_time(session_folder)
|
||
|
||
if folder_time < cutoff_time:
|
||
shutil.rmtree(session_path)
|
||
logger.info(f"Deleted: {session_path} (created: {folder_time})")
|
||
deleted_count += 1
|
||
except ValueError as e:
|
||
# 文件夹名称格式不匹配,跳过
|
||
logger.debug(f"Skipped: {session_path} ({e})")
|
||
except Exception as e:
|
||
logger.warning(f"Failed to delete {session_path}: {e}")
|
||
error_count += 1
|
||
|
||
return deleted_count, error_count
|
||
|
||
|
||
def signal_handler(signum, frame):
|
||
"""信号处理器"""
|
||
global running
|
||
logger.info(f"Received signal {signum}, shutting down...")
|
||
running = False
|
||
|
||
|
||
def main():
|
||
global running
|
||
|
||
args = parse_args()
|
||
|
||
# 注册信号处理器
|
||
signal.signal(signal.SIGTERM, signal_handler)
|
||
signal.signal(signal.SIGINT, signal_handler)
|
||
|
||
logger.info(f"HLS Cleanup Service starting")
|
||
logger.info(f"Config: hls_root={args.hls_root_path}, "
|
||
f"retention_days={args.retention_days}, interval={args.interval}h")
|
||
|
||
# 主循环
|
||
while running:
|
||
try:
|
||
logger.info(f"Starting cleanup scan...")
|
||
deleted, errors = cleanup_hls_folders(args.hls_root_path, args.retention_days)
|
||
|
||
if deleted > 0 or errors > 0:
|
||
logger.info(f"Cleanup completed: {deleted} deleted, {errors} errors "
|
||
f"(retention: {args.retention_days} days)")
|
||
else:
|
||
logger.info(f"Cleanup scan completed, no folders to delete "
|
||
f"(retention: {args.retention_days} days)")
|
||
except Exception as e:
|
||
logger.error(f"Error during cleanup: {e}")
|
||
|
||
# 等待下一次轮询(每小时检查一次)
|
||
for _ in range(args.interval * 3600):
|
||
if not running:
|
||
break
|
||
time.sleep(1)
|
||
|
||
logger.info("HLS Cleanup Service stopped")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|