139 lines
5.0 KiB
Python
139 lines
5.0 KiB
Python
import os
|
|
import subprocess
|
|
import platform
|
|
import folder_paths
|
|
from server import PromptServer
|
|
from aiohttp import web
|
|
import json
|
|
|
|
|
|
class OpenPathNode:
|
|
"""
|
|
A ComfyUI node that takes a path string as input and provides a button to open it in the default file explorer.
|
|
"""
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"path": ("STRING", {
|
|
"default": "",
|
|
"multiline": False,
|
|
"placeholder": "Enter file or folder path..."
|
|
}),
|
|
},
|
|
"optional": {},
|
|
}
|
|
|
|
RETURN_TYPES = ("STRING",)
|
|
RETURN_NAMES = ("path",)
|
|
FUNCTION = "open_path"
|
|
CATEGORY = "Grover/System"
|
|
OUTPUT_NODE = True
|
|
|
|
def open_path(self, path):
|
|
"""Process the path and return it (the button action is handled on the frontend)"""
|
|
return (path,)
|
|
|
|
@staticmethod
|
|
def open_file_explorer(path):
|
|
"""Open the given path in the default file explorer"""
|
|
try:
|
|
# Normalize the path
|
|
normalized_path = os.path.normpath(path)
|
|
|
|
# Check if path exists
|
|
if not os.path.exists(normalized_path):
|
|
print(f"Path does not exist: {normalized_path}")
|
|
return False
|
|
|
|
# Get the system platform
|
|
system = platform.system()
|
|
|
|
if system == "Windows":
|
|
# Windows - use explorer
|
|
# Note: Don't use check=True as explorer can return non-zero exit codes even on success
|
|
if os.path.isfile(normalized_path):
|
|
# If it's a file, select it in explorer
|
|
result = subprocess.run(
|
|
['explorer', '/select,', normalized_path], capture_output=True)
|
|
# For files, we can check if explorer process started successfully
|
|
# Only treat severe errors (>1) as failures
|
|
if result.returncode > 1:
|
|
print(
|
|
f"Explorer returned code {result.returncode} for file: {normalized_path}")
|
|
print(
|
|
f"stderr: {result.stderr.decode() if result.stderr else 'None'}")
|
|
return False
|
|
else:
|
|
# If it's a directory, open it
|
|
result = subprocess.run(
|
|
['explorer', normalized_path], capture_output=True)
|
|
# For directories, explorer often returns 1 even on success, so only fail on severe errors
|
|
# Only treat severe errors (>1) as failures
|
|
if result.returncode > 1:
|
|
print(
|
|
f"Explorer returned code {result.returncode} for directory: {normalized_path}")
|
|
print(
|
|
f"stderr: {result.stderr.decode() if result.stderr else 'None'}")
|
|
return False
|
|
elif system == "Darwin": # macOS
|
|
# macOS - use open command
|
|
subprocess.run(['open', normalized_path], check=True)
|
|
elif system == "Linux":
|
|
# Linux - try various file managers
|
|
try:
|
|
subprocess.run(['xdg-open', normalized_path], check=True)
|
|
except FileNotFoundError:
|
|
# Fallback to other common file managers
|
|
for fm in ['nautilus', 'dolphin', 'thunar', 'pcmanfm']:
|
|
try:
|
|
subprocess.run([fm, normalized_path], check=True)
|
|
break
|
|
except FileNotFoundError:
|
|
continue
|
|
else:
|
|
print("No suitable file manager found for Linux")
|
|
return False
|
|
else:
|
|
print(f"Unsupported operating system: {system}")
|
|
return False
|
|
|
|
return True
|
|
|
|
except Exception as e:
|
|
print(f"Error opening path '{path}': {str(e)}")
|
|
return False
|
|
|
|
|
|
# Web endpoint for handling button clicks
|
|
@PromptServer.instance.routes.post("/grover/open_path")
|
|
async def open_path_endpoint(request):
|
|
"""Handle the open path button click from the frontend"""
|
|
try:
|
|
data = await request.json()
|
|
path = data.get("path", "")
|
|
|
|
if not path:
|
|
return web.json_response({"success": False, "error": "No path provided"})
|
|
|
|
success = OpenPathNode.open_file_explorer(path)
|
|
|
|
if success:
|
|
return web.json_response({"success": True, "message": f"Opened path: {path}"})
|
|
else:
|
|
return web.json_response({"success": False, "error": f"Failed to open path: {path}"})
|
|
|
|
except Exception as e:
|
|
return web.json_response({"success": False, "error": str(e)})
|
|
|
|
|
|
# Node registration
|
|
NODE_CLASS_MAPPINGS = {
|
|
"OpenPathNode": OpenPathNode
|
|
}
|
|
|
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
|
"OpenPathNode": "📁 Open Path"
|
|
}
|