|
|
""" |
|
|
Web API endpoints for ND Super Nodes |
|
|
""" |
|
|
|
|
|
from aiohttp import web |
|
|
import os |
|
|
try: |
|
|
import folder_paths |
|
|
except Exception: |
|
|
folder_paths = None |
|
|
from .lora_utils import get_available_loras, extract_trigger_words |
|
|
from .template_manager import get_template_manager |
|
|
from ..civitai_service import get_civitai_service |
|
|
from .version_utils import get_local_version |
|
|
|
|
|
|
|
|
async def get_loras(request): |
|
|
"""Get list of available LoRA files""" |
|
|
try: |
|
|
loras = get_available_loras() |
|
|
return web.json_response({"loras": loras}) |
|
|
except Exception as e: |
|
|
return web.json_response({"error": str(e)}, status=500) |
|
|
|
|
|
|
|
|
async def get_files(request): |
|
|
"""Generic file lister using ComfyUI folder_paths (e.g., folder_name=loras|vae|checkpoints)""" |
|
|
try: |
|
|
folder_name = request.rel_url.query.get("folder_name") |
|
|
ext_param = request.rel_url.query.get("extensions", "") |
|
|
extensions = [e.strip().lower() for e in ext_param.split(",") if e.strip()] |
|
|
|
|
|
if not folder_name: |
|
|
return web.json_response({"error": "folder_name is required", "files": []}, status=400) |
|
|
|
|
|
if folder_paths is None: |
|
|
return web.json_response({"error": "folder_paths unavailable", "files": []}, status=500) |
|
|
|
|
|
|
|
|
mapped = folder_paths.map_legacy(folder_name) |
|
|
dirs, supported = folder_paths.folder_names_and_paths.get(mapped, ([], set())) |
|
|
if not dirs: |
|
|
|
|
|
dirs, supported = folder_paths.folder_names_and_paths.get(folder_name, ([], set())) |
|
|
|
|
|
|
|
|
if extensions: |
|
|
supported = set([e.lower() for e in extensions]) |
|
|
|
|
|
out_files = [] |
|
|
for d in dirs: |
|
|
if not os.path.isdir(d): |
|
|
continue |
|
|
try: |
|
|
|
|
|
for root, _, files in os.walk(d): |
|
|
for name in files: |
|
|
fp = os.path.join(root, name) |
|
|
if not os.path.isfile(fp): |
|
|
continue |
|
|
_, ext = os.path.splitext(name) |
|
|
if supported and ext.lower() not in supported and supported != {""}: |
|
|
continue |
|
|
st = os.stat(fp) |
|
|
out_files.append({ |
|
|
"name": name, |
|
|
"path": os.path.relpath(fp, d).replace("\\", "/"), |
|
|
"extension": ext.lower(), |
|
|
"size": st.st_size, |
|
|
"modified": st.st_mtime |
|
|
}) |
|
|
except Exception: |
|
|
continue |
|
|
|
|
|
out_files.sort(key=lambda x: x["name"].lower()) |
|
|
return web.json_response({"files": out_files, "total": len(out_files)}) |
|
|
except Exception as e: |
|
|
return web.json_response({"error": str(e), "files": []}, status=500) |
|
|
|
|
|
|
|
|
async def get_templates(request): |
|
|
"""Get list of available templates or a specific template by query param""" |
|
|
try: |
|
|
template_manager = get_template_manager() |
|
|
|
|
|
|
|
|
name = request.rel_url.query.get("name") |
|
|
if name: |
|
|
template = template_manager.load_template(name) |
|
|
if template: |
|
|
return web.json_response(template) |
|
|
return web.json_response({"error": "Template not found"}, status=404) |
|
|
|
|
|
templates = template_manager.list_templates() |
|
|
return web.json_response({"templates": templates}) |
|
|
except Exception as e: |
|
|
return web.json_response({"error": str(e)}, status=500) |
|
|
|
|
|
|
|
|
async def save_template(request): |
|
|
"""Save a LoRA template or handle action-based operations (e.g., delete)""" |
|
|
try: |
|
|
data = await request.json() |
|
|
action = data.get("action") |
|
|
|
|
|
|
|
|
if action == "delete": |
|
|
name = data.get("name") |
|
|
if not name: |
|
|
return web.json_response({"error": "Template name is required"}, status=400) |
|
|
template_manager = get_template_manager() |
|
|
deleted = template_manager.delete_template(name) |
|
|
if deleted: |
|
|
return web.json_response({"success": True, "message": f"Template '{name}' deleted"}) |
|
|
return web.json_response({"error": "Template not found or could not be deleted"}, status=404) |
|
|
|
|
|
name = data.get("name") |
|
|
|
|
|
lora_configs = data.get("lora_configs") |
|
|
if lora_configs is None: |
|
|
lora_configs = data.get("loras", []) |
|
|
|
|
|
if not name: |
|
|
return web.json_response({"error": "Template name is required"}, status=400) |
|
|
|
|
|
template_manager = get_template_manager() |
|
|
success = template_manager.save_template(name, lora_configs) |
|
|
|
|
|
if success: |
|
|
return web.json_response({"success": True, "message": f"Template '{name}' saved"}) |
|
|
else: |
|
|
return web.json_response({"error": "Failed to save template"}, status=500) |
|
|
|
|
|
except Exception as e: |
|
|
return web.json_response({"error": str(e)}, status=500) |
|
|
|
|
|
|
|
|
async def load_template(request): |
|
|
"""Load a LoRA template""" |
|
|
try: |
|
|
template_name = request.match_info.get("name") |
|
|
|
|
|
if not template_name: |
|
|
return web.json_response({"error": "Template name is required"}, status=400) |
|
|
|
|
|
template_manager = get_template_manager() |
|
|
template_data = template_manager.load_template(template_name) |
|
|
|
|
|
if template_data: |
|
|
return web.json_response(template_data) |
|
|
else: |
|
|
return web.json_response({"error": "Template not found"}, status=404) |
|
|
|
|
|
except Exception as e: |
|
|
return web.json_response({"error": str(e)}, status=500) |
|
|
|
|
|
|
|
|
async def get_civitai_info(request): |
|
|
"""Get CivitAI info for a LoRA""" |
|
|
try: |
|
|
data = await request.json() |
|
|
lora_filename = data.get("lora_filename") |
|
|
|
|
|
if not lora_filename: |
|
|
return web.json_response({"error": "LoRA filename is required"}, status=400) |
|
|
|
|
|
civitai_service = get_civitai_service() |
|
|
trigger_words = civitai_service.get_trigger_words_by_filename(lora_filename) |
|
|
|
|
|
|
|
|
if not trigger_words: |
|
|
try: |
|
|
meta_words = extract_trigger_words(lora_filename) |
|
|
if meta_words: |
|
|
trigger_words = meta_words |
|
|
except Exception: |
|
|
pass |
|
|
|
|
|
|
|
|
payload = { |
|
|
"lora_filename": lora_filename, |
|
|
"trigger_words": trigger_words, |
|
|
"trainedWords": trigger_words, |
|
|
"success": True |
|
|
} |
|
|
|
|
|
return web.json_response(payload) |
|
|
|
|
|
except Exception as e: |
|
|
return web.json_response({"error": str(e)}, status=500) |
|
|
|
|
|
|
|
|
async def delete_template(request): |
|
|
"""Delete a template via JSON body: { name }""" |
|
|
try: |
|
|
data = await request.json() |
|
|
name = data.get("name") |
|
|
if not name: |
|
|
return web.json_response({"error": "Template name is required"}, status=400) |
|
|
template_manager = get_template_manager() |
|
|
deleted = template_manager.delete_template(name) |
|
|
if deleted: |
|
|
return web.json_response({"success": True, "message": f"Template '{name}' deleted"}) |
|
|
return web.json_response({"error": "Template not found"}, status=404) |
|
|
except Exception as e: |
|
|
return web.json_response({"error": str(e)}, status=500) |
|
|
|
|
|
|
|
|
async def delete_template_by_name(request): |
|
|
"""Delete a template by path parameter""" |
|
|
try: |
|
|
name = request.match_info.get("name") |
|
|
if not name: |
|
|
return web.json_response({"error": "Template name is required"}, status=400) |
|
|
template_manager = get_template_manager() |
|
|
deleted = template_manager.delete_template(name) |
|
|
if deleted: |
|
|
return web.json_response({"success": True, "message": f"Template '{name}' deleted"}) |
|
|
return web.json_response({"error": "Template not found"}, status=404) |
|
|
except Exception as e: |
|
|
return web.json_response({"error": str(e)}, status=500) |
|
|
|
|
|
|
|
|
async def get_version_info(request): |
|
|
"""Return local version info only (forked version, no update checking).""" |
|
|
try: |
|
|
local_version = get_local_version() |
|
|
return web.json_response({ |
|
|
"localVersion": local_version, |
|
|
"hasUpdate": False, |
|
|
"message": "This is a forked version. Update checking disabled." |
|
|
}) |
|
|
except Exception as e: |
|
|
return web.json_response({"error": str(e)}, status=500) |
|
|
|
|
|
|
|
|
|
|
|
def register_routes(app): |
|
|
"""Register all Super LoRA Loader routes""" |
|
|
app.router.add_get("/super_lora/loras", get_loras) |
|
|
app.router.add_get("/super_lora/files", get_files) |
|
|
app.router.add_get("/super_lora/templates", get_templates) |
|
|
app.router.add_post("/super_lora/templates", save_template) |
|
|
app.router.add_get("/super_lora/templates/{name}", load_template) |
|
|
|
|
|
app.router.add_delete("/super_lora/templates", delete_template) |
|
|
app.router.add_post("/super_lora/templates/delete", delete_template) |
|
|
app.router.add_delete("/super_lora/templates/{name}", delete_template_by_name) |
|
|
app.router.add_post("/super_lora/civitai_info", get_civitai_info) |
|
|
app.router.add_get("/super_lora/version", get_version_info) |
|
|
|
|
|
|
|
|
app.router.add_get("/superlora/loras", get_loras) |
|
|
app.router.add_get("/superlora/files", get_files) |
|
|
app.router.add_get("/superlora/templates", get_templates) |
|
|
app.router.add_post("/superlora/templates", save_template) |
|
|
app.router.add_get("/superlora/templates/{name}", load_template) |
|
|
app.router.add_delete("/superlora/templates", delete_template) |
|
|
app.router.add_post("/superlora/templates/delete", delete_template) |
|
|
app.router.add_delete("/superlora/templates/{name}", delete_template_by_name) |
|
|
app.router.add_post("/superlora/civitai_info", get_civitai_info) |
|
|
app.router.add_get("/superlora/version", get_version_info) |
|
|
|