Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
686ebcfd8b | ||
|
|
51aaba39cf | ||
|
|
d7c6632499 | ||
|
|
1b0ea06876 | ||
|
|
8ebe88629b | ||
|
|
e226992703 | ||
|
|
11a8394d69 | ||
|
|
9f54a1b91a | ||
|
|
35d11061e9 | ||
|
|
65d8b490ca | ||
|
|
0fef12c3b1 | ||
|
|
8516bff224 | ||
|
|
1bcc501352 | ||
|
|
f492b17fbe | ||
|
|
48ae90f80e | ||
|
|
9321ccbc48 | ||
|
|
402cd01e1a | ||
|
|
7b2d0e29c6 | ||
|
|
52d38c401a | ||
|
|
a34dd61076 | ||
|
|
a53a3e772a | ||
|
|
8f24c294a7 | ||
|
|
acc3f76654 | ||
|
|
8c977fb442 | ||
|
|
aa20a2de67 | ||
|
|
5fcb154d89 | ||
|
|
0980129f4e |
@@ -1,6 +1,6 @@
|
||||
> 适配了最新版comfyui的py3.11 ,torch 2.1.2+cu121
|
||||
|
||||
> [discord](https://discord.gg/cXs9vZSqeK)
|
||||
> [Mixlab nodes discord](https://discord.gg/cXs9vZSqeK)
|
||||
|
||||
####
|
||||
[comfyui-ultralytics-yolo](https://github.com/shadowcz007/comfyui-ultralytics-yolo)
|
||||
@@ -45,7 +45,12 @@ APP-JSON:
|
||||
|
||||
> seed统一输入控件,支持:SamplerCustom、KSampler
|
||||
|
||||
> [ps插件](https://github.com/shadowcz007/comfyui-ps-plugin)
|
||||
> 配套[ps插件](https://github.com/shadowcz007/comfyui-ps-plugin)
|
||||
|
||||
> 如果遇到上传图片不成功,请检查下:局域网或者是云服务,请使用https,端口8189这个服务( 感谢 @Damien 反馈问题)
|
||||
|
||||
> If you encounter difficulties in uploading images, please check the following: for local network or cloud services, please use HTTPS and the service on port 8189. (Thanks to @Damien for reporting the issue.)
|
||||
|
||||
|
||||
|
||||
## 🏃🚗🚚🚀 Real-time Design
|
||||
@@ -68,7 +73,7 @@ https://github.com/shadowcz007/comfyui-mixlab-nodes/assets/12645064/e7e77f90-e43
|
||||
[Voice + Real-time Face Swap Workflow](./workflow/语音+实时换脸workflow.json)
|
||||
|
||||
### GPT
|
||||
> Support for calling multiple GPTs.ChatGPT、ChatGLM3 , Some code provided by rui. If you are using OpenAI's service, fill in https://api.openai.com/v1 . If you are using a local LLM service, fill in http://127.0.0.1:xxxx/v1 . Azure OpenAI:https://xxxx.openai.azure.com
|
||||
> Support for calling multiple GPTs.ChatGPT、ChatGLM3 、ChatGLM4 , Some code provided by rui. If you are using OpenAI's service, fill in https://api.openai.com/v1 . If you are using a local LLM service, fill in http://127.0.0.1:xxxx/v1 . Azure OpenAI:https://xxxx.openai.azure.com
|
||||
|
||||

|
||||
|
||||
@@ -163,6 +168,7 @@ from [simple-lama-inpainting](https://github.com/enesmsahin/simple-lama-inpainti
|
||||
|
||||
"briarmbg","u2net","u2netp","u2net_human_seg","u2net_cloth_seg","silueta","isnet-general-use","isnet-anime"
|
||||
|
||||
*** briarmbg *** model was developed by BRlA Al and can be used as an open-source model for non-commercial purposes
|
||||
|
||||
|
||||
### Improvement
|
||||
@@ -226,6 +232,14 @@ pip3 install -r requirements.txt
|
||||
访问 [www.mixcomfy.com](https://www.mixcomfy.com),获得更多内测功能,关注微信公众号:Mixlab无界社区
|
||||
|
||||
|
||||
|
||||
|
||||
####
|
||||
File / LoadImagesFromPath SaveImageToLocal LoadImagesFromURL
|
||||
|
||||
|
||||
|
||||
|
||||
#### discussions:
|
||||
[discussions](https://github.com/shadowcz007/comfyui-mixlab-nodes/discussions)
|
||||
|
||||
|
||||
+3
-1
@@ -535,7 +535,7 @@ from .nodes.ImageNode import SaveImageToLocal,SplitImage,GridOutput,GetImageSize
|
||||
# from .nodes.Vae import VAELoader,VAEDecode
|
||||
from .nodes.ScreenShareNode import ScreenShareNode,FloatingVideo
|
||||
|
||||
from .nodes.ChatGPT import ChatGPTNode,ShowTextForGPT,CharacterInText
|
||||
from .nodes.ChatGPT import ChatGPTNode,ShowTextForGPT,CharacterInText,TextSplitByDelimiter
|
||||
from .nodes.Audio import GamePal,SpeechRecognition,SpeechSynthesis
|
||||
from .nodes.Utils import CreateLoraNames,CreateSampler_names,CreateCkptNames,CreateSeedNode,TESTNODE_,TESTNODE_TOKEN,AppInfo,IntNumber,FloatSlider,TextInput,ColorInput,FontInput,TextToNumber,DynamicDelayProcessor,LimitNumber,SwitchByIndex,MultiplicationNode
|
||||
from .nodes.Mask import OutlineMask,FeatheredMask
|
||||
@@ -548,6 +548,7 @@ NODE_CLASS_MAPPINGS = {
|
||||
"TESTNODE_":TESTNODE_,
|
||||
"TESTNODE_TOKEN":TESTNODE_TOKEN,
|
||||
"RandomPrompt":RandomPrompt,
|
||||
# "LoraPrompt":LoraPrompt,
|
||||
"EmbeddingPrompt":EmbeddingPrompt,
|
||||
"PromptSlide":PromptSlide,
|
||||
"PromptSimplification":PromptSimplification,
|
||||
@@ -584,6 +585,7 @@ NODE_CLASS_MAPPINGS = {
|
||||
"ChatGPTOpenAI":ChatGPTNode,
|
||||
"ShowTextForGPT":ShowTextForGPT,
|
||||
"CharacterInText":CharacterInText,
|
||||
"TextSplitByDelimiter":TextSplitByDelimiter,
|
||||
"SpeechRecognition":SpeechRecognition,
|
||||
"SpeechSynthesis":SpeechSynthesis,
|
||||
"Color":ColorInput,
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{}
|
||||
+63
-4
@@ -4,7 +4,7 @@ import urllib.error
|
||||
import re,json,os,string,random
|
||||
import folder_paths
|
||||
import hashlib
|
||||
|
||||
from zhipuai import ZhipuAI
|
||||
def get_unique_hash(string):
|
||||
hash_object = hashlib.sha1(string.encode())
|
||||
unique_hash = hash_object.hexdigest()
|
||||
@@ -46,6 +46,11 @@ def openai_client(key,url):
|
||||
base_url=url
|
||||
)
|
||||
return client
|
||||
def ZhipuAI_client(key):
|
||||
client = ZhipuAI(
|
||||
api_key=key, # 填写您的 APIKey
|
||||
)
|
||||
return client
|
||||
|
||||
|
||||
|
||||
@@ -100,7 +105,7 @@ class ChatGPTNode:
|
||||
"default": "You are ChatGPT, a large language model trained by OpenAI. Answer as concisely as possible.",
|
||||
"multiline": True,"dynamicPrompts": False
|
||||
}),
|
||||
"model": (["gpt-3.5-turbo","gpt-35-turbo","gpt-3.5-turbo-16k", "gpt-3.5-turbo-16k-0613", "gpt-4-0613","gpt-4-1106-preview"],
|
||||
"model": (["gpt-3.5-turbo","gpt-35-turbo","gpt-3.5-turbo-16k", "gpt-3.5-turbo-16k-0613", "gpt-4-0613","gpt-4-1106-preview","glm-4"],
|
||||
{"default": "gpt-3.5-turbo"}),
|
||||
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "step": 1}),
|
||||
"context_size":("INT", {"default": 1, "min": 0, "max":30, "step": 1}),
|
||||
@@ -143,8 +148,13 @@ class ChatGPTNode:
|
||||
if is_azure_url(api_url):
|
||||
client=azure_client(api_key,api_url)
|
||||
else:
|
||||
client=openai_client(api_key,api_url)
|
||||
print('openai url')
|
||||
# 根据用户选择的模型,设置相应的接口和模型名称
|
||||
if model == "glm-4" :
|
||||
client = ZhipuAI_client(api_key) # 使用 Zhipuai 的接口
|
||||
print('using Zhipuai interface')
|
||||
else :
|
||||
client = openai_client(api_key,api_url) # 使用 ChatGPT 的接口
|
||||
print('using ChatGPT interface')
|
||||
|
||||
# 把用户的提示添加到会话历史中
|
||||
# 调用API时传递整个会话历史
|
||||
@@ -291,3 +301,52 @@ class CharacterInText:
|
||||
|
||||
return (b+start_index,)
|
||||
|
||||
class TextSplitByDelimiter:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"text": ("STRING", {"multiline": True,"dynamicPrompts": False}),
|
||||
"delimiter":(["newline","comma"],),
|
||||
"start_index": ("INT", {
|
||||
"default": 0,
|
||||
"min": 0, #Minimum value
|
||||
"max": 1000, #Maximum value
|
||||
"step": 1, #Slider's step
|
||||
"display": "number" # Cosmetic only: display as "number" or "slider"
|
||||
}),
|
||||
"skip_every": ("INT", {
|
||||
"default": 0,
|
||||
"min": 0, #Minimum value
|
||||
"max": 10, #Maximum value
|
||||
"step": 1, #Slider's step
|
||||
"display": "number" # Cosmetic only: display as "number" or "slider"
|
||||
}),
|
||||
"max_count": ("INT", {
|
||||
"default": 10,
|
||||
"min": 1, #Minimum value
|
||||
"max": 1000, #Maximum value
|
||||
"step": 1, #Slider's step
|
||||
"display": "number" # Cosmetic only: display as "number" or "slider"
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
INPUT_IS_LIST = False
|
||||
RETURN_TYPES = ("STRING",)
|
||||
FUNCTION = "run"
|
||||
# OUTPUT_NODE = True
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
|
||||
CATEGORY = "♾️Mixlab/GPT"
|
||||
|
||||
def run(self, text,delimiter,start_index,skip_every,max_count):
|
||||
arr=[]
|
||||
if delimiter=='newline':
|
||||
arr = [line for line in text.split('\n') if line.strip()]
|
||||
elif delimiter=='comma':
|
||||
arr = [line for line in text.split(',') if line.strip()]
|
||||
|
||||
arr= arr[start_index:start_index + max_count * (skip_every+1):(skip_every+1)]
|
||||
|
||||
return (arr,)
|
||||
|
||||
+1
-1
@@ -2408,7 +2408,7 @@ class SaveImageToLocal:
|
||||
full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(filename_prefix, self.output_dir, images[0].shape[1], images[0].shape[0])
|
||||
|
||||
|
||||
if not os.path.exists(file_path) and not extension:
|
||||
if not os.path.exists(file_path):
|
||||
# 使用os.makedirs函数创建新目录
|
||||
os.makedirs(file_path)
|
||||
print("目录已创建")
|
||||
|
||||
+130
-1
@@ -6,6 +6,12 @@ from urllib import request, parse
|
||||
import folder_paths
|
||||
from PIL import Image, ImageOps,ImageFilter,ImageEnhance,ImageDraw,ImageSequence, ImageFont
|
||||
from PIL.PngImagePlugin import PngInfo
|
||||
|
||||
import hashlib
|
||||
import requests
|
||||
import json
|
||||
|
||||
|
||||
# def queue_prompt(prompt_workflow):
|
||||
# p = {"prompt": prompt_workflow}
|
||||
# data = json.dumps(p).encode('utf-8')
|
||||
@@ -28,6 +34,50 @@ def join_with_(text_list,delimiter):
|
||||
return joined_text
|
||||
|
||||
|
||||
|
||||
def load_json(file_path):
|
||||
try:
|
||||
with open(file_path, 'r') as json_file:
|
||||
data = json.load(json_file)
|
||||
return data
|
||||
except FileNotFoundError:
|
||||
print(f"File not found: {file_path}")
|
||||
return None
|
||||
except json.JSONDecodeError:
|
||||
print(f"Error decoding JSON in file: {file_path}")
|
||||
return None
|
||||
|
||||
def save_json(data_dict, file_path):
|
||||
try:
|
||||
with open(file_path, 'w') as json_file:
|
||||
json.dump(data_dict, json_file, indent=4)
|
||||
print(f"Data saved to {file_path}")
|
||||
except Exception as e:
|
||||
print(f"Error saving JSON to file: {e}")
|
||||
|
||||
# pysss的lora加载器
|
||||
# def get_model_version_info(hash_value):
|
||||
# # http://127.0.0.1:1082
|
||||
# proxies = {'http': 'http://127.0.0.1:1082', 'https': 'https://127.0.0.1:1082'}
|
||||
# api_url = f"https://civitai.com/api/v1/model-versions/by-hash/{hash_value}"
|
||||
# print(api_url)
|
||||
# response = requests.get(api_url,proxies=proxies, verify=False)
|
||||
|
||||
# if response.status_code == 200:
|
||||
# return response.json()
|
||||
# else:
|
||||
# return None
|
||||
|
||||
# def calculate_sha256(file_path):
|
||||
# sha256_hash = hashlib.sha256()
|
||||
# with open(file_path, "rb") as f:
|
||||
# for chunk in iter(lambda: f.read(4096), b""):
|
||||
# sha256_hash.update(chunk)
|
||||
# return sha256_hash.hexdigest()
|
||||
|
||||
|
||||
|
||||
|
||||
class AnyType(str):
|
||||
"""A special class that is always equal in not equal comparisons. Credit to pythongosssss"""
|
||||
|
||||
@@ -385,7 +435,82 @@ class RandomPrompt:
|
||||
return {"ui": {"prompts": prompts}, "result": (prompts,)}
|
||||
|
||||
|
||||
# class LoraPrompt:
|
||||
# @classmethod
|
||||
# def INPUT_TYPES(s):
|
||||
# return {
|
||||
# "required": {
|
||||
# "lora_name":(sorted(folder_paths.get_filename_list("loras"), key=str.lower),),
|
||||
# "weight": ("FLOAT", {"default": 1, "min": -2, "max": 2,"step":0.01 ,"display": "slider"}),
|
||||
# "force_update": ("BOOLEAN", {"default": False}),
|
||||
# },
|
||||
|
||||
# }
|
||||
|
||||
# RETURN_TYPES = ("STRING","STRING",any_type)
|
||||
# RETURN_NAMES = ("lora_name","prompt","tags",)
|
||||
|
||||
# FUNCTION = "run"
|
||||
|
||||
# CATEGORY = "♾️Mixlab/Prompt"
|
||||
|
||||
# OUTPUT_IS_LIST = (False,False,True,)
|
||||
# # OUTPUT_NODE = True
|
||||
|
||||
# # 运行的函数
|
||||
# def run(self,lora_name,weight,force_update=False):
|
||||
|
||||
# # print('##LoraPrompt',__file__)
|
||||
# # 从本地数据库读取
|
||||
# json_tags_path = os.path.join(os.path.dirname(os.path.dirname(__file__)),r'data/loras_tags.json')
|
||||
|
||||
# if not os.path.exists(json_tags_path):
|
||||
# save_json({},json_tags_path)
|
||||
|
||||
# lora_tags = load_json(json_tags_path)
|
||||
# output_tags = lora_tags.get(lora_name, None) if lora_tags is not None else None
|
||||
# if output_tags is not None:
|
||||
# output_tags = ",".join(output_tags)
|
||||
# print("trainedWords:",output_tags)
|
||||
# else:
|
||||
# output_tags = ""
|
||||
|
||||
|
||||
# lora_path = folder_paths.get_full_path("loras", lora_name)
|
||||
# if output_tags == "" or force_update:
|
||||
# print("calculating lora hash")
|
||||
# LORAsha256 = calculate_sha256(lora_path)
|
||||
# print("requesting infos")
|
||||
# model_info = get_model_version_info(LORAsha256)
|
||||
# if model_info is not None:
|
||||
# if "trainedWords" in model_info:
|
||||
# print("tags found!")
|
||||
# if lora_tags is None:
|
||||
# lora_tags = {}
|
||||
# lora_tags[lora_name] = model_info["trainedWords"]
|
||||
# save_json(lora_tags,json_tags_path)
|
||||
# output_tags = ",".join(model_info["trainedWords"])
|
||||
# print("trainedWords:",output_tags)
|
||||
# else:
|
||||
# print("No informations found.")
|
||||
# if lora_tags is None:
|
||||
# lora_tags = {}
|
||||
# lora_tags[lora_name] = []
|
||||
# save_json(lora_tags,json_tags_path)
|
||||
|
||||
|
||||
# weight = round(weight, 3)
|
||||
# prompt=[]
|
||||
# for p in output_tags.split(','):
|
||||
|
||||
# if weight!=1:
|
||||
# prompt.append('('+p+':'+str(weight)+')')
|
||||
# else:
|
||||
# prompt.append(p)
|
||||
|
||||
# prompt=",".join(prompt)
|
||||
|
||||
# return (lora_name,prompt,output_tags.split(','),)
|
||||
|
||||
|
||||
|
||||
@@ -426,7 +551,7 @@ class JoinWithDelimiter:
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": {
|
||||
"text_list": (any_type,),
|
||||
"delimiter":(["newline","comma"],),
|
||||
"delimiter":(["newline","comma","backslash","space"],),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -445,6 +570,10 @@ class JoinWithDelimiter:
|
||||
delimiter='\n'
|
||||
elif delimiter=='comma':
|
||||
delimiter=','
|
||||
elif delimiter=='backslash':
|
||||
delimiter='\\'
|
||||
elif delimiter=='space':
|
||||
delimiter=' '
|
||||
t=''
|
||||
if isinstance(text_list, list):
|
||||
t=join_with_(text_list,delimiter)
|
||||
|
||||
+28
-12
@@ -6,7 +6,7 @@ import numpy as np
|
||||
import folder_paths
|
||||
import matplotlib.font_manager as fm
|
||||
import torch
|
||||
|
||||
import importlib.util
|
||||
|
||||
|
||||
def recursive_search(directory, excluded_dir_names=None):
|
||||
@@ -347,7 +347,7 @@ class MultiplicationNode:
|
||||
return {"required": {
|
||||
"numberA":(any_type,),
|
||||
"multiply_by":("FLOAT", {
|
||||
"default": 0,
|
||||
"default": 1,
|
||||
"min": -2, #Minimum value
|
||||
"max": 0xffffffffffffffff,
|
||||
"step": 0.01, #Slider's step
|
||||
@@ -560,9 +560,11 @@ class SwitchByIndex:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"A":(any_type,),
|
||||
"B":(any_type,),
|
||||
"optional":{
|
||||
"A":(any_type,),
|
||||
"B":(any_type,),
|
||||
},
|
||||
"required": {
|
||||
"index":("INT", {
|
||||
"default": -1,
|
||||
"min": -1,
|
||||
@@ -574,17 +576,17 @@ class SwitchByIndex:
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = (any_type,)
|
||||
RETURN_NAMES = ("C",)
|
||||
RETURN_TYPES = (any_type,"INT",)
|
||||
RETURN_NAMES = ("C","count",)
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/Utils"
|
||||
|
||||
INPUT_IS_LIST = True
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
OUTPUT_IS_LIST = (True,False,)
|
||||
|
||||
def run(self, A,B,index,flat):
|
||||
def run(self, A=[],B=[],index=-1,flat='on'):
|
||||
|
||||
flat=flat[0]
|
||||
|
||||
@@ -604,8 +606,8 @@ class SwitchByIndex:
|
||||
C=[C[index]]
|
||||
except Exception as e:
|
||||
C=[]
|
||||
|
||||
return (C,)
|
||||
|
||||
return (C,len(C),)
|
||||
|
||||
|
||||
|
||||
@@ -711,12 +713,26 @@ class TESTNODE_:
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
|
||||
def run(self,ANY):
|
||||
print(ANY)
|
||||
# print(ANY)
|
||||
# data=ANY
|
||||
list_stats = ListStatistics()
|
||||
|
||||
# 调用count_types方法进行统计
|
||||
result = list_stats.count_types(ANY)
|
||||
|
||||
|
||||
# 假设我们有一个模块文件名为 my_module.py,它位于 'importables' 目录下
|
||||
module_path = os.path.join(os.path.dirname(__file__),'test.py')
|
||||
|
||||
# 使用 spec_from_file_location 获取模块的元数据(名称、定义等)
|
||||
spec = importlib.util.spec_from_file_location('test', module_path)
|
||||
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
|
||||
functions = getattr(module, 'run') # 获取函数
|
||||
|
||||
functions(ANY)
|
||||
|
||||
|
||||
return {"ui": {"data": result,"type":[str(type(ANY[0]))]}, "result": (ANY,)}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import folder_paths
|
||||
|
||||
# 外挂一个文件,用来编写新的节点
|
||||
def run(v):
|
||||
|
||||
output_dir = folder_paths.get_temp_directory()
|
||||
|
||||
print('1323',v,output_dir)
|
||||
+2
-1
@@ -6,4 +6,5 @@ matplotlib
|
||||
openai
|
||||
simple-lama-inpainting
|
||||
clip-interrogator==0.6.0
|
||||
transformers>=4.36.0
|
||||
transformers>=4.36.0
|
||||
zhipuai
|
||||
|
||||
+104
-39
@@ -11,6 +11,12 @@
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.app {
|
||||
display: flex;
|
||||
width: 90%;
|
||||
@@ -142,6 +148,10 @@
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.description img {
|
||||
min-height: unset !important;
|
||||
}
|
||||
|
||||
.panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -172,6 +182,7 @@
|
||||
width: fit-content;
|
||||
max-width: 100%;
|
||||
margin-left: 12px;
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
.input_card {
|
||||
@@ -378,9 +389,7 @@
|
||||
|
||||
<body>
|
||||
<div id="editor_container"></div>
|
||||
<div style="display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;">
|
||||
<div class="header">
|
||||
<div style="margin: 0 24px;
|
||||
margin-bottom: 24px;
|
||||
padding: 8px;
|
||||
@@ -890,6 +899,7 @@
|
||||
|
||||
|
||||
function generateRainbowVideo() {
|
||||
|
||||
// 创建一个canvas元素
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = 640; // 设置canvas宽度
|
||||
@@ -908,30 +918,26 @@
|
||||
context.fillStyle = 'green';
|
||||
context.fillRect(canvas.width / 2, 0, canvas.width / 2, canvas.height);
|
||||
|
||||
// 创建一个新的video元素
|
||||
const video = document.createElement('video');
|
||||
video.setAttribute('controls', true)
|
||||
// 将canvas绘制到video中
|
||||
const stream = canvas.captureStream();
|
||||
video.srcObject = stream;
|
||||
|
||||
// 导出视频
|
||||
const mediaRecorder = new MediaRecorder(stream);
|
||||
const chunks = [];
|
||||
mediaRecorder.ondataavailable = function (event) {
|
||||
chunks.push(event.data);
|
||||
};
|
||||
mediaRecorder.onstop = function () {
|
||||
const blob = new Blob(chunks, { type: 'video/mp4' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
video.src = url;
|
||||
};
|
||||
mediaRecorder.start();
|
||||
setTimeout(function () {
|
||||
mediaRecorder.stop();
|
||||
}, 1000); // 设置录制时长,这里设置为1秒
|
||||
return new Promise((res, rej) => {
|
||||
// 导出视频
|
||||
const mediaRecorder = new MediaRecorder(stream);
|
||||
const chunks = [];
|
||||
mediaRecorder.ondataavailable = function (event) {
|
||||
chunks.push(event.data);
|
||||
};
|
||||
mediaRecorder.onstop = function () {
|
||||
const blob = new Blob(chunks, { type: 'video/mp4' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
res(url)
|
||||
};
|
||||
mediaRecorder.start();
|
||||
setTimeout(function () {
|
||||
mediaRecorder.stop();
|
||||
}, 1000); // 设置录制时长,这里设置为1秒
|
||||
})
|
||||
|
||||
return video;
|
||||
}
|
||||
|
||||
|
||||
@@ -1129,7 +1135,8 @@
|
||||
// console.log('inputData',inputData)
|
||||
inputData.forEach(data => {
|
||||
console.log(data)
|
||||
// Check if the class_type is "LoadImage"
|
||||
|
||||
// 图片 or 视频输入
|
||||
if (data.class_type === "LoadImage" || data.class_type === "VHS_LoadVideo") {
|
||||
|
||||
let isVideoUpload = data.class_type === "VHS_LoadVideo";
|
||||
@@ -1145,6 +1152,7 @@
|
||||
uploadContainer.appendChild(nameLabel);
|
||||
|
||||
let actionDiv = document.createElement('div');
|
||||
actionDiv.style = `padding: 0 8px;`
|
||||
|
||||
// Create an input field for the image name
|
||||
const uploadImageInput = document.createElement("button");
|
||||
@@ -1172,6 +1180,7 @@
|
||||
// Create an image element to display the uploaded image
|
||||
let imageElement = document.createElement("img");
|
||||
if (isVideoUpload) {
|
||||
// 视频
|
||||
imageElement = document.createElement('video');
|
||||
imageElement.setAttribute('controls', true)
|
||||
let [subfolder, name] = data.inputs.video.split('/');
|
||||
@@ -1182,6 +1191,9 @@
|
||||
}
|
||||
let url = `${get_url()}/view?filename=${encodeURIComponent(name)}&type=input&subfolder=${subfolder}&rand=${Math.random()}`
|
||||
imageElement.src = url;
|
||||
|
||||
// imageElement.innerHTML=`<img src="${base64Df}"/>`
|
||||
|
||||
} else {
|
||||
// 图片
|
||||
let [subfolder, name] = data.inputs.image.split('/');
|
||||
@@ -1192,6 +1204,9 @@
|
||||
// imageElement.src = base64Df
|
||||
let url = `${get_url()}/view?filename=${encodeURIComponent(name)}&type=input&subfolder=${subfolder}&rand=${Math.random()}`
|
||||
imageElement.src = url;
|
||||
imageElement.setAttribute('onerror',`this.src='${base64Df}'`)
|
||||
|
||||
|
||||
}
|
||||
|
||||
imageElement.style.maxWidth = '200px';
|
||||
@@ -1254,6 +1269,7 @@
|
||||
container.appendChild(uploadContainer);
|
||||
}
|
||||
|
||||
// 滑块输入
|
||||
if (["PromptSlide"].includes(data.class_type)) {
|
||||
// 滑块输入
|
||||
let options = data.options || {
|
||||
@@ -1283,6 +1299,7 @@
|
||||
}
|
||||
|
||||
|
||||
// 数字输入支持
|
||||
if (['FloatSlider', 'IntNumber'].includes(data.class_type)) {
|
||||
// console.log('data.options',data.options)
|
||||
// 滑块输入
|
||||
@@ -1305,8 +1322,8 @@
|
||||
container.appendChild(silde);
|
||||
}
|
||||
|
||||
// Check if the class_type is "CLIPTextEncode"
|
||||
if (["TextInput_", "CLIPTextEncode", "PromptSimplification"].includes(data.class_type)) {
|
||||
// 文本输入支持
|
||||
if (["TextInput_", "CLIPTextEncode", "PromptSimplification","ChinesePrompt_Mix"].includes(data.class_type)) {
|
||||
// Create a container for the upload control
|
||||
const uploadContainer = document.createElement("div");
|
||||
uploadContainer.className = 'card';
|
||||
@@ -1371,7 +1388,7 @@
|
||||
container.appendChild(uploadContainer);
|
||||
}
|
||||
|
||||
|
||||
// lora的输入支持
|
||||
if (["CheckpointLoaderSimple", "LoraLoader"].includes(data.class_type)) {
|
||||
let value = data.inputs.ckpt_name || data.inputs.lora_name;
|
||||
|
||||
@@ -1415,6 +1432,7 @@
|
||||
container.appendChild(div);
|
||||
}
|
||||
|
||||
// 色彩选择器
|
||||
if (["Color"].includes(data.class_type)) {
|
||||
let value = data.inputs.color.hex || '#000000';
|
||||
let d = document.createElement('div');
|
||||
@@ -2097,9 +2115,10 @@
|
||||
};
|
||||
|
||||
api.addEventListener("status", ({ detail }) => {
|
||||
console.log("status", detail, detail.exec_info?.queue_remaining);
|
||||
console.log("status", detail, detail?.exec_info?.queue_remaining);
|
||||
try {
|
||||
ui.status.update(`queue#${detail.exec_info?.queue_remaining}`);
|
||||
window.parent.postMessage({ cmd: 'status', data: `queue#${detail.exec_info?.queue_remaining}` }, '*');
|
||||
if (detail.exec_info?.queue_remaining === 0) {
|
||||
// 运行按钮重设
|
||||
ui.submitButton.reset()
|
||||
@@ -2107,6 +2126,7 @@
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
window.parent.postMessage({ cmd: 'status' }, '*');
|
||||
}
|
||||
|
||||
});
|
||||
@@ -2206,6 +2226,7 @@
|
||||
api.addEventListener("execution_error", ({ detail }) => {
|
||||
|
||||
console.log("execution_error", detail)
|
||||
window.parent.postMessage({ cmd: 'status', data: `execution_error:${JSON.stringify(detail)}` }, '*');
|
||||
// show(URL.createObjectURL(detail));
|
||||
});
|
||||
|
||||
@@ -2235,7 +2256,6 @@
|
||||
api.init();
|
||||
|
||||
// 外挂的UI
|
||||
|
||||
createAllColorInput();
|
||||
|
||||
|
||||
@@ -2286,7 +2306,7 @@
|
||||
}
|
||||
|
||||
// 创建app的选择菜单
|
||||
function createAppList(apps = []) {
|
||||
function createAppList(apps = [], innerApp = false) {
|
||||
let details = document.createElement('details');
|
||||
details.className = 'apps';
|
||||
|
||||
@@ -2332,27 +2352,72 @@
|
||||
// console.log(div)
|
||||
};
|
||||
|
||||
let uploadApp = createUploadJson(details);
|
||||
div.appendChild(uploadApp);
|
||||
if (!innerApp) {
|
||||
let uploadApp = createUploadJson(details);
|
||||
div.appendChild(uploadApp);
|
||||
}
|
||||
|
||||
document.body.appendChild(details);
|
||||
}
|
||||
|
||||
async function init_app() {
|
||||
|
||||
const { category, filename } = getFilenameAndCategoryFromUrl(location.href);
|
||||
window._apps = await get_my_app(category, filename);
|
||||
const innerApp = checkIsInnerApp();
|
||||
|
||||
window._appData = window._apps[0];
|
||||
if (!innerApp) {
|
||||
const { category, filename } = getFilenameAndCategoryFromUrl(location.href);
|
||||
window._apps = await get_my_app(category, filename);
|
||||
|
||||
createAppList(window._apps);
|
||||
window._appData = window._apps[0];
|
||||
|
||||
createApp(window._appData);
|
||||
createAppList(window._apps);
|
||||
|
||||
createApp(window._appData);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
init_app();
|
||||
|
||||
// 支持内嵌app
|
||||
function checkIsInnerApp() {
|
||||
const url = new URL(window.location.href);
|
||||
const params = new URLSearchParams(url.search);
|
||||
const innerApp = params.get("innerApp");
|
||||
// console.log(window.location.href, innerApp == 1, document.body);
|
||||
if (innerApp == 1) {
|
||||
document.body.querySelector('.header').style.display = 'none';
|
||||
window.parent.postMessage({ innerApp, cmd: 'init' }, '*');
|
||||
|
||||
// 在iframe中监听来自父窗口的消息
|
||||
window.addEventListener("message", function (event) {
|
||||
console.log("Received message from parent:", event.data);
|
||||
const { init, url } = event.data;
|
||||
|
||||
window._hostUrl = url;
|
||||
|
||||
window._apps = init;
|
||||
|
||||
window._appData = window._apps[0];
|
||||
|
||||
if (window._appData) {
|
||||
createAppList(window._apps, innerApp);
|
||||
|
||||
createApp(window._appData);
|
||||
} else {
|
||||
// todo welcome页面
|
||||
document.body.innerHTML = `<h3 style="padding: 99px;">Welcome to Mixlab Nodes App!</h3>`
|
||||
}
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
return innerApp == 1
|
||||
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { app } from '../../../scripts/app.js'
|
||||
const repoOwner = 'shadowcz007' // 替换为仓库的所有者
|
||||
const repoName = 'comfyui-mixlab-nodes' // 替换为仓库的名称
|
||||
|
||||
const version = 'v0.17.0'
|
||||
const version = 'v0.17.1'
|
||||
|
||||
fetch(`https://api.github.com/repos/${repoOwner}/${repoName}/releases/latest`)
|
||||
.then(response => response.json())
|
||||
|
||||
@@ -408,7 +408,7 @@ const _createResult = async (node, widget, message) => {
|
||||
const width = node.size[0] * 0.5 - 12
|
||||
|
||||
let height_add = 0
|
||||
|
||||
|
||||
for (let index = 0; index < message._images.length; index++) {
|
||||
const imgs = message._images[index]
|
||||
|
||||
@@ -559,8 +559,7 @@ app.registerExtension({
|
||||
|
||||
let cards = widget.div.querySelectorAll('.card')
|
||||
if (cards.length == 0) node.size = [280, 120]
|
||||
|
||||
_createResult(node, widget, widget.value)
|
||||
if(widget.value) _createResult(node, widget, widget.value)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
+20
-16
@@ -1478,31 +1478,35 @@ app.registerExtension({
|
||||
|
||||
widget.element.addEventListener('mouseover', e => {
|
||||
// console.log(node.widgets_values[index])
|
||||
if (node.widgets_values[index])
|
||||
if (node.widgets_values&&node.widgets_values[index])
|
||||
widget.element.setAttribute('title', node.widgets_values[index])
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 右上角的badge是否已经绘制
|
||||
if (!node.badge_enabled) {
|
||||
if (!node.getNickname) {
|
||||
node.getNickname = function () {
|
||||
if (node.nickname) {
|
||||
return node.nickname
|
||||
fetch('manager/badge_mode').then(r => {
|
||||
if (r.status === 404) {
|
||||
// 右上角的badge是否已经绘制
|
||||
if (!node.badge_enabled) {
|
||||
if (!node.getNickname) {
|
||||
node.getNickname = function () {
|
||||
if (node.nickname) {
|
||||
return node.nickname
|
||||
}
|
||||
return
|
||||
// return getNickname(node, node.comfyClass.trim())
|
||||
}
|
||||
}
|
||||
return
|
||||
// return getNickname(node, node.comfyClass.trim())
|
||||
|
||||
const orig = node.__proto__.onDrawForeground
|
||||
node.onDrawForeground = function (ctx) {
|
||||
drawBadge(node, orig, arguments)
|
||||
}
|
||||
node.badge_enabled = true
|
||||
}
|
||||
}
|
||||
|
||||
const orig = node.__proto__.onDrawForeground
|
||||
node.onDrawForeground = function (ctx) {
|
||||
drawBadge(node, orig, arguments)
|
||||
}
|
||||
node.badge_enabled = true
|
||||
}
|
||||
})
|
||||
},
|
||||
async loadedGraphNode (node, app) {
|
||||
// console.log(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"last_node_id": 23,
|
||||
"last_link_id": 25,
|
||||
"last_node_id": 24,
|
||||
"last_link_id": 26,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 9,
|
||||
@@ -203,6 +203,7 @@
|
||||
"Node name for S&R": "ShowTextForGPT"
|
||||
},
|
||||
"widgets_values": [
|
||||
"a girl face,super,(Pop Art:1.26),(Black and White:1.26)",
|
||||
"a girl face,super,(Pop Art:1.26),(Black and White:1.26)"
|
||||
]
|
||||
},
|
||||
@@ -537,7 +538,7 @@
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
24,
|
||||
25
|
||||
26
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
@@ -547,7 +548,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 23,
|
||||
"id": 24,
|
||||
"type": "AppInfo",
|
||||
"pos": [
|
||||
3363.0014990624995,
|
||||
@@ -562,9 +563,9 @@
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "LOGO",
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"link": 25
|
||||
"link": 26
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
@@ -580,7 +581,7 @@
|
||||
"https://",
|
||||
"",
|
||||
"enable",
|
||||
1
|
||||
2
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -706,10 +707,10 @@
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
25,
|
||||
26,
|
||||
10,
|
||||
0,
|
||||
23,
|
||||
24,
|
||||
0,
|
||||
"IMAGE"
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user