Add custom nodes, Civitai loras (LFS), and vast.ai setup script
Some checks failed
Python Linting / Run Ruff (push) Has been cancelled
Python Linting / Run Pylint (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.10, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.11, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.12, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-unix-nightly (12.1, , linux, 3.11, [self-hosted Linux], nightly) (push) Has been cancelled
Execution Tests / test (macos-latest) (push) Has been cancelled
Execution Tests / test (ubuntu-latest) (push) Has been cancelled
Execution Tests / test (windows-latest) (push) Has been cancelled
Test server launches without errors / test (push) Has been cancelled
Unit Tests / test (macos-latest) (push) Has been cancelled
Unit Tests / test (ubuntu-latest) (push) Has been cancelled
Unit Tests / test (windows-2022) (push) Has been cancelled
Some checks failed
Python Linting / Run Ruff (push) Has been cancelled
Python Linting / Run Pylint (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.10, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.11, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.12, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-unix-nightly (12.1, , linux, 3.11, [self-hosted Linux], nightly) (push) Has been cancelled
Execution Tests / test (macos-latest) (push) Has been cancelled
Execution Tests / test (ubuntu-latest) (push) Has been cancelled
Execution Tests / test (windows-latest) (push) Has been cancelled
Test server launches without errors / test (push) Has been cancelled
Unit Tests / test (macos-latest) (push) Has been cancelled
Unit Tests / test (ubuntu-latest) (push) Has been cancelled
Unit Tests / test (windows-2022) (push) Has been cancelled
Includes 30 custom nodes committed directly, 7 Civitai-exclusive loras stored via Git LFS, and a setup script that installs all dependencies and downloads HuggingFace-hosted models on vast.ai. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
372
custom_nodes/ComfyUI-Easy-Use/py/libs/api/bizyair.py
Normal file
372
custom_nodes/ComfyUI-Easy-Use/py/libs/api/bizyair.py
Normal file
@@ -0,0 +1,372 @@
|
||||
import yaml
|
||||
import pathlib
|
||||
import base64
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import pickle
|
||||
import zlib
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
from enum import Enum
|
||||
from functools import singledispatch
|
||||
from typing import Any, List, Union
|
||||
|
||||
import numpy as np
|
||||
import torch
|
||||
from PIL import Image
|
||||
|
||||
root_path = pathlib.Path(__file__).parent.parent.parent.parent
|
||||
config_path = os.path.join(root_path, 'config.yaml')
|
||||
|
||||
class BizyAIRAPI:
|
||||
def __init__(self):
|
||||
self.base_url = 'https://bizyair-api.siliconflow.cn/x/v1'
|
||||
self.api_key = None
|
||||
|
||||
|
||||
def getAPIKey(self):
|
||||
if self.api_key is None:
|
||||
if os.path.isfile(config_path):
|
||||
with open(config_path, 'r') as f:
|
||||
data = yaml.load(f, Loader=yaml.FullLoader)
|
||||
if 'BIZYAIR_API_KEY' not in data:
|
||||
raise Exception("Please add BIZYAIR_API_KEY to config.yaml")
|
||||
self.api_key = data['BIZYAIR_API_KEY']
|
||||
else:
|
||||
raise Exception("Please add config.yaml to root path")
|
||||
return self.api_key
|
||||
|
||||
def send_post_request(self, url, payload, headers):
|
||||
try:
|
||||
data = json.dumps(payload).encode("utf-8")
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method="POST")
|
||||
with urllib.request.urlopen(req) as response:
|
||||
response_data = response.read().decode("utf-8")
|
||||
return response_data
|
||||
except urllib.error.URLError as e:
|
||||
if "Unauthorized" in str(e):
|
||||
raise Exception(
|
||||
"Key is invalid, please refer to https://cloud.siliconflow.cn to get the API key.\n"
|
||||
"If you have the key, please click the 'BizyAir Key' button at the bottom right to set the key."
|
||||
)
|
||||
else:
|
||||
raise Exception(
|
||||
f"Failed to connect to the server: {e}, if you have no key, "
|
||||
)
|
||||
|
||||
# joycaption
|
||||
def joyCaption(self, payload, image, apikey_override=None, API_URL='/supernode/joycaption2'):
|
||||
if apikey_override is not None:
|
||||
api_key = apikey_override
|
||||
else:
|
||||
api_key = self.getAPIKey()
|
||||
url = f"{self.base_url}{API_URL}"
|
||||
print('Sending request to:', url)
|
||||
auth = f"Bearer {api_key}"
|
||||
headers = {
|
||||
"accept": "application/json",
|
||||
"content-type": "application/json",
|
||||
"authorization": auth,
|
||||
}
|
||||
input_image = encode_data(image, disable_image_marker=True)
|
||||
payload["image"] = input_image
|
||||
|
||||
ret: str = self.send_post_request(url=url, payload=payload, headers=headers)
|
||||
ret = json.loads(ret)
|
||||
|
||||
try:
|
||||
if "result" in ret:
|
||||
ret = json.loads(ret["result"])
|
||||
except Exception as e:
|
||||
raise Exception(f"Unexpected response: {ret} {e=}")
|
||||
|
||||
if ret["type"] == "error":
|
||||
raise Exception(ret["message"])
|
||||
|
||||
msg = ret["data"]
|
||||
if msg["type"] not in ("comfyair", "bizyair",):
|
||||
raise Exception(f"Unexpected response type: {msg}")
|
||||
|
||||
caption = msg["data"]
|
||||
|
||||
return caption
|
||||
|
||||
bizyairAPI = BizyAIRAPI()
|
||||
|
||||
|
||||
|
||||
BIZYAIR_DEBUG = True
|
||||
# Marker to identify base64-encoded tensors
|
||||
TENSOR_MARKER = "TENSOR:"
|
||||
IMAGE_MARKER = "IMAGE:"
|
||||
|
||||
|
||||
class TaskStatus(Enum):
|
||||
PENDING = "pending"
|
||||
PROCESSING = "processing"
|
||||
COMPLETED = "completed"
|
||||
|
||||
|
||||
def convert_image_to_rgb(image: Image.Image) -> Image.Image:
|
||||
if image.mode != "RGB":
|
||||
return image.convert("RGB")
|
||||
return image
|
||||
|
||||
|
||||
def encode_image_to_base64(
|
||||
image: Image.Image, format: str = "png", quality: int = 100, lossless=False
|
||||
) -> str:
|
||||
image = convert_image_to_rgb(image)
|
||||
with io.BytesIO() as output:
|
||||
image.save(output, format=format, quality=quality, lossless=lossless)
|
||||
output.seek(0)
|
||||
img_bytes = output.getvalue()
|
||||
if BIZYAIR_DEBUG:
|
||||
print(f"encode_image_to_base64: {format_bytes(len(img_bytes))}")
|
||||
return base64.b64encode(img_bytes).decode("utf-8")
|
||||
|
||||
|
||||
def decode_base64_to_np(img_data: str, format: str = "png") -> np.ndarray:
|
||||
img_bytes = base64.b64decode(img_data)
|
||||
if BIZYAIR_DEBUG:
|
||||
print(f"decode_base64_to_np: {format_bytes(len(img_bytes))}")
|
||||
with io.BytesIO(img_bytes) as input_buffer:
|
||||
img = Image.open(input_buffer)
|
||||
# https://github.com/comfyanonymous/ComfyUI/blob/a178e25912b01abf436eba1cfaab316ba02d272d/nodes.py#L1511
|
||||
img = img.convert("RGB")
|
||||
return np.array(img)
|
||||
|
||||
|
||||
def decode_base64_to_image(img_data: str) -> Image.Image:
|
||||
img_bytes = base64.b64decode(img_data)
|
||||
with io.BytesIO(img_bytes) as input_buffer:
|
||||
img = Image.open(input_buffer)
|
||||
if BIZYAIR_DEBUG:
|
||||
format_info = img.format.upper() if img.format else "Unknown"
|
||||
print(f"decode image format: {format_info}")
|
||||
return img
|
||||
|
||||
|
||||
def format_bytes(num_bytes: int) -> str:
|
||||
"""
|
||||
Converts a number of bytes to a human-readable string with units (B, KB, or MB).
|
||||
|
||||
:param num_bytes: The number of bytes to convert.
|
||||
:return: A string representing the number of bytes in a human-readable format.
|
||||
"""
|
||||
if num_bytes < 1024:
|
||||
return f"{num_bytes} B"
|
||||
elif num_bytes < 1024 * 1024:
|
||||
return f"{num_bytes / 1024:.2f} KB"
|
||||
else:
|
||||
return f"{num_bytes / (1024 * 1024):.2f} MB"
|
||||
|
||||
|
||||
def _legacy_encode_comfy_image(image: torch.Tensor, image_format="png") -> str:
|
||||
input_image = image.cpu().detach().numpy()
|
||||
i = 255.0 * input_image[0]
|
||||
input_image = np.clip(i, 0, 255).astype(np.uint8)
|
||||
base64ed_image = encode_image_to_base64(
|
||||
Image.fromarray(input_image), format=image_format
|
||||
)
|
||||
return base64ed_image
|
||||
|
||||
|
||||
def _legacy_decode_comfy_image(
|
||||
img_data: Union[List, str], image_format="png"
|
||||
) -> torch.tensor:
|
||||
if isinstance(img_data, List):
|
||||
decoded_imgs = [decode_comfy_image(x, old_version=True) for x in img_data]
|
||||
|
||||
combined_imgs = torch.cat(decoded_imgs, dim=0)
|
||||
return combined_imgs
|
||||
|
||||
out = decode_base64_to_np(img_data, format=image_format)
|
||||
out = np.array(out).astype(np.float32) / 255.0
|
||||
output = torch.from_numpy(out)[None,]
|
||||
return output
|
||||
|
||||
|
||||
def _new_encode_comfy_image(images: torch.Tensor, image_format="WEBP", **kwargs) -> str:
|
||||
"""https://docs.comfy.org/essentials/custom_node_snippets#save-an-image-batch
|
||||
Encode a batch of images to base64 strings.
|
||||
|
||||
Args:
|
||||
images (torch.Tensor): A batch of images.
|
||||
image_format (str, optional): The format of the images. Defaults to "WEBP".
|
||||
|
||||
Returns:
|
||||
str: A JSON string containing the base64-encoded images.
|
||||
"""
|
||||
results = {}
|
||||
for batch_number, image in enumerate(images):
|
||||
i = 255.0 * image.cpu().numpy()
|
||||
img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8))
|
||||
base64ed_image = encode_image_to_base64(img, format=image_format, **kwargs)
|
||||
results[batch_number] = base64ed_image
|
||||
|
||||
return json.dumps(results)
|
||||
|
||||
|
||||
def _new_decode_comfy_image(img_datas: str, image_format="WEBP") -> torch.tensor:
|
||||
"""
|
||||
Decode a batch of base64-encoded images.
|
||||
|
||||
Args:
|
||||
img_datas (str): A JSON string containing the base64-encoded images.
|
||||
image_format (str, optional): The format of the images. Defaults to "WEBP".
|
||||
|
||||
Returns:
|
||||
torch.Tensor: A tensor containing the decoded images.
|
||||
"""
|
||||
img_datas = json.loads(img_datas)
|
||||
|
||||
decoded_imgs = []
|
||||
for img_data in img_datas.values():
|
||||
decoded_image = decode_base64_to_np(img_data, format=image_format)
|
||||
decoded_image = np.array(decoded_image).astype(np.float32) / 255.0
|
||||
decoded_imgs.append(torch.from_numpy(decoded_image)[None,])
|
||||
|
||||
return torch.cat(decoded_imgs, dim=0)
|
||||
|
||||
|
||||
def encode_comfy_image(
|
||||
image: torch.Tensor, image_format="WEBP", old_version=False, lossless=False
|
||||
) -> str:
|
||||
if old_version:
|
||||
return _legacy_encode_comfy_image(image, image_format)
|
||||
return _new_encode_comfy_image(image, image_format, lossless=lossless)
|
||||
|
||||
|
||||
def decode_comfy_image(
|
||||
img_data: Union[List, str], image_format="WEBP", old_version=False
|
||||
) -> torch.tensor:
|
||||
if old_version:
|
||||
return _legacy_decode_comfy_image(img_data, image_format)
|
||||
return _new_decode_comfy_image(img_data, image_format)
|
||||
|
||||
|
||||
def tensor_to_base64(tensor: torch.Tensor, compress=True) -> str:
|
||||
tensor_np = tensor.cpu().detach().numpy()
|
||||
|
||||
tensor_bytes = pickle.dumps(tensor_np)
|
||||
if compress:
|
||||
tensor_bytes = zlib.compress(tensor_bytes)
|
||||
|
||||
tensor_b64 = base64.b64encode(tensor_bytes).decode("utf-8")
|
||||
return tensor_b64
|
||||
|
||||
|
||||
def base64_to_tensor(tensor_b64: str, compress=True) -> torch.Tensor:
|
||||
tensor_bytes = base64.b64decode(tensor_b64)
|
||||
|
||||
if compress:
|
||||
tensor_bytes = zlib.decompress(tensor_bytes)
|
||||
|
||||
tensor_np = pickle.loads(tensor_bytes)
|
||||
|
||||
tensor = torch.from_numpy(tensor_np)
|
||||
return tensor
|
||||
|
||||
|
||||
@singledispatch
|
||||
def decode_data(input, old_version=False):
|
||||
raise NotImplementedError(f"Unsupported type: {type(input)}")
|
||||
|
||||
|
||||
@decode_data.register(int)
|
||||
@decode_data.register(float)
|
||||
@decode_data.register(bool)
|
||||
@decode_data.register(type(None))
|
||||
def _(input, **kwargs):
|
||||
return input
|
||||
|
||||
|
||||
@decode_data.register(dict)
|
||||
def _(input, **kwargs):
|
||||
return {k: decode_data(v, **kwargs) for k, v in input.items()}
|
||||
|
||||
|
||||
@decode_data.register(list)
|
||||
def _(input, **kwargs):
|
||||
return [decode_data(x, **kwargs) for x in input]
|
||||
|
||||
|
||||
@decode_data.register(str)
|
||||
def _(input: str, **kwargs):
|
||||
if input.startswith(TENSOR_MARKER):
|
||||
tensor_b64 = input[len(TENSOR_MARKER) :]
|
||||
return base64_to_tensor(tensor_b64)
|
||||
elif input.startswith(IMAGE_MARKER):
|
||||
tensor_b64 = input[len(IMAGE_MARKER) :]
|
||||
old_version = kwargs.get("old_version", False)
|
||||
return decode_comfy_image(tensor_b64, old_version=old_version)
|
||||
return input
|
||||
|
||||
|
||||
@singledispatch
|
||||
def encode_data(output, disable_image_marker=False, old_version=False):
|
||||
raise NotImplementedError(f"Unsupported type: {type(output)}")
|
||||
|
||||
|
||||
@encode_data.register(dict)
|
||||
def _(output, **kwargs):
|
||||
return {k: encode_data(v, **kwargs) for k, v in output.items()}
|
||||
|
||||
|
||||
@encode_data.register(list)
|
||||
def _(output, **kwargs):
|
||||
return [encode_data(x, **kwargs) for x in output]
|
||||
|
||||
|
||||
def is_image_tensor(tensor) -> bool:
|
||||
"""https://docs.comfy.org/essentials/custom_node_datatypes#image
|
||||
|
||||
Check if the given tensor is in the format of an IMAGE (shape [B, H, W, C] where C=3).
|
||||
|
||||
`Args`:
|
||||
tensor (torch.Tensor): The tensor to check.
|
||||
|
||||
`Returns`:
|
||||
bool: True if the tensor is in the IMAGE format, False otherwise.
|
||||
"""
|
||||
try:
|
||||
if not isinstance(tensor, torch.Tensor):
|
||||
return False
|
||||
|
||||
if len(tensor.shape) != 4:
|
||||
return False
|
||||
|
||||
B, H, W, C = tensor.shape
|
||||
if C != 3:
|
||||
return False
|
||||
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
|
||||
@encode_data.register(torch.Tensor)
|
||||
def _(output, **kwargs):
|
||||
if is_image_tensor(output) and not kwargs.get("disable_image_marker", False):
|
||||
old_version = kwargs.get("old_version", False)
|
||||
lossless = kwargs.get("lossless", True)
|
||||
return IMAGE_MARKER + encode_comfy_image(
|
||||
output, image_format="WEBP", old_version=old_version, lossless=lossless
|
||||
)
|
||||
return TENSOR_MARKER + tensor_to_base64(output)
|
||||
|
||||
|
||||
@encode_data.register(int)
|
||||
@encode_data.register(float)
|
||||
@encode_data.register(bool)
|
||||
@encode_data.register(type(None))
|
||||
def _(output, **kwargs):
|
||||
return output
|
||||
|
||||
|
||||
@encode_data.register(str)
|
||||
def _(output, **kwargs):
|
||||
return output
|
||||
51
custom_nodes/ComfyUI-Easy-Use/py/libs/api/fluxai.py
Normal file
51
custom_nodes/ComfyUI-Easy-Use/py/libs/api/fluxai.py
Normal file
@@ -0,0 +1,51 @@
|
||||
import json
|
||||
import os
|
||||
import yaml
|
||||
import requests
|
||||
import pathlib
|
||||
from aiohttp import web
|
||||
|
||||
root_path = pathlib.Path(__file__).parent.parent.parent.parent
|
||||
config_path = os.path.join(root_path,'config.yaml')
|
||||
class FluxAIAPI:
|
||||
def __init__(self):
|
||||
self.api_url = "https://fluxaiimagegenerator.com/api"
|
||||
self.origin = "https://fluxaiimagegenerator.com"
|
||||
self.user_agent = None
|
||||
self.cookie = None
|
||||
|
||||
def promptGenerate(self, text, cookies=None):
|
||||
cookie = self.cookie if cookies is None else cookies
|
||||
if cookie is None:
|
||||
if os.path.isfile(config_path):
|
||||
with open(config_path, 'r') as f:
|
||||
data = yaml.load(f, Loader=yaml.FullLoader)
|
||||
if 'FLUXAI_COOKIE' not in data:
|
||||
raise Exception("Please add FLUXAI_COOKIE to config.yaml")
|
||||
if "FLUXAI_USER_AGENT" in data:
|
||||
self.user_agent = data["FLUXAI_USER_AGENT"]
|
||||
self.cookie = cookie = data['FLUXAI_COOKIE']
|
||||
|
||||
headers = {
|
||||
"Cookie": cookie,
|
||||
"Referer": "https://fluxaiimagegenerator.com/flux-prompt-generator",
|
||||
"Origin": self.origin,
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
if self.user_agent is not None:
|
||||
headers['User-Agent'] = self.user_agent
|
||||
|
||||
url = self.api_url + '/prompt'
|
||||
json = {
|
||||
"prompt": text
|
||||
}
|
||||
|
||||
response = requests.post(url, json=json, headers=headers)
|
||||
res = response.json()
|
||||
if "error" in res:
|
||||
return res['error']
|
||||
elif "data" in res and "prompt" in res['data']:
|
||||
return res['data']['prompt']
|
||||
|
||||
fluxaiAPI = FluxAIAPI()
|
||||
|
||||
200
custom_nodes/ComfyUI-Easy-Use/py/libs/api/stability.py
Normal file
200
custom_nodes/ComfyUI-Easy-Use/py/libs/api/stability.py
Normal file
@@ -0,0 +1,200 @@
|
||||
import json
|
||||
import os
|
||||
import yaml
|
||||
import requests
|
||||
import pathlib
|
||||
from aiohttp import web
|
||||
from server import PromptServer
|
||||
from ..image import tensor2pil, pil2tensor, image2base64, pil2byte
|
||||
from ..log import log_node_error
|
||||
|
||||
|
||||
root_path = pathlib.Path(__file__).parent.parent.parent.parent
|
||||
config_path = os.path.join(root_path,'config.yaml')
|
||||
default_key = [{'name':'Default', 'key':''}]
|
||||
|
||||
|
||||
class StabilityAPI:
|
||||
def __init__(self):
|
||||
self.api_url = "https://api.stability.ai"
|
||||
self.api_keys = None
|
||||
self.api_current = 0
|
||||
self.user_info = {}
|
||||
|
||||
def getErrors(self, code):
|
||||
errors = {
|
||||
400: "Bad Request",
|
||||
403: "ApiKey Forbidden",
|
||||
413: "Your request was larger than 10MiB.",
|
||||
429: "You have made more than 150 requests in 10 seconds.",
|
||||
500: "Internal Server Error",
|
||||
}
|
||||
return errors.get(code, "Unknown Error")
|
||||
|
||||
def getAPIKeys(self):
|
||||
if os.path.isfile(config_path):
|
||||
with open(config_path, 'r') as f:
|
||||
data = yaml.load(f, Loader=yaml.FullLoader)
|
||||
if not data:
|
||||
data = {'STABILITY_API_KEY': default_key, 'STABILITY_API_DEFAULT':0}
|
||||
with open(config_path, 'w') as f:
|
||||
yaml.dump(data, f)
|
||||
if 'STABILITY_API_KEY' not in data:
|
||||
data['STABILITY_API_KEY'] = default_key
|
||||
data['STABILITY_API_DEFAULT'] = 0
|
||||
with open(config_path, 'w') as f:
|
||||
yaml.dump(data, f)
|
||||
api_keys = data['STABILITY_API_KEY']
|
||||
self.api_current = data['STABILITY_API_DEFAULT']
|
||||
self.api_keys = api_keys
|
||||
return api_keys
|
||||
else:
|
||||
# create a yaml file
|
||||
with open(config_path, 'w') as f:
|
||||
data = {'STABILITY_API_KEY': default_key, 'STABILITY_API_DEFAULT':0}
|
||||
yaml.dump(data, f)
|
||||
return data['STABILITY_API_KEY']
|
||||
pass
|
||||
|
||||
def setAPIKeys(self, api_keys):
|
||||
if len(api_keys) > 0:
|
||||
self.api_keys = api_keys
|
||||
# load and save the yaml file
|
||||
with open(config_path, 'r') as f:
|
||||
data = yaml.load(f, Loader=yaml.FullLoader)
|
||||
data['STABILITY_API_KEY'] = api_keys
|
||||
with open(config_path, 'w') as f:
|
||||
yaml.dump(data, f)
|
||||
return True
|
||||
|
||||
def setAPIDefault(self, current):
|
||||
if current is not None:
|
||||
self.api_current = current
|
||||
# load and save the yaml file
|
||||
with open(config_path, 'r') as f:
|
||||
data = yaml.load(f, Loader=yaml.FullLoader)
|
||||
data['STABILITY_API_DEFAULT'] = current
|
||||
with open(config_path, 'w') as f:
|
||||
yaml.dump(data, f)
|
||||
return True
|
||||
|
||||
def generate_sd3_image(self, prompt, negative_prompt, aspect_ratio, model, seed, mode='text-to-image', image=None, strength=1, output_format='png', node_name='easy stableDiffusion3API'):
|
||||
url = f"{self.api_url}/v2beta/stable-image/generate/sd3"
|
||||
api_key = self.api_keys[self.api_current]['key']
|
||||
files = None
|
||||
data = {
|
||||
"prompt": prompt,
|
||||
"mode": mode,
|
||||
"model": model,
|
||||
"seed": seed,
|
||||
"output_format": output_format,
|
||||
}
|
||||
if model == 'sd3':
|
||||
data['negative_prompt'] = negative_prompt
|
||||
|
||||
if mode == 'text-to-image':
|
||||
files = {"none": ''}
|
||||
data['aspect_ratio'] = aspect_ratio
|
||||
elif mode == 'image-to-image':
|
||||
pil_image = tensor2pil(image)
|
||||
image_byte = pil2byte(pil_image)
|
||||
files = {"image": ("output.png", image_byte, 'image/png')}
|
||||
data['strength'] = strength
|
||||
|
||||
response = requests.post(url,
|
||||
headers={"authorization": f"{api_key}", "accept": "application/json"},
|
||||
files=files,
|
||||
data=data,
|
||||
)
|
||||
if response.status_code == 200:
|
||||
PromptServer.instance.send_sync('stable-diffusion-api-generate-succeed',{"model":model})
|
||||
json_data = response.json()
|
||||
image_base64 = json_data['image']
|
||||
image_data = image2base64(image_base64)
|
||||
output_t = pil2tensor(image_data)
|
||||
return output_t
|
||||
else:
|
||||
if 'application/json' in response.headers['Content-Type']:
|
||||
error_info = response.json()
|
||||
log_node_error(node_name, error_info.get('name', 'No name provided'))
|
||||
log_node_error(node_name, error_info.get('errors', ['No details provided']))
|
||||
error_status_text = self.getErrors(response.status_code)
|
||||
PromptServer.instance.send_sync('easyuse-toast',{"type": "error", "content": error_status_text})
|
||||
raise Exception(f"Failed to generate image: {error_status_text}")
|
||||
|
||||
# get user account
|
||||
async def getUserAccount(self, cache=True):
|
||||
url = f"{self.api_url}/v1/user/account"
|
||||
api_key = self.api_keys[self.api_current]['key']
|
||||
name = self.api_keys[self.api_current]['name']
|
||||
if cache and name in self.user_info:
|
||||
return self.user_info[name]
|
||||
else:
|
||||
response = requests.get(url, headers={"Authorization": f"Bearer {api_key}"})
|
||||
if response.status_code == 200:
|
||||
user_info = response.json()
|
||||
self.user_info[name] = user_info
|
||||
return user_info
|
||||
else:
|
||||
PromptServer.instance.send_sync('easyuse-toast',{'type': 'error', 'content': self.getErrors(response.status_code)})
|
||||
return None
|
||||
|
||||
# get user balance
|
||||
async def getUserBalance(self):
|
||||
url = f"{self.api_url}/v1/user/balance"
|
||||
api_key = self.api_keys[self.api_current]['key']
|
||||
response = requests.get(url, headers={
|
||||
"Authorization": f"Bearer {api_key}"
|
||||
})
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
else:
|
||||
PromptServer.instance.send_sync('easyuse-toast', {'type': 'error', 'content': self.getErrors(response.status_code)})
|
||||
return None
|
||||
|
||||
stableAPI = StabilityAPI()
|
||||
|
||||
@PromptServer.instance.routes.get("/easyuse/stability/api_keys")
|
||||
async def get_stability_api_keys(request):
|
||||
stableAPI.getAPIKeys()
|
||||
return web.json_response({"keys": stableAPI.api_keys, "current": stableAPI.api_current})
|
||||
|
||||
@PromptServer.instance.routes.post("/easyuse/stability/set_api_keys")
|
||||
async def set_stability_api_keys(request):
|
||||
post = await request.post()
|
||||
api_keys = post.get("api_keys")
|
||||
current = post.get('current')
|
||||
if api_keys is not None:
|
||||
api_keys = json.loads(api_keys)
|
||||
stableAPI.setAPIKeys(api_keys)
|
||||
if current is not None:
|
||||
print(current)
|
||||
stableAPI.setAPIDefault(int(current))
|
||||
account = await stableAPI.getUserAccount()
|
||||
balance = await stableAPI.getUserBalance()
|
||||
return web.json_response({'account': account, 'balance': balance})
|
||||
else:
|
||||
return web.json_response({'status': 'ok'})
|
||||
else:
|
||||
return web.Response(status=400)
|
||||
|
||||
@PromptServer.instance.routes.post("/easyuse/stability/set_apikey_default")
|
||||
async def set_stability_api_default(request):
|
||||
post = await request.post()
|
||||
current = post.get("current")
|
||||
if current is not None and current < len(stableAPI.api_keys):
|
||||
stableAPI.api_current = current
|
||||
return web.json_response({'status': 'ok'})
|
||||
else:
|
||||
return web.Response(status=400)
|
||||
|
||||
@PromptServer.instance.routes.get("/easyuse/stability/user_info")
|
||||
async def get_account_info(request):
|
||||
account = await stableAPI.getUserAccount()
|
||||
balance = await stableAPI.getUserBalance()
|
||||
return web.json_response({'account': account, 'balance': balance})
|
||||
|
||||
@PromptServer.instance.routes.get("/easyuse/stability/balance")
|
||||
async def get_balance_info(request):
|
||||
balance = await stableAPI.getUserBalance()
|
||||
return web.json_response({'balance': balance})
|
||||
Reference in New Issue
Block a user