Support saves selected model in list when reloaded and loaded page browser. Write backend code get models in json format, and create file json default models for local server. Add buttons refresh model list
238 lines
6.9 KiB
Python
238 lines
6.9 KiB
Python
import os
|
|
import PIL
|
|
from PIL import Image, ImageOps
|
|
import hashlib
|
|
import base64
|
|
from io import BytesIO
|
|
import torch
|
|
import numpy as np
|
|
import requests
|
|
import asyncio
|
|
from server import PromptServer
|
|
import time
|
|
from aiohttp import web
|
|
import folder_paths
|
|
import json
|
|
|
|
|
|
# Message Handling
|
|
# class MessageHolder:
|
|
# messages = {}
|
|
|
|
# @classmethod
|
|
# def addMessage(self, id, message):
|
|
# self.messages[str(id)] = message
|
|
|
|
# @classmethod
|
|
# def waitForMessage(self, id, period = 0.1):
|
|
# sid = str(id)
|
|
# while not (sid in self.messages):
|
|
# time.sleep(period)
|
|
# message = self.messages.pop(str(id),None)
|
|
# return message
|
|
|
|
# handle proxy response
|
|
# @PromptServer.instance.routes.post('/HYPE/proxy_reply')
|
|
# async def proxyHandle(request):
|
|
# post = await request.json()
|
|
# MessageHolder.addMessage(post["node_id"], post["outputs"])
|
|
# return web.json_response({"status": "ok"})
|
|
|
|
|
|
# Global variables
|
|
DEBUG = True
|
|
THREEVIEW_DICT = {} # ThreeView dict instances
|
|
|
|
# Directory threejs
|
|
extension_threejs = os.path.dirname(os.path.realpath(__file__))
|
|
|
|
# Support extension models
|
|
support_ext = ("glb",)
|
|
def getModelsInDir(folder, path):
|
|
list_models = []
|
|
for model in os.scandir(folder):
|
|
if not model.is_file():
|
|
continue
|
|
|
|
model_file_size = model.stat().st_size
|
|
if model_file_size == 0:
|
|
continue
|
|
|
|
model_file = model.name.split(".")
|
|
ext = model_file[-1]
|
|
|
|
if ext not in support_ext:
|
|
continue
|
|
|
|
pathFile = os.path.join(path, model.name)
|
|
|
|
# model_file_name = ".".join(model_file[:-1])
|
|
list_models.append({"name": model.name, "size": model_file_size, "path": pathFile})
|
|
|
|
return list_models
|
|
|
|
|
|
def createJsonFileStartup():
|
|
models_js_dir = os.path.join(extension_threejs, "js")
|
|
models_assets_dir = os.path.join(models_js_dir, "assets")
|
|
|
|
# Assets directory
|
|
if not os.path.exists(models_assets_dir):
|
|
return
|
|
|
|
models = {
|
|
"models": {
|
|
"default": getModelsInDir(models_assets_dir, "./assets/"),
|
|
}
|
|
}
|
|
|
|
saveFilePath = os.path.join(models_js_dir, "models_default.json")
|
|
with open(saveFilePath, "w+", encoding="utf-8") as file:
|
|
file.write(json.dumps(models, indent=4))
|
|
|
|
|
|
# Create on startup default models json file in assets directory
|
|
createJsonFileStartup()
|
|
|
|
|
|
@PromptServer.instance.routes.get("/lth/models")
|
|
async def getModels(request):
|
|
try:
|
|
models_assets_dir = os.path.join(extension_threejs, "js", "assets")
|
|
models_input_dir = os.path.join(folder_paths.get_input_directory(), "ThreeViewModels")
|
|
|
|
models = {
|
|
"default": [],
|
|
"loaded": []
|
|
}
|
|
|
|
# Assets directory
|
|
if os.path.exists(models_assets_dir):
|
|
models["default"] = getModelsInDir(models_assets_dir, "assets/")
|
|
else:
|
|
print(f"ThreeView: Path {models_assets_dir} not exists!")
|
|
|
|
# Input > ThreeViewModels directory
|
|
if os.path.exists(models_input_dir):
|
|
models["loaded"] = getModelsInDir(models_input_dir, "ThreeViewModels/")
|
|
else:
|
|
print(f"ThreeView: Path {models_input_dir} not exists!")
|
|
|
|
return web.json_response({"status":"ok", "models": models}, status=200)
|
|
|
|
except Exception as err:
|
|
print("ThreeView: Get models error", err)
|
|
return web.json_response({"status":"error"}, status=500)
|
|
|
|
@PromptServer.instance.routes.get("/api/lth/")
|
|
async def indexPage(request):
|
|
pathIndex = os.path.join(extension_threejs, "js", "index.html")
|
|
response = web.FileResponse(pathIndex)
|
|
response.headers['Cache-Control'] = 'no-cache'
|
|
response.headers["Pragma"] = "no-cache"
|
|
response.headers["Expires"] = "0"
|
|
return response
|
|
|
|
|
|
@PromptServer.instance.routes.post("/lth/save_complete")
|
|
async def checkSaveImage(request):
|
|
json_data = await request.json()
|
|
unique_id = json_data.get("unique_id", None)
|
|
|
|
if unique_id is not None and unique_id in THREEVIEW_DICT:
|
|
THREEVIEW_DICT[unique_id] = True
|
|
return web.json_response({"status": "Complete"}, status=200)
|
|
|
|
return web.json_response({"status": "Error invalid unique_id"}, status=400)
|
|
|
|
|
|
def waitSaveImage(unique_id, time_out=100):
|
|
for _ in range(time_out):
|
|
if (THREEVIEW_DICT.get(unique_id, False) == True):
|
|
THREEVIEW_DICT[unique_id] = False
|
|
|
|
if DEBUG:
|
|
print(f"File for ThreeView{unique_id} is ready")
|
|
return True
|
|
|
|
time.sleep(0.1)
|
|
|
|
if DEBUG:
|
|
print(f"Timeout waiting for ThreeView{unique_id}")
|
|
return False
|
|
|
|
class ThreeView:
|
|
savedimage = False
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(self):
|
|
return {
|
|
"required": {
|
|
"imageThreejs": ("STRING", {"default":"theejs_image.png,theejs_image_lines.png,theejs_image_depth.png,theejs_image_normal.png"},)
|
|
},
|
|
"hidden": { "unique_id":"UNIQUE_ID" }
|
|
}
|
|
|
|
@classmethod
|
|
def IS_CHANGED(self, imageThreejs, unique_id):
|
|
if unique_id not in THREEVIEW_DICT:
|
|
THREEVIEW_DICT[unique_id] = False
|
|
|
|
THREEVIEW_DICT[unique_id] = False
|
|
# Send to save image
|
|
PromptServer.instance.send_sync("lth_save_image", {"unique_id": unique_id})
|
|
|
|
waitSaveImage(unique_id)
|
|
|
|
return float("NaN") #m.digest().hex()
|
|
|
|
|
|
RETURN_TYPES = ("IMAGE", "IMAGE", "IMAGE", "IMAGE")
|
|
RETURN_NAMES = ("image", "lines", "depth", "normal")
|
|
FUNCTION = "process_three_js_image"
|
|
CATEGORY = "lth"
|
|
|
|
|
|
def process_three_js_image(self, imageThreejs, unique_id):
|
|
imageThreejs = imageThreejs.split(",")
|
|
images_make = []
|
|
|
|
for imageName in imageThreejs:
|
|
pathImage = folder_paths.get_annotated_filepath(imageName)
|
|
|
|
for _ in range(5):
|
|
if os.path.exists(pathImage) and os.path.getsize(pathImage) > 0:
|
|
try:
|
|
with Image.open(pathImage) as i:
|
|
i = ImageOps.exif_transpose(i)
|
|
image = i.convert("RGB")
|
|
image = np.array(image).astype(np.float32) / 255.0
|
|
image = torch.from_numpy(image)[None,]
|
|
images_make.append(image)
|
|
break
|
|
except (OSError, SyntaxError, PIL.UnidentifiedImageError) as e:
|
|
if DEBUG:
|
|
print(f"Error reading image {imageName}: {e}. Retrying...")
|
|
else:
|
|
if DEBUG:
|
|
print(f"File {imageName} not ready. Waiting...")
|
|
|
|
time.sleep(0.5)
|
|
else:
|
|
if DEBUG:
|
|
print(f"Failed to process {imageName} after multiple attempts.")
|
|
images_make.append(None)
|
|
|
|
return tuple(images_make)
|
|
|
|
|
|
NODE_CLASS_MAPPINGS = {
|
|
"ThreeView": ThreeView
|
|
}
|
|
|
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
|
"ThreeView": "Three View"
|
|
}
|
|
|
|
|