mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-08-11 00:25:36 +08:00
135 lines
4.7 KiB
Python
135 lines
4.7 KiB
Python
import os
|
||
import platform
|
||
from pathlib import Path
|
||
from typing import Any, Dict, List, Optional, Tuple
|
||
|
||
from app.log import logger
|
||
from app.utils.system import SystemUtils
|
||
|
||
|
||
def count_directory_entries(directory: Path, max_check: int = 10000) -> Tuple[int, int]:
|
||
"""
|
||
统计目录下的文件与子目录数量(用于检测是否超过系统限制)。
|
||
:param directory: 目录路径
|
||
:param max_check: 最大检查文件数量,避免长时间阻塞
|
||
:return: (文件数量, 目录数量)
|
||
"""
|
||
file_count = 0
|
||
dir_count = 0
|
||
try:
|
||
for _, dirs, files in os.walk(str(directory)):
|
||
file_count += len(files)
|
||
dir_count += len(dirs)
|
||
if file_count > max_check:
|
||
break
|
||
except Exception as err:
|
||
logger.debug(f"统计目录规模失败: {err}")
|
||
return file_count, dir_count
|
||
|
||
|
||
def count_directory_files(directory: Path, max_check: int = 10000) -> int:
|
||
"""
|
||
统计目录下的文件数量。
|
||
:param directory: 目录路径
|
||
:param max_check: 最大检查数量,避免长时间阻塞
|
||
:return: 文件数量
|
||
"""
|
||
file_count, _ = count_directory_entries(directory, max_check=max_check)
|
||
return file_count
|
||
|
||
|
||
def check_system_limits() -> Dict[str, Any]:
|
||
"""
|
||
检查系统监控相关限制。
|
||
:return: 系统限制信息
|
||
"""
|
||
limits = {
|
||
'max_user_watches': 0,
|
||
'max_user_instances': 0,
|
||
'warnings': []
|
||
}
|
||
|
||
try:
|
||
if platform.system() == 'Linux':
|
||
# 检查 inotify 限制
|
||
try:
|
||
with open('/proc/sys/fs/inotify/max_user_watches', 'r', encoding='utf-8', errors='replace') as f:
|
||
limits['max_user_watches'] = int(f.read().strip())
|
||
except Exception as e:
|
||
logger.debug(f"读取 inotify 限制失败: {e}")
|
||
limits['max_user_watches'] = 8192 # 默认值
|
||
|
||
try:
|
||
with open('/proc/sys/fs/inotify/max_user_instances', 'r', encoding='utf-8', errors='replace') as f:
|
||
limits['max_user_instances'] = int(f.read().strip())
|
||
except Exception as e:
|
||
logger.debug(f"读取 inotify 实例限制失败: {e}")
|
||
except Exception as e:
|
||
limits['warnings'].append(f"检查系统限制时出错: {e}")
|
||
|
||
return limits
|
||
|
||
|
||
def get_system_optimization_tips() -> List[str]:
|
||
"""
|
||
获取系统优化建议。
|
||
:return: 优化建议列表
|
||
"""
|
||
tips = []
|
||
system = platform.system()
|
||
|
||
if system == 'Linux':
|
||
tips.extend([
|
||
"增加 inotify 监控数量限制:",
|
||
"echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf",
|
||
"echo fs.inotify.max_user_instances=524288 | sudo tee -a /etc/sysctl.conf",
|
||
"sudo sysctl -p",
|
||
"",
|
||
"如果在Docker中运行,请在宿主机上执行以上命令"
|
||
])
|
||
elif system == 'Darwin':
|
||
tips.extend([
|
||
"macOS 系统优化建议:",
|
||
"sudo sysctl kern.maxfiles=65536",
|
||
"sudo sysctl kern.maxfilesperproc=32768",
|
||
"ulimit -n 32768"
|
||
])
|
||
elif system == 'Windows':
|
||
tips.extend([
|
||
"Windows 系统优化建议:",
|
||
"1. 关闭不必要的实时保护软件对监控目录的扫描",
|
||
"2. 将监控目录添加到Windows Defender排除列表",
|
||
"3. 确保有足够的可用内存"
|
||
])
|
||
|
||
return tips
|
||
|
||
|
||
def decide_monitor_mode(directory: Path,
|
||
monitor_mode: str) -> Tuple[bool, str, Optional[Dict[str, Any]], Optional[int]]:
|
||
"""
|
||
决策监控模式。兼容模式与网络文件系统直接短路,只有快速模式候选才统计
|
||
目录规模与系统限制,避免启动期对网络挂载做无谓的全量遍历。
|
||
|
||
inotify 的 max_user_watches 按监视点(目录)计数,因此用目录数量而不是
|
||
文件数量与上限比较。
|
||
|
||
:param directory: 监控目录
|
||
:param monitor_mode: 配置的监控模式
|
||
:return: (是否使用轮询, 原因, 系统限制信息或None, 文件数量或None)
|
||
"""
|
||
if monitor_mode == "compatibility":
|
||
return True, "用户配置为兼容模式", None, None
|
||
|
||
# 检查网络文件系统
|
||
if SystemUtils.is_network_filesystem(directory):
|
||
return True, "检测到网络文件系统,建议使用兼容模式", None, None
|
||
|
||
limits = check_system_limits()
|
||
file_count, dir_count = count_directory_entries(directory)
|
||
max_watches = limits.get('max_user_watches')
|
||
if max_watches and dir_count > max_watches * 0.8:
|
||
return (True, f"目录数量({dir_count})接近 inotify 监控上限({max_watches})",
|
||
limits, file_count)
|
||
return False, "使用快速模式", limits, file_count
|