-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathQuickSave.py
More file actions
executable file
·134 lines (110 loc) · 4.23 KB
/
Copy pathQuickSave.py
File metadata and controls
executable file
·134 lines (110 loc) · 4.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
"""
QuickSave Backend - API endpoint for the QuickSave toolbar button.
This module registers the /quicksave POST endpoint that receives images
from the frontend and saves them to disk.
"""
import server
import os
import re
import base64
from aiohttp import web
# Default save directory: ComfyUI's output folder
# Can be overridden by passing savePath in the request
DEFAULT_OUTPUT_DIR = os.path.join(
os.path.dirname(os.path.realpath(__file__)),
"..", "..", "output", "quicksave"
)
@server.PromptServer.instance.routes.post("/quicksave")
async def handle_quick_save(request):
"""
API endpoint at /quicksave that receives image data and saves it to disk.
Expected JSON payload:
- imageData: Base64-encoded image data (with data URL prefix)
- filename: Original filename (will be sanitized)
- savePath: Optional custom save directory
Returns:
- status: "success", "skipped", or "error"
- message: Human-readable result message
"""
try:
data = await request.json()
image_data_b64 = data.get("imageData")
filename = data.get("filename", "saved_image.png")
save_path_str = data.get("savePath", "")
if not image_data_b64:
return web.json_response(
{"status": "error", "message": "Missing image data"},
status=400
)
# Sanitize filename to prevent path traversal and special chars
if filename:
# Allow alphanumeric, dots, underscores, hyphens
filename = re.sub(r'[^a-zA-Z0-9._-]', '_', filename)
else:
filename = "saved_image.png"
# Decode base64 image data
# Format: "data:image/png;base64,iVBORw0KGgo..."
try:
header, encoded = image_data_b64.split(",", 1)
image_data = base64.b64decode(encoded)
except (ValueError, base64.binascii.Error) as e:
return web.json_response(
{"status": "error", "message": f"Invalid image data: {e}"},
status=400
)
# Determine save directory
if save_path_str and os.path.isdir(save_path_str):
base_dir = save_path_str
else:
base_dir = DEFAULT_OUTPUT_DIR
# Ensure directory exists
os.makedirs(base_dir, exist_ok=True)
initial_path = os.path.join(base_dir, filename)
# Prevent overwriting and detect duplicates
final_path = initial_path
base, ext = os.path.splitext(initial_path)
counter = 1
while os.path.exists(final_path):
try:
with open(final_path, "rb") as f:
existing_data = f.read()
if existing_data == image_data:
print(f"[QuickSave] Skipped duplicate: {final_path}")
return web.json_response({
"status": "skipped",
"message": f"Duplicate skipped: {os.path.basename(final_path)}"
})
except Exception as e:
print(f"[QuickSave] Warning: Could not check for duplicate: {e}")
final_path = f"{base}_{counter}{ext}"
counter += 1
# Write the file
with open(final_path, "wb") as f:
f.write(image_data)
print(f"[QuickSave] Saved: {final_path}")
return web.json_response({
"status": "success",
"message": f"Saved to {final_path}"
})
except Exception as e:
print(f"[QuickSave] Error: {e}")
return web.json_response(
{"status": "error", "message": str(e)},
status=500
)
class QuickSave:
"""
Dummy node class - exists only to ensure ComfyUI loads this module
and registers the /quicksave web route above.
The actual QuickSave functionality is in the toolbar button (JS)
and the API endpoint (above).
"""
@classmethod
def INPUT_TYPES(cls):
return {"required": {}}
RETURN_TYPES = ()
FUNCTION = "execute"
OUTPUT_NODE = True
CATEGORY = "Custom/QuickSave"
def execute(self, **kwargs):
return {}