Files
2026-01-14 00:54:34 +03:00

146 lines
5.5 KiB
Python

import os
import server
from aiohttp import web
import folder_paths
from pathlib import Path
# --- ИМПОРТ КЛАССОВ НОД ---
from .video_crossfade import VideoBatchCrossfade
from .ultimate_memory_cleaner import _UltimateMemoryCleaner
from .enhanced_video_preview import EnhancedVideoPreview
from .save_images_preview import SaveImagesPreviewPassthrough
from .video_concat import VideoConcatFFmpeg
from .image_size_control import GetImageSizeWithPreview
# Константы безопасности
MAX_PATH_LENGTH = 1024 # Разумное ограничение
# --- БЕЗОПАСНАЯ ПРОВЕРКА ПУТЕЙ ---
def is_path_allowed(path_str: str) -> bool:
"""Проверяет, что путь находится внутри разрешенных директорий ComfyUI"""
try:
if not path_str: return False
if len(path_str) > MAX_PATH_LENGTH: return False
path = Path(path_str)
resolved = path.resolve()
# Разрешенные корневые директории
allowed_roots = [
Path(folder_paths.get_output_directory()).resolve(),
Path(folder_paths.get_temp_directory()).resolve()
]
for allowed in allowed_roots:
try:
# Для Python 3.9+
if hasattr(resolved, "is_relative_to"):
if resolved.is_relative_to(allowed):
return True
else:
# Для Python 3.8
if str(resolved).startswith(str(allowed)):
return True
except: continue
return False
except Exception:
return False
# --- ОБЩАЯ ЛОГИКА API ДЛЯ БРАУЗЕРА ПАПОК ---
async def handle_list_dirs(request):
try:
data = await request.json()
current_path = data.get("path", "")
# Проверка длины пути
if len(current_path) > MAX_PATH_LENGTH:
return web.json_response({"error": "Path too long"}, status=400)
# Если путь не задан, используем output directory
if not current_path or current_path.strip() == "":
current_path = folder_paths.get_output_directory()
# Нормализация
try:
abs_current_path = str(Path(current_path).resolve())
except:
return web.json_response({"error": "Invalid path syntax"}, status=400)
# КРИТИЧЕСКАЯ ПРОВЕРКА БЕЗОПАСНОСТИ
if not is_path_allowed(abs_current_path):
return web.json_response(
{"error": "Access denied: Path is outside allowed directories", "path": current_path},
status=403
)
if not os.path.exists(abs_current_path) or not os.path.isdir(abs_current_path):
return web.json_response(
{"error": "Path not found", "path": current_path},
status=404
)
# Проверяем родителя
parent_path = os.path.dirname(abs_current_path)
if not is_path_allowed(parent_path):
parent_path = None
dirs = []
try:
with os.scandir(abs_current_path) as it:
for entry in it:
if entry.is_dir() and not entry.name.startswith('.'):
dirs.append(entry.name)
except PermissionError:
return web.json_response({"error": "Permission denied"}, status=403)
except Exception as e:
return web.json_response({"error": str(e)}, status=500)
dirs.sort()
return web.json_response({
"current_path": abs_current_path,
"parent_path": parent_path,
"dirs": dirs
})
except Exception as e:
return web.json_response({"error": f"Unexpected error: {str(e)}"}, status=500)
# --- РЕГИСТРАЦИЯ МАРШРУТОВ API ---
@server.PromptServer.instance.routes.post("/enhanced_preview/list_dirs")
async def route_enhanced_list_dirs(request):
return await handle_list_dirs(request)
@server.PromptServer.instance.routes.post("/save_preview/list_dirs")
async def route_save_list_dirs(request):
return await handle_list_dirs(request)
@server.PromptServer.instance.routes.post("/api/save_preview/list_dirs")
async def route_api_save_list_dirs(request):
return await handle_list_dirs(request)
# --- MAPPINGS ---
NODE_CLASS_MAPPINGS = {
"VideoBatchCrossfade": VideoBatchCrossfade,
"UltimateMemoryCleaner": _UltimateMemoryCleaner,
"EnhancedVideoPreview": EnhancedVideoPreview,
"Save Images & Preview": SaveImagesPreviewPassthrough,
"Video Concat (FFmpeg)": VideoConcatFFmpeg,
"GetImageSizeWithPreview": GetImageSizeWithPreview
}
NODE_DISPLAY_NAME_MAPPINGS = {
"VideoBatchCrossfade": "📹 Video Batch Crossfade",
"UltimateMemoryCleaner": "🧹 Ultimate Memory Cleaner",
"EnhancedVideoPreview": "🎬 Enhanced Video Save'n'Preview",
"Save Images & Preview": "💾 Save Images & Preview (Passthrough)",
"Video Concat (FFmpeg)": "🎥 Video Concat (FFmpeg)",
"GetImageSizeWithPreview": "📏 Image Size Info & Edit"
}
WEB_DIRECTORY = "./web/js"
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS", "WEB_DIRECTORY"]