add preset node
This commit is contained in:
@@ -207,3 +207,4 @@ marimo/_lsp/
|
||||
__marimo__/
|
||||
config.ini
|
||||
config.json
|
||||
.vscode/
|
||||
@@ -1,4 +1,5 @@
|
||||
from .gemini_image_node import *
|
||||
from .gemini_image_preset_node import *
|
||||
from typing_extensions import override
|
||||
|
||||
class APIExtension(ComfyExtension):
|
||||
@@ -6,6 +7,7 @@ class APIExtension(ComfyExtension):
|
||||
async def get_node_list(self) -> list[type[io.ComfyNode]]:
|
||||
return [
|
||||
GeminiImage,
|
||||
GeminiImagePreset
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -334,5 +334,3 @@ from server import PromptServer
|
||||
@PromptServer.instance.routes.get("/hello")
|
||||
async def get_hello(request):
|
||||
return web.json_response("hello")
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
[
|
||||
{
|
||||
"title": "👨💼3D手办",
|
||||
"description": "将你的照片变成可收藏的3D角色手办,配有完整的包装。",
|
||||
"prompt": "turn this photo into a character figure. Behind it, place a box with the character’s image printed on it, and a computer showing the Blender modeling process on its screen. In front of the box, add a round plastic base with the character figure standing on it. set the scene indoors if possible"
|
||||
},
|
||||
{
|
||||
"title": "🧶钩针娃娃",
|
||||
"description": "将您的图像变成一个柔软的手工钩针娃娃。",
|
||||
"prompt": "Transform the subject into a handmade crocheted yarn doll with a cute, chibi-style appearance."
|
||||
},
|
||||
{
|
||||
"title": "💃姿势参考",
|
||||
"description": "将一张图像中的姿势应用到另一张图像中的角色上。",
|
||||
"prompt": "Apply the pose from the second image to the character in the first image. Render as a professional studio photograph."
|
||||
},
|
||||
{
|
||||
"title": "🏞️光影参考",
|
||||
"description": "将一张图像中的光影应用到另一张图像上。",
|
||||
"prompt": "图一换成图二打光,专业摄影"
|
||||
},
|
||||
{
|
||||
"title": "📸时尚杂志",
|
||||
"description": "为您的照片赋予高级时尚、编辑风格的外观,堪比杂志封面。",
|
||||
"prompt": "Transform the photo into a stylized, ultra-realistic fashion magazine portrait with cinematic lighting."
|
||||
},
|
||||
{
|
||||
"title": "💡产品渲染",
|
||||
"description": "将产品草图变成专业的、照片级的3D渲染图。",
|
||||
"prompt": "Turn this product sketch into a photorealistic 3D render with studio lighting."
|
||||
},
|
||||
{
|
||||
"title": "🗿黏土动画",
|
||||
"description": "将您的图像重现为一个迷人的定格黏土场景。",
|
||||
"prompt": "Recreate the image as a charming stop-motion claymation scene."
|
||||
},
|
||||
{
|
||||
"title": "🔶低多边形",
|
||||
"description": "使用低多边形几何网格重建您的图像。",
|
||||
"prompt": "Reconstruct the image using a low-polygon geometric mesh."
|
||||
},
|
||||
{
|
||||
"title": "⚙️蒸汽朋克",
|
||||
"description": "用齿轮、黄铜和维多利亚时代的技术重新想象您的主题。",
|
||||
"prompt": "Reimagine the subject with steampunk aesthetics, featuring gears, brass, and Victorian-era technology."
|
||||
},
|
||||
{
|
||||
"title": "🌌线稿艺术",
|
||||
"description": "将您的照片简化为其基本线条,创建一个干净的草图。",
|
||||
"prompt": "Turn the image into a clean, hand-drawn line art sketch."
|
||||
},
|
||||
{
|
||||
"title": "👾像素艺术",
|
||||
"description": "将您的图像转换为复古的8位像素艺术。",
|
||||
"prompt": "Redraw the image in a retro 8-bit pixel art style."
|
||||
},
|
||||
{
|
||||
"title": "🖌️水彩艺术",
|
||||
"description": "将您的图像转换为柔和、充满活力的水彩画。",
|
||||
"prompt": "Transform the image into a soft and vibrant watercolor painting."
|
||||
},
|
||||
{
|
||||
"title": "🎨波普艺术",
|
||||
"description": "以安迪·沃霍尔的大胆风格重新想象您的图像。",
|
||||
"prompt": "Reimagine the image in the style of Andy Warhol's pop art, with bold colors and screen-print effects."
|
||||
},
|
||||
{
|
||||
"title": "✏️涂鸦艺术",
|
||||
"description": "在您的图像上覆盖好玩的手绘涂鸦。",
|
||||
"prompt": "Overlay the image with playful, hand-drawn doodle-style illustrations."
|
||||
},
|
||||
{
|
||||
"title": "🐉奇幻艺术",
|
||||
"description": "将您的图像转变为一幅史诗般的奇幻风格绘画。",
|
||||
"prompt": "Transform the image into an epic fantasy-style painting, with magical elements and dramatic lighting."
|
||||
},
|
||||
{
|
||||
"title": "🖼️矢量艺术",
|
||||
"description": "将您的照片转换为干净、可缩放的矢量艺术。",
|
||||
"prompt": "Convert the photo into clean, scalable vector art with flat colors and sharp lines."
|
||||
},
|
||||
{
|
||||
"title": "✂️折纸艺术",
|
||||
"description": "用折纸风格重建您的主题。",
|
||||
"prompt": "Reconstruct the subject of the image using folded paper in an origami style."
|
||||
},
|
||||
{
|
||||
"title": "🏜️沙画艺术",
|
||||
"description": "重新创作您的图像,仿佛它是由彩色沙子制成的。",
|
||||
"prompt": "Recreate the image as if it were made from colored sand."
|
||||
},
|
||||
{
|
||||
"title": "👨🔧乐高小人仔",
|
||||
"description": "为你的主体制作一个乐高小人仔,准备好玩耍。",
|
||||
"prompt": "Transform the person into a LEGO minifigure, inside its packaging box."
|
||||
},
|
||||
{
|
||||
"title": "🥤汽水罐设计",
|
||||
"description": "将您的图像包装到汽水罐上,并将其放置在精美的产品照片中。",
|
||||
"prompt": "Design a soda can using this image as the main graphic, and show it in a professional product shot."
|
||||
},
|
||||
{
|
||||
"title": "🧸可爱毛绒玩具",
|
||||
"description": "将您的主题转换成一个可爱的、柔软的毛绒玩具。",
|
||||
"prompt": "Turn the person in this photo into a cute, soft plushie doll."
|
||||
},
|
||||
{
|
||||
"title": "🔑亚克力钥匙扣",
|
||||
"description": "创建一个您的主题的可爱亚克力钥匙扣,非常适合挂在包上。",
|
||||
"prompt": "Turn the subject into a cute acrylic keychain, shown attached to a bag."
|
||||
},
|
||||
{
|
||||
"title": "🎭动漫转Cosplay",
|
||||
"description": "将动漫角色变为一张逼真的Cosplay照片。",
|
||||
"prompt": "Generate a highly detailed, realistic photo of a person cosplaying the character in this illustration. Replicate the pose, expression, and framing."
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,116 @@
|
||||
import io
|
||||
import os
|
||||
import json
|
||||
import base64
|
||||
import requests
|
||||
import torch
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
from io import BytesIO
|
||||
from typing_extensions import override
|
||||
from comfy_api.latest import ComfyExtension, io
|
||||
from .image_utils import tensor_to_base64_string
|
||||
|
||||
# 公共函数,用于处理预设数据
|
||||
def load_preset_data():
|
||||
"""加载预设数据从JSON配置文件"""
|
||||
try:
|
||||
config_path = os.path.join(os.path.dirname(__file__), "gemini_image_preset.json")
|
||||
with open(config_path, 'r', encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
except Exception as e:
|
||||
print(f"Error loading preset config: {e}")
|
||||
return []
|
||||
|
||||
def get_preset_titles():
|
||||
"""获取所有预设的标题列表"""
|
||||
preset_data = load_preset_data()
|
||||
if preset_data:
|
||||
return [preset['title'] for preset in preset_data]
|
||||
return ["None"]
|
||||
|
||||
def get_preset_by_title(title):
|
||||
"""根据title获取对应的预设数据"""
|
||||
preset_data = load_preset_data()
|
||||
for preset in preset_data:
|
||||
if preset.get('title') == title:
|
||||
return preset
|
||||
return None
|
||||
|
||||
class GeminiImagePreset(io.ComfyNode):
|
||||
"""
|
||||
这个节点为Gemini Image API 提供预设
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def define_schema(cls) -> io.Schema:
|
||||
"""
|
||||
返回一个包含该节点所有信息的模式(schema)。
|
||||
一些可用类型:"Model", "Vae", "Clip", "Conditioning", "Latent", "Image", "Int", "String", "Float", "Combo"。
|
||||
对于输出,应使用 "io.Model.Output",对于输入,可以使用 "io.Model.Input"。
|
||||
类型可以是 "Combo" —— 这将是一个供选择的列表。
|
||||
"""
|
||||
# 从配置文件加载预设
|
||||
preset_options = get_preset_titles()
|
||||
|
||||
return io.Schema(
|
||||
node_id="YCYY_Gemini_Image_Preset",
|
||||
display_name="Gemini Image Preset",
|
||||
category="YCYY/API/utils",
|
||||
inputs=[
|
||||
io.Combo.Input(
|
||||
id="preset",
|
||||
options=preset_options,
|
||||
default=preset_options[0] if preset_options and preset_options[0] != "None" else "None",
|
||||
tooltip="Gemini image preset name"
|
||||
),
|
||||
io.String.Input(
|
||||
id="description",
|
||||
multiline=True,
|
||||
tooltip="Gemini image preset description"
|
||||
),
|
||||
io.String.Input(
|
||||
id="prompt",
|
||||
multiline=True,
|
||||
tooltip="Gemini image preset prompt"
|
||||
),
|
||||
],
|
||||
outputs=[
|
||||
io.String.Output()
|
||||
],
|
||||
description="This node provides presets for the Gemini Image API."
|
||||
)
|
||||
# 执行 GeminiImagePreset 节点
|
||||
@classmethod
|
||||
def execute(cls, preset, description, prompt) -> io.NodeOutput:
|
||||
return (prompt,)
|
||||
|
||||
WEB_DIRECTORY = "./web/js"
|
||||
|
||||
# 使用 router 添加自定义 API 路由
|
||||
from aiohttp import web
|
||||
from server import PromptServer
|
||||
|
||||
|
||||
|
||||
|
||||
@PromptServer.instance.routes.post("/ycyy/gemini/images/preset")
|
||||
async def get_preset_data(request):
|
||||
"""获取preset数据的API端点"""
|
||||
try:
|
||||
data = await request.json()
|
||||
preset_title = data.get('title')
|
||||
|
||||
if not preset_title:
|
||||
return web.json_response({"error": "Missing preset title"}, status=400)
|
||||
|
||||
preset_data = get_preset_by_title(preset_title)
|
||||
|
||||
if preset_data:
|
||||
return web.json_response(preset_data)
|
||||
else:
|
||||
return web.json_response({"error": f"Preset '{preset_title}' not found"}, status=404)
|
||||
|
||||
except Exception as e:
|
||||
return web.json_response({"error": f"Internal server error: {str(e)}"}, status=500)
|
||||
|
||||
@@ -29,5 +29,29 @@
|
||||
"name": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"YCYY_Gemini_Image_Preset":{
|
||||
"display_name": "Gemini Image Preset",
|
||||
"description": "This node provides presets for the Gemini Image API.",
|
||||
"inputs": {
|
||||
"preset":{
|
||||
"name": "preset",
|
||||
"tooltip": "Gemini image preset name"
|
||||
},
|
||||
"description":{
|
||||
"name": "description",
|
||||
"tooltip": "Gemini image preset description"
|
||||
},
|
||||
"prompt":{
|
||||
"name": "prompt",
|
||||
"tooltip": "Gemini image preset prompt"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"name": "String"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"YCYY_Gemini_Image_API": {
|
||||
"display_name": "Gemini 图像 API",
|
||||
"description": "该节点使用 Google Gemini 图像 API 生成或修改图像",
|
||||
"inputs": {
|
||||
"images": {
|
||||
"name": "图像",
|
||||
@@ -29,5 +30,29 @@
|
||||
"name": "字符串"
|
||||
}
|
||||
}
|
||||
},
|
||||
"YCYY_Gemini_Image_Preset":{
|
||||
"display_name": "Gemini 图像预设",
|
||||
"description": "该节点为 Gemini 图像 API 提供预设值",
|
||||
"inputs": {
|
||||
"preset":{
|
||||
"name": "预设名称",
|
||||
"tooltip": "预设名称"
|
||||
},
|
||||
"description":{
|
||||
"name": "预设描述",
|
||||
"tooltip": "预设描述"
|
||||
},
|
||||
"prompt":{
|
||||
"name": "预设提示词",
|
||||
"tooltip": "预设提示词"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"name": "字符串"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
import { api } from "../../scripts/api.js";
|
||||
|
||||
app.registerExtension({
|
||||
name: "YCYY.Gemini.Image.Preset",
|
||||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||
if (nodeType.comfyClass === "YCYY_Gemini_Image_Preset") {
|
||||
const onNodeCreated = nodeType.prototype.onNodeCreated;
|
||||
nodeType.prototype.onNodeCreated = function () {
|
||||
const r = onNodeCreated?.apply(this, arguments);
|
||||
|
||||
const presetWidget = this.widgets?.find(w => w.name === "preset");
|
||||
const descriptionWidget = this.widgets?.find(w => w.name === "description");
|
||||
const promptWidget = this.widgets?.find(w => w.name === "prompt");
|
||||
|
||||
if (presetWidget && descriptionWidget && promptWidget) {
|
||||
const originalCallback = presetWidget.callback;
|
||||
|
||||
presetWidget.callback = async (value) => {
|
||||
originalCallback?.call(this, value);
|
||||
|
||||
if (!value || value === "None") return;
|
||||
|
||||
try {
|
||||
const response = await api.fetchApi("/ycyy/gemini/images/preset", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ title: value })
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const presetData = await response.json();
|
||||
if (presetData?.description && presetData?.prompt) {
|
||||
descriptionWidget.value = presetData.description;
|
||||
promptWidget.value = presetData.prompt;
|
||||
this.graph?.canvas?.draw(true, true);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching preset data:", error);
|
||||
}
|
||||
};
|
||||
|
||||
if (presetWidget.value && presetWidget.value !== "None") {
|
||||
setTimeout(() => presetWidget.callback(presetWidget.value), 50);
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user