diff --git a/__init__.py b/__init__.py index 574d856..a2b45c2 100644 --- a/__init__.py +++ b/__init__.py @@ -1,3 +1,4 @@ +_d='RiceRoundTempTokenNode' _c='RiceRoundOutputImageNode' _b='RiceRoundEncryptNode' _a='RiceRoundOutputTextNode' @@ -45,8 +46,8 @@ def create_dynamic_nodes(base_class): for(node_name,info)in rice_prompt_info.choice_classname_map.items():class_name=node_name;category='RiceRound/Advanced/Choice';dynamic_class=type(class_name,(base_class,),{'__node_name__':node_name,'CATEGORY':category});dynamic_classes[class_name]={_B:dynamic_class,_A:info.get(_A,class_name)} return dynamic_classes dynamic_choice_nodes=create_dynamic_nodes(RiceRoundBaseChoiceNode) -NODE_CLASS_MAPPINGS={_C:RiceRoundSimpleChoiceNode,_D:RiceRoundAdvancedChoiceNode,_E:RiceRoundDebugNode,_F:RiceRoundImageBridgeNode,_G:RiceRoundSimpleImageNode,_H:RiceRoundDownloadImageNode,_I:RiceRoundRandomSeedNode,_J:RiceRoundInputTextNode,_K:RiceRoundMaskBridgeNode,_L:RiceRoundDownloadMaskNode,_M:RiceRoundIntNode,_N:RiceRoundFloatNode,_O:RiceRoundBooleanNode,_P:RiceRoundStrToIntNode,_Q:RiceRoundStrToFloatNode,_R:RiceRoundStrToBooleanNode,_S:RiceRoundDecryptNode,_T:RiceRoundOutputImageBridgeNode,_U:RiceRoundImageUrlNode,_V:RiceRoundUploadImageNode,_W:RiceRoundOutputMaskBridgeNode,_X:RiceRoundOutputIntNode,_Y:RiceRoundOutputFloatNode,_Z:RiceRoundOutputBooleanNode,_a:RiceRoundOutputTextNode,_b:RiceRoundEncryptNode,_c:RiceRoundOutputImageNode,**{name:cls[_B]for(name,cls)in dynamic_choice_nodes.items()}} -NODE_DISPLAY_NAME_MAPPINGS={_C:'Simple Choice',_D:'Advanced Choice',_E:'Debug',_F:'Image Bridge',_G:'Simple Image',_H:'Download Image',_I:'Random Seed',_J:'Input Text',_K:'Mask Bridge',_L:'Download Mask',_M:'RiceRound Int',_N:'RiceRound Float',_O:'RiceRound Boolean',_P:'RiceRound Str To Int',_Q:'RiceRound Str To Float',_R:'RiceRound Str To Boolean',_S:'Decrypt',_T:'Output Image Bridge',_U:'Image URL',_V:'Upload Image',_W:'Output Mask Bridge',_X:'Output Int',_Y:'Output Float',_Z:'Output Boolean',_a:'Output Text',_b:'Encrypt',_c:'Output Image',**{name:cls[_A]for(name,cls)in dynamic_choice_nodes.items()}} +NODE_CLASS_MAPPINGS={_C:RiceRoundSimpleChoiceNode,_D:RiceRoundAdvancedChoiceNode,_E:RiceRoundDebugNode,_F:RiceRoundImageBridgeNode,_G:RiceRoundSimpleImageNode,_H:RiceRoundDownloadImageNode,_I:RiceRoundRandomSeedNode,_J:RiceRoundInputTextNode,_K:RiceRoundMaskBridgeNode,_L:RiceRoundDownloadMaskNode,_M:RiceRoundIntNode,_N:RiceRoundFloatNode,_O:RiceRoundBooleanNode,_P:RiceRoundStrToIntNode,_Q:RiceRoundStrToFloatNode,_R:RiceRoundStrToBooleanNode,_S:RiceRoundDecryptNode,_T:RiceRoundOutputImageBridgeNode,_U:RiceRoundImageUrlNode,_V:RiceRoundUploadImageNode,_W:RiceRoundOutputMaskBridgeNode,_X:RiceRoundOutputIntNode,_Y:RiceRoundOutputFloatNode,_Z:RiceRoundOutputBooleanNode,_a:RiceRoundOutputTextNode,_b:RiceRoundEncryptNode,_c:RiceRoundOutputImageNode,_d:RiceRoundTempTokenNode,**{name:cls[_B]for(name,cls)in dynamic_choice_nodes.items()}} +NODE_DISPLAY_NAME_MAPPINGS={_C:'Simple Choice',_D:'Advanced Choice',_E:'Debug',_F:'Image Bridge',_G:'Simple Image',_H:'Download Image',_I:'Random Seed',_J:'Input Text',_K:'Mask Bridge',_L:'Download Mask',_M:'RiceRound Int',_N:'RiceRound Float',_O:'RiceRound Boolean',_P:'RiceRound Str To Int',_Q:'RiceRound Str To Float',_R:'RiceRound Str To Boolean',_S:'Decrypt',_T:'Output Image Bridge',_U:'Image URL',_V:'Upload Image',_W:'Output Mask Bridge',_X:'Output Int',_Y:'Output Float',_Z:'Output Boolean',_a:'Output Text',_b:'Encrypt',_c:'Output Image',_d:'Temp Token',**{name:cls[_A]for(name,cls)in dynamic_choice_nodes.items()}} WEB_DIRECTORY='./js' __all__=['NODE_CLASS_MAPPINGS','NODE_DISPLAY_NAMES_MAPPINGS','WEB_DIRECTORY'] handler_instance=RiceRoundPromptHandler() diff --git a/auth_unit.py b/auth_unit.py index c74e734..220dece 100644 --- a/auth_unit.py +++ b/auth_unit.py @@ -17,10 +17,12 @@ class AuthUnit: return A._instance def __init__(A): B=True - if not hasattr(A,'initialized'):A.machine_id=get_machine_id();A.url_config=RiceUrlConfig();A.callback_path='/riceround/auth_callback';A.shift_key=sum(ord(A)for A in A.machine_id[:8])%20+1;C=get_local_app_path();C.mkdir(parents=B,exist_ok=B);A.config_path=C/'config.ini';A.auto_login=False;A.load_config();A.last_check_time=0;A.initialized=B + if not hasattr(A,'initialized'):A.machine_id=get_machine_id();A.url_config=RiceUrlConfig();A.callback_path='/riceround/auth_callback';A.shift_key=sum(ord(A)for A in A.machine_id[:8])%20+1;C=get_local_app_path();C.mkdir(parents=B,exist_ok=B);A.config_path=C/'config.ini';A.auto_login=False;A.load_config();A.last_check_time=0;A.temp_token='';A.initialized=B def load_config(A):B=configparser.ConfigParser();B.read(A.config_path,encoding=_A);A.auto_login=B.get(_B,'auto_login',fallback=False) + def set_temp_token(A,temp_token):A.temp_token=temp_token def get_user_token(B): A=B.read_user_token() + if not A and B.temp_token:A=B.temp_token if A and time.time()-B.last_check_time>120: try: D=urllib.request.Request(B.url_config.login_api_url,headers={'Content-Type':'application/json','Authorization':f"Bearer {A}"},method='GET') @@ -53,13 +55,14 @@ class AuthUnit: if not D:return'' return AuthUnit._decrypt(D,A.shift_key) except Exception as E:print(f"Error reading token: {E}");return'' - def save_user_token(B,user_token): + def save_user_token(A,user_token): 'Encrypt and save the user token.';C=user_token if not C:return + if C==A.temp_token:return try: - A=configparser.ConfigParser() - if os.path.exists(B.config_path):A.read(B.config_path,encoding=_A) - if _B not in A:A.add_section(_B) - E=AuthUnit._encrypt(C,B.shift_key);A[_B][_D]=E - with open(B.config_path,'w',encoding=_A)as F:A.write(F) + B=configparser.ConfigParser() + if os.path.exists(A.config_path):B.read(A.config_path,encoding=_A) + if _B not in B:B.add_section(_B) + E=AuthUnit._encrypt(C,A.shift_key);B[_B][_D]=E + with open(A.config_path,'w',encoding=_A)as F:B.write(F) except Exception as D:print(f"Error saving token: {D}");raise RuntimeError(f"Failed to save token: {D}") \ No newline at end of file diff --git a/encrypt_node.py b/encrypt_node.py index 287cc72..ddb23db 100644 --- a/encrypt_node.py +++ b/encrypt_node.py @@ -1,50 +1,49 @@ -_A0='00000000-0000-0000-0000-000000000000' -_z='task_id' -_y='RiceRoundImageUrlNode' -_x='RiceRoundUploadImageNode' -_w='RiceRoundOutputImageBridgeNode' -_v='Node name for S&R' -_u='properties' -_t='RiceRoundEncryptNode' -_s='input_anything' -_r='main_link_type' -_q='last_node_id' -_p='owner_id' -_o='RiceRound' -_n='EXTRA_PNGINFO' -_m='PROMPT' -_l='UNIQUE_ID' -_k='template_id' -_j='hidden' -_i='optional' -_h='required' -_g='RiceRoundInputTextNode' -_f='class_type' -_e='main_link_id' -_d='slot_index' -_c='RiceRoundAdvancedChoiceNode' -_b='RiceRoundSimpleChoiceNode' -_a='RiceRoundDecryptNode' -_Z='extra_pnginfo' -_Y='prompt' -_X='RiceRoundStrToBooleanNode' -_W='RiceRoundBooleanNode' -_V='RiceRoundStrToFloatNode' -_U='RiceRoundStrToIntNode' -_T='RiceRoundFloatNode' -_S='RiceRoundIntNode' -_R='RiceRoundDownloadMaskNode' -_Q='RiceRoundMaskBridgeNode' -_P='RiceRoundImageBridgeNode' -_O='RiceRoundSimpleImageNode' -_N='name' -_M='unique_id' -_L='RiceRoundDownloadImageNode' -_K='IMAGE' -_J='default' -_I='inputs' -_H='STRING' -_G='label' +_z='00000000-0000-0000-0000-000000000000' +_y='task_id' +_x='RiceRoundImageUrlNode' +_w='RiceRoundUploadImageNode' +_v='RiceRoundOutputImageBridgeNode' +_u='Node name for S&R' +_t='properties' +_s='RiceRoundEncryptNode' +_r='input_anything' +_q='main_link_type' +_p='last_node_id' +_o='owner_id' +_n='RiceRound' +_m='EXTRA_PNGINFO' +_l='PROMPT' +_k='UNIQUE_ID' +_j='template_id' +_i='hidden' +_h='optional' +_g='required' +_f='RiceRoundInputTextNode' +_e='class_type' +_d='main_link_id' +_c='slot_index' +_b='RiceRoundAdvancedChoiceNode' +_a='RiceRoundSimpleChoiceNode' +_Z='RiceRoundDecryptNode' +_Y='extra_pnginfo' +_X='prompt' +_W='RiceRoundStrToBooleanNode' +_V='RiceRoundBooleanNode' +_U='RiceRoundStrToFloatNode' +_T='RiceRoundStrToIntNode' +_S='RiceRoundFloatNode' +_R='RiceRoundIntNode' +_Q='RiceRoundDownloadMaskNode' +_P='RiceRoundMaskBridgeNode' +_O='RiceRoundImageBridgeNode' +_N='RiceRoundSimpleImageNode' +_M='name' +_L='unique_id' +_K='RiceRoundDownloadImageNode' +_J='IMAGE' +_I='default' +_H='inputs' +_G='STRING' _F='outputs' _E='nodes' _D='links' @@ -60,15 +59,15 @@ from.utils import combine_files from.rice_url_config import machine_upload_image if __name__=='__main__':output_project_folder='D:\\output' else:import folder_paths;from server import PromptServer;from.rice_url_config import RiceUrlConfig;from.rice_prompt_info import RicePromptInfo;output_project_folder=folder_paths.output_directory -INPUT_NODE_TYPES=[_b,_c,_O,_L,_P,_g,_Q,_R,_S,_T,_U,_V,_W,_X] +INPUT_NODE_TYPES=[_a,_b,_N,_K,_O,_f,_P,_Q,_R,_S,_T,_U,_V,_W] class RiceRoundEncryptNode: def __init__(A):A.template_id=uuid.uuid4().hex @classmethod - def INPUT_TYPES(A):return{_h:{'project_name':(_H,{_J:'my_project'}),_k:(_H,{_J:uuid.uuid4().hex})},_i:{'output':(_K,)},_j:{_M:_l,_Y:_m,_Z:_n}} + def INPUT_TYPES(A):return{_g:{'project_name':(_G,{_I:'my_project'}),_j:(_G,{_I:uuid.uuid4().hex})},_h:{'output':(_J,)},_i:{_L:_k,_X:_l,_Y:_m}} @classmethod def IS_CHANGED(A,**B):return float('NaN') - RETURN_TYPES=();OUTPUT_NODE=True;FUNCTION='encrypt';CATEGORY=_o - def encrypt(E,project_name,template_id,output,**A):F=A.pop(_M,_B);B=A.pop(_Z,_B);C=A.pop(_Y,_B);D=Encrypt(B['workflow'],C,project_name,template_id);D.do_encrypt();return{} + RETURN_TYPES=();OUTPUT_NODE=True;FUNCTION='encrypt';CATEGORY=_n + def encrypt(E,project_name,template_id,output,**A):F=A.pop(_L,_B);B=A.pop(_Y,_B);C=A.pop(_X,_B);D=Encrypt(B['workflow'],C,project_name,template_id);D.do_encrypt();return{} class Encrypt: def __init__(A,workflow,prompt,project_name,template_id): A.original_workflow=workflow;A.original_prompt=prompt;A.template_id=template_id;A.project_name=project_name;A.project_folder=os.path.join(output_project_folder,A.project_name) @@ -84,8 +83,8 @@ class Encrypt: for D in G: E=D.get(_D,[]) if not E:continue - for B in E:B=int(B);A.link_owner_map[B][_D]=copy.deepcopy(E);A.link_owner_map[B][_d]=D.get(_d,0);A.link_owner_map[B][_p]=int(F[_C]);A.link_owner_map[B][_A]=D.get(_A,'') - A.last_node_id=int(C[_q]);A.last_link_id=int(C['last_link_id']) + for B in E:B=int(B);A.link_owner_map[B][_D]=copy.deepcopy(E);A.link_owner_map[B][_c]=D.get(_c,0);A.link_owner_map[B][_o]=int(F[_C]);A.link_owner_map[B][_A]=D.get(_A,'') + A.last_node_id=int(C[_p]);A.last_link_id=int(C['last_link_id']) def load_prompt(A):B=copy.deepcopy(A.original_prompt);A.node_prompt_map={int(A):B for(A,B)in B.items()} def analyze_input_from_workflow(A): for(id,B)in A.workflow_nodes_dict.items(): @@ -95,61 +94,63 @@ class Encrypt: if not C:continue D=C[0].get(_D,[]) if not D:continue - F=int(D[0]);A.input_node_map[id][_e]=F;A.input_node_map[id][_r]=C[0].get(_A,_H) + F=int(D[0]);A.input_node_map[id][_d]=F;A.input_node_map[id][_q]=C[0].get(_A,_G) + A.input_node_map={A:B for(A,B)in sorted(A.input_node_map.items(),key=lambda x:x[0])} def assemble_new_workflow(A):C=list(A.input_node_map.keys());B=copy.deepcopy(A.original_workflow);A.related_node_ids=A.find_workflow_related_nodes(B[_D],C);B[_E]=[B for B in B[_E]if int(B[_C])in A.related_node_ids];A.invalid_new_workflow(B);D=A.add_decrypt_node(B);A.remove_redundant_links(B);A.remove_unrelated_nodes(B,A.related_node_ids,D);A.replace_choice_template(B);A.replace_workflow_node(B);A.output_file(B,f"workflow") - def output_template_json_file(G): - U='模型选择';T='提示词';S='请上传蒙版';R='image/*';Q='tip';P='max_size';O='accept';N='请上传图片';L='number';K='max';J='min';I='数值';F='settings';E='describe';V=RicePromptInfo();M=[] - for(H,W)in G.input_node_map.items(): - X=W[_s];C=G.workflow_nodes_dict[H][_A];D=G.node_prompt_map[H].get(_I,{});A=str(D.get(_N,''));B={_C:str(X),_A:'',E:'输入组件','node_id':str(H),F:{}} - if C in[_O,_L,_P]:B[_A]='image_upload';B[E]=A if A else N,;B[F]={_G:A if A else N,O:R,P:500000,Q:'请上传不超过500KB的图片'} - elif C in[_Q,_R]:B[_A]='mask_upload';B[E]=A if A else S,;B[F]={_G:A if A else S,O:R,P:50000,Q:'请上传不超过50KB的图片'} - elif C==_g:B[_A]='text';B[E]=A if A else T,;B[F]={_G:A if A else T,'placeholder':'请描述图片内容','multiline':True} - elif C==_c or C==_b:B[_A]='choice';B[E]=A if A else U,;B[F]={_G:A if A else U,'options':V.get_choice_value(H),_J:D.get(_J,'')} - elif C==_S or C==_U:B[_A]='number_int';B[E]=A if A else I,;B[F]={_G:A if A else I,J:D.get(J,0),K:D.get(K,1000),L:D.get(L,0)} - elif C==_T or C==_V:B[_A]='number_float';B[E]=A if A else I,;B[F]={_G:A if A else I,J:D.get(J,.0),K:D.get(K,1e3),L:D.get(L,.0)} - elif C==_W or C==_X:B[_A]='switch';B[E]=A if A else'开关';B[F]={_G:A if A else'开关',_J:D.get('value',False)} - else:raise ValueError(f"Error: The node {H} is not a valid RiceRound node.") - M.append(B) - Y={_k:G.template_id,'title':G.project_name,'elements':M};G.output_file(Y,f"{G.template_id}_template") + def output_template_json_file(F): + P='image/*';O='tip';N='max_size';M='accept';J='number';I='max';H='min';E='settings';D='describe';Q=RicePromptInfo();K=[] + for(G,R)in F.input_node_map.items(): + S=R[_r];B=F.workflow_nodes_dict[G][_A];C=F.node_prompt_map[G].get(_H,{});L=str(C.get(_M,''));A={_C:str(S),_A:'',D:'输入组件','node_id':str(G),E:{}} + if B in[_N,_K,_O]:A[_A]='image_upload';A[D]='请上传图片';A[E]={M:P,N:500000,O:'请上传不超过500KB的图片'} + elif B in[_P,_Q]:A[_A]='mask_upload';A[D]='请上传蒙版';A[E]={M:P,N:50000,O:'请上传不超过50KB的图片'} + elif B==_f:A[_A]='text';A[D]='提示词';A[E]={'placeholder':'请描述图片内容','multiline':True} + elif B==_b or B==_a:A[_A]='choice';A[D]='模型选择';A[E]={'options':Q.get_choice_value(G),_I:C.get(_I,'')} + elif B==_R or B==_T:A[_A]='number_int';A[D]='数值';A[E]={H:C.get(H,0),I:C.get(I,1000),J:C.get(J,0)} + elif B==_S or B==_U:A[_A]='number_float';A[D]='数值';A[E]={H:C.get(H,.0),I:C.get(I,1e3),J:C.get(J,.0)} + elif B==_V or B==_W:A[_A]='switch';A[D]='开关';A[E]={_I:C.get('value',False)} + else:raise ValueError(f"Error: The node {G} is not a valid RiceRound node.") + if L:A[D]=L + K.append(A) + T={_j:F.template_id,'title':F.project_name,'elements':K};F.output_file(T,f"{F.template_id}_template") def assemble_new_prompt(A):'\n 组装新的prompt配置。主要完成:\n 1. 移除不需要的节点\n 2. 转换特定节点的类型和输入\n 3. 保存处理后的prompt配置\n ';B=A._create_filtered_prompt();A._transform_node_types(B);A.output_file(B,f"{A.template_id}_job") def _create_filtered_prompt(B): '\n 创建经过过滤的prompt副本,移除不需要的节点\n ';A=copy.deepcopy(B.original_prompt);C=B._get_exclude_node_ids(A) for D in C:A.pop(str(D),_B) return A def save_rice_zip(A): - '\n 将工作流相关文件打包成rice.zip\n 包括:\n - workflow.json\n - {template_id}_job.json\n - {template_id}_template.json \n - choice_node文件夹(如果存在)\n - original_workflow.json\n - original_prompt.json\n ';J='job.bin';import pyzipper as F + '\n 将工作流相关文件打包成rice.zip\n 包括:\n - workflow.json\n - {template_id}_job.json\n - {template_id}_template.json \n - choice_node文件夹(如果存在)\n - original_workflow.json\n - original_prompt.json\n ';I='job.bin';import pyzipper as F try: - C=[];B=[];G=[];D=os.path.join(A.project_folder,J) - for K in[f"{A.template_id}_job.json",'original_workflow.json','original_prompt.json']:H=os.path.join(A.project_folder,K);G.append(H);B.append(H) - combine_files(G,A.template_id,D);C.append((D,J));B.append(D);I=os.path.join(A.project_folder,f"{A.template_id}_template.json");C.append((I,'template.bin'));B.append(I);L=os.path.join(A.project_folder,'rice.zip') + B=[];C=[];G=[];D=os.path.join(A.project_folder,I) + for J in[f"{A.template_id}_job.json",'original_workflow.json','original_prompt.json']:H=os.path.join(A.project_folder,J);G.append(H);C.append(H) + combine_files(G,A.template_id,D);B.append((D,I));C.append(D);K=os.path.join(A.project_folder,f"{A.template_id}_template.json");B.append((K,'template.bin'));L=os.path.join(A.project_folder,'rice.zip') with F.AESZipFile(L,'w',compression=F.ZIP_DEFLATED)as M: - for(E,N)in C:M.write(E,N) + for(E,N)in B:M.write(E,N) print(f"save zip success") - for E in B:os.remove(E) + for E in C:os.remove(E) except Exception as O:print(f"Error creating zip: {str(O)}");raise def _get_exclude_node_ids(A,prompt): - '\n 获取需要从prompt中排除的节点ID集合\n ';C={_t,_a,'RiceRoundDebugNode'};B=A.related_node_ids.difference(set(A.input_node_map.keys())) + '\n 获取需要从prompt中排除的节点ID集合\n ';C={_s,_Z,'RiceRoundDebugNode'};B=A.related_node_ids.difference(set(A.input_node_map.keys())) for(D,E)in prompt.items(): - if E.get(_f,'')in C:B.add(int(D)) + if E.get(_e,'')in C:B.add(int(D)) return B def _transform_node_types(K,prompt): - '\n 转换节点类型和更新节点输入配置\n ';J='image_url';D='str';B='new_inputs';A='new_type';E={_P:{A:_L,B:{J:''}},_O:{A:_L,B:{J:''}},_Q:{A:_R,B:{'mask_url':''}},_S:{A:_U,B:{D:''}},_T:{A:_V,B:{D:''}},_W:{A:_X,B:{D:''}}} + '\n 转换节点类型和更新节点输入配置\n ';J='image_url';D='str';B='new_inputs';A='new_type';E={_O:{A:_K,B:{J:''}},_N:{A:_K,B:{J:''}},_P:{A:_Q,B:{'mask_url':''}},_R:{A:_T,B:{D:''}},_S:{A:_U,B:{D:''}},_V:{A:_W,B:{D:''}}} for(L,C)in prompt.items(): - C.pop('is_changed',_B);F=C.get(_f,'');G=C.get(_I,{}) + C.pop('is_changed',_B);F=C.get(_e,'');G=C.get(_H,{}) if not G:continue - H=G.get(_N,'') + H=G.get(_M,'') if F in E: - I=E[F];C[_f]=I[A];C[_I]=I[B].copy() - if H:C[_I][_N]=H + I=E[F];C[_e]=I[A];C[_H]=I[B].copy() + if H:C[_H][_M]=H def add_decrypt_node(A,workflow): - C=workflow;G=set();A.last_node_id+=1;H={_C:A.last_node_id,_A:_a,'pos':[420,0],'size':[500,150],'flags':{},'mode':0,'order':20,_I:[],_F:[{_N:_K,_A:_K,_D:[],_G:_K,_d:0}],_u:{_v:_a},'widgets_values':[str(A.template_id)]} + K='label';C=workflow;G=set();A.last_node_id+=1;H={_C:A.last_node_id,_A:_Z,'pos':[420,0],'size':[500,150],'flags':{},'mode':0,'order':20,_H:[],_F:[{_M:_J,_A:_J,_D:[],K:_J,_c:0}],_t:{_u:_Z},'widgets_values':[str(A.template_id),735127949069071,'randomize']} for(B,(D,E))in enumerate(A.input_node_map.items()): - I=E[_e];F=E[_r];E[_s]=B;J={_N:f"input_anything{B if B>0 else''} ({D})",_A:'*','link':I,_G:f"input_anything{B if B>0 else''} ({D})"} + I=E[_d];F=E[_q];E[_r]=B;J={_M:f"input_anything{B if B>0 else''} ({D})",_A:'*','link':I,K:f"input_anything{B if B>0 else''} ({D})"} if B==0:J['shape']=7 - H[_I].append(J) - if F not in[_K,_H]:F=_H - K=[I,D,0,A.last_node_id,B,F];C[_D].append(K) - G.add(A.last_node_id);C[_E].append(H);C[_q]=A.last_node_id;return G + H[_H].append(J) + if F not in[_J,_G]:F=_G + L=[I,D,0,A.last_node_id,B,F];C[_D].append(L) + G.add(A.last_node_id);C[_E].append(H);C[_p]=A.last_node_id;return G def output_file(A,workflow,prefix): B=os.path.join(A.project_folder,f"{prefix}.json") with open(B,'w',encoding='utf-8')as C:json.dump(workflow,C,ensure_ascii=False,indent=4) @@ -158,7 +159,7 @@ class Encrypt: for C in B[_E]: F=int(C[_C]) if F in D.input_node_map: - G=D.input_node_map[F][_e];A=C.get(_F,[]) + G=D.input_node_map[F][_d];A=C.get(_F,[]) if not A:continue if len(A)!=1:raise ValueError(f"Error: The node {C[_C]} has an invalid number of outputs.") H=A[0].get(_D,[]) @@ -171,25 +172,25 @@ class Encrypt: D='extra';C=workflow;E=RicePromptInfo() for A in C[_E]: B=int(A[_C]) - if A.get(_A,'')==_c: + if A.get(_A,'')==_b: F=E.get_choice_classname(B) if F:A[_A]=F else:print(f"Warning: The node {B} is not a valid RiceRound Choice node.") G={} for(B,A)in H.input_node_map.items(): - if A.get(_A,'')==_b:I=E.get_choice_value(B);G[B]=I + if A.get(_A,'')==_a:I=E.get_choice_value(B);G[B]=I if D not in C:C[D]={} C[D]['choice_node_map']=G def replace_workflow_node(J,workflow): - I='unknown';D=workflow;C='RiceRoundOutputTextNode';E={_P:_w,_O:_x,_L:_y,_Q:'RiceRoundOutputMaskBridgeNode',_R:'RiceRoundMaskUrlNode',_S:'RiceRoundOutputIntNode',_T:'RiceRoundOutputFloatNode',_W:'RiceRoundOutputBooleanNode',_X:C,_U:C,_V:C};F=set() + I='unknown';D=workflow;C='RiceRoundOutputTextNode';E={_O:_v,_N:_w,_K:_x,_P:'RiceRoundOutputMaskBridgeNode',_Q:'RiceRoundMaskUrlNode',_R:'RiceRoundOutputIntNode',_S:'RiceRoundOutputFloatNode',_V:'RiceRoundOutputBooleanNode',_W:C,_T:C,_U:C};F=set() for A in D[_E]: G=A.get(_A,'') if G in E: if _F not in A:raise ValueError(f"Node {A.get(_C,I)} missing outputs") if not A[_F]or not isinstance(A[_F],list):raise ValueError(f"Invalid outputs format in node {A.get(_C,I)}") - H=E[G];A.update({_A:H,_F:[{_A:_H,**A[_F][0]}],_u:{_v:H}});F.add(int(A[_C])) + H=E[G];A.update({_A:H,_F:[{_A:_G,**A[_F][0]}],_t:{_u:H}});F.add(int(A[_C])) for B in D[_D]: - if len(B)==6 and B[1]in F:B[5]=_H + if len(B)==6 and B[1]in F:B[5]=_G def remove_unrelated_nodes(E,workflow,related_node_ids,new_node_ids): B=workflow;C=[];D=related_node_ids.union(new_node_ids) for A in B[_D]: @@ -202,15 +203,15 @@ class Encrypt: for A in workflow[_E]: B=A.get(_A,'') if B=='RiceRoundOutputImageNode':C=A;continue - elif B==_t:D+=1;continue - if B in[_a,_w,_x,_y]:raise ValueError(f"Error: The node {A[_C]} is not a valid RiceRound node.") + elif B==_s:D+=1;continue + if B in[_Z,_v,_w,_x]:raise ValueError(f"Error: The node {A[_C]} is not a valid RiceRound node.") if D!=1:raise ValueError('Error: Multiple RiceRoundEncryptNode nodes are not allowed.') if C is _B:raise ValueError("Error: The node is not an 'RiceRoundOutputImageNode'.") def invalid_new_workflow(A,workflow): for B in workflow[_E]: - C=B.get(_I,[]) + C=B.get(_H,[]) for input in C: - D=int(input.get('link',0));E=A.link_owner_map[D][_p];F=A.workflow_nodes_dict[E][_A] + D=int(input.get('link',0));E=A.link_owner_map[D][_o];F=A.workflow_nodes_dict[E][_A] if F in INPUT_NODE_TYPES:raise ValueError(f"Error: The node {B[_C]} may have circular references, generation failed.") def find_workflow_related_nodes(F,links,input_ids): E=input_ids;B=set(E);C=list(E) @@ -226,14 +227,14 @@ class Encrypt: class RiceRoundOutputImageNode: def __init__(A):A.url_config=RiceUrlConfig() @classmethod - def INPUT_TYPES(A):return{_h:{'images':(_K,)},_i:{_z:(_H,{_J:_A0})},_j:{_M:_l,_Y:_m,_Z:_n}} - RETURN_TYPES=();OUTPUT_NODE=True;FUNCTION='load';CATEGORY=_o + def INPUT_TYPES(A):return{_g:{'images':(_J,)},_h:{_y:(_G,{_I:_z})},_i:{_L:_k,_X:_l,_Y:_m}} + RETURN_TYPES=();OUTPUT_NODE=True;FUNCTION='load';CATEGORY=_n def load(S,images,task_id,**C): - L='image_type';E='PNG';B=task_id;A=images;F=C.pop(_M,_B);M=C.pop(_Y,_B);T=C.pop(_Z,_B);G=PromptServer.instance.client_id;H='' + L='image_type';E='PNG';B=task_id;A=images;F=C.pop(_L,_B);M=C.pop(_X,_B);T=C.pop(_Y,_B);G=PromptServer.instance.client_id;H='' if hasattr(PromptServer.instance,'last_prompt_id')and PromptServer.instance.last_prompt_id:H=PromptServer.instance.last_prompt_id if F is _B:raise Exception("Warning: 'unique_id' is missing.") if M is _B:raise Exception("Warning: 'prompt' is missing.") - if B==_A0 or B=='': + if B==_z or B=='': N=comfy.utils.ProgressBar(A.shape[0]);I=0 for D in A:O=255.*D.cpu().numpy();P=Image.fromarray(np.clip(O,0,255).astype(np.uint8));N.update_absolute(I,A.shape[0],(E,P,_B));I+=1 return{} @@ -245,4 +246,4 @@ class RiceRoundOutputImageNode: K=machine_upload_image(D,B) if not K:raise ValueError('Error: Failed to upload image.') J.append(K) - Q={L:E,'image_results':J};R={_z:B,_M:F,'client_id':G,'prompt_id':H,'timestamp':int(time.time()*1000),L:E,'result_data':Q};PromptServer.instance.send_sync('rice_round_done',R,sid=G);return{} \ No newline at end of file + Q={L:E,'image_results':J};R={_y:B,_L:F,'client_id':G,'prompt_id':H,'timestamp':int(time.time()*1000),L:E,'result_data':Q};PromptServer.instance.send_sync('rice_round_done',R,sid=G);return{} \ No newline at end of file diff --git a/input_node.py b/input_node.py index 30d8ae1..d89d387 100644 --- a/input_node.py +++ b/input_node.py @@ -1,3 +1,4 @@ +_Y='RiceRound/Advanced' _X='number' _W='bridge' _V='only image.' @@ -17,11 +18,11 @@ _I='load' _H='INT' _G='name' _F='value' -_E='required' +_E='RiceRound/Input' _D=True -_C='RiceRound/Input' -_B='STRING' -_A='default' +_C='required' +_B='default' +_A='STRING' import hashlib,json,os,re,time,random from PIL import Image,ImageOps from PIL.PngImagePlugin import PngInfo @@ -32,15 +33,15 @@ import requests,sys from.utils import pil2tensor from comfy.utils import ProgressBar class _BasicTypes(str): - basic_types=[_B] + basic_types=[_A] def __eq__(B,other):A=other;return A in B.basic_types or isinstance(A,(list,_BasicTypes)) def __ne__(A,other):return not A.__eq__(other) BasicTypes=_BasicTypes('BASIC') class RiceRoundSimpleChoiceNode: def __init__(A):A.prompt_info=RicePromptInfo() @classmethod - def INPUT_TYPES(A):return{_E:{_G:(_B,{_A:'Parameter'}),_A:(_B,{_A:''})},_K:{},_N:{_S:'UNIQUE_ID',_T:'PROMPT','extra_pnginfo':'EXTRA_PNGINFO'}} - RETURN_TYPES=BasicTypes,;RETURN_NAMES=_F,;FUNCTION='placeholder';CATEGORY=_C + def INPUT_TYPES(A):return{_C:{_G:(_A,{_B:'Parameter'}),_B:(_A,{_B:''})},_K:{},_N:{_S:'UNIQUE_ID',_T:'PROMPT','extra_pnginfo':'EXTRA_PNGINFO'}} + RETURN_TYPES=BasicTypes,;RETURN_NAMES=_F,;FUNCTION='placeholder';CATEGORY=_E def placeholder(B,name,default,**C): A=int(C.pop(_S,0));D=C.pop(_T,None);E=_D if D: @@ -56,31 +57,31 @@ class RiceRoundAdvancedChoiceNode(RiceRoundSimpleChoiceNode): def __init__(A):super().__init__() class RiceRoundSimpleImageNode(LoadImage): def __init__(A):super().__init__() - RETURN_TYPES=_L,;RETURN_NAMES=_F,;OUTPUT_NODE=_D;CATEGORY=_C;FUNCTION=_U + RETURN_TYPES=_L,;RETURN_NAMES=_F,;OUTPUT_NODE=_D;CATEGORY=_E;FUNCTION=_U def load_image(B,image):A,C=super().load_image(image);return A, class RiceRoundDownloadImageNode: def __init__(A):0 @classmethod - def INPUT_TYPES(A):return{_E:{'image_url':(_B,{_A:''})},_K:{},_N:{}} - RETURN_TYPES=_L,;RETURN_NAMES=_F,;OUTPUT_NODE=_D;FUNCTION=_U;CATEGORY=_C + def INPUT_TYPES(A):return{_C:{'image_url':(_A,{_B:''})},_K:{},_N:{}} + RETURN_TYPES=_L,;RETURN_NAMES=_F,;OUTPUT_NODE=_D;FUNCTION=_U;CATEGORY=_E def load_image(B,image_url,**C):A=Image.open(requests.get(image_url,stream=_D).raw);A=ImageOps.exif_transpose(A);return pil2tensor(A), class RiceRoundImageBridgeNode: def __init__(A):0 @classmethod - def INPUT_TYPES(A):return{_E:{'images':(_L,{_O:_V})},_K:{}} - RETURN_TYPES=_L,;RETURN_NAMES=_F,;OUTPUT_NODE=_D;FUNCTION=_W;CATEGORY=_C + def INPUT_TYPES(A):return{_C:{'images':(_L,{_O:_V})},_K:{}} + RETURN_TYPES=_L,;RETURN_NAMES=_F,;OUTPUT_NODE=_D;FUNCTION=_W;CATEGORY=_E def bridge(A,images,**B):return images, class RiceRoundMaskBridgeNode: def __init__(A):0 @classmethod - def INPUT_TYPES(A):return{_E:{'mask':(_P,{_O:_V})}} - RETURN_TYPES=_P,;RETURN_NAMES=_F,;OUTPUT_NODE=_D;FUNCTION=_W;CATEGORY=_C + def INPUT_TYPES(A):return{_C:{'mask':(_P,{_O:_V})}} + RETURN_TYPES=_P,;RETURN_NAMES=_F,;OUTPUT_NODE=_D;FUNCTION=_W;CATEGORY=_E def bridge(A,mask,**B):return mask, class RiceRoundDownloadMaskNode: def __init__(A):0 @classmethod - def INPUT_TYPES(A):return{_E:{'mask_url':(_B,{_A:''})}} - RETURN_TYPES=_P,;RETURN_NAMES=_F,;OUTPUT_NODE=_D;FUNCTION='load_mask';CATEGORY=_C + def INPUT_TYPES(A):return{_C:{'mask_url':(_A,{_B:''})}} + RETURN_TYPES=_P,;RETURN_NAMES=_F,;OUTPUT_NODE=_D;FUNCTION='load_mask';CATEGORY=_E def load_mask(E,mask_url,**F): C=mask_url try: @@ -92,43 +93,43 @@ class RiceRoundDownloadMaskNode: class RiceRoundIntNode: def __init__(A):0 @classmethod - def INPUT_TYPES(A):return{_E:{_G:(_B,{_A:_M}),_X:(_H,{_A:0}),'min':(_H,{_A:0}),'max':(_H,{_A:100})}} - RETURN_TYPES=_H,;RETURN_NAMES=_F,;OUTPUT_NODE=_D;FUNCTION=_I;CATEGORY=_C + def INPUT_TYPES(A):return{_C:{_G:(_A,{_B:_M}),_X:(_H,{_B:0}),'min':(_H,{_B:0}),'max':(_H,{_B:100})}} + RETURN_TYPES=_H,;RETURN_NAMES=_F,;OUTPUT_NODE=_D;FUNCTION=_I;CATEGORY=_E def load(A,name,number,min,max,**B):return number, class RiceRoundStrToIntNode: def __init__(A):0 @classmethod - def INPUT_TYPES(A):return{_E:{_G:(_B,{_A:_M}),_Q:(_B,)}} - RETURN_TYPES=_H,;RETURN_NAMES=_F,;OUTPUT_NODE=_D;FUNCTION=_I;CATEGORY=_C + def INPUT_TYPES(A):return{_C:{_G:(_A,{_B:_M}),_Q:(_A,)}} + RETURN_TYPES=_H,;RETURN_NAMES=_F,;OUTPUT_NODE=_D;FUNCTION=_I;CATEGORY=_E def load(A,name,str,**B):return int(str), class RiceRoundFloatNode: def __init__(A):0 @classmethod - def INPUT_TYPES(A):return{_E:{_G:(_B,{_A:_M}),_X:(_J,{_A:.0}),'min':(_J,{_A:.0}),'max':(_J,{_A:1e2})}} - RETURN_TYPES=_J,;RETURN_NAMES=_F,;OUTPUT_NODE=_D;FUNCTION=_I;CATEGORY=_C + def INPUT_TYPES(A):return{_C:{_G:(_A,{_B:_M}),_X:(_J,{_B:.0}),'min':(_J,{_B:.0}),'max':(_J,{_B:1e2})}} + RETURN_TYPES=_J,;RETURN_NAMES=_F,;OUTPUT_NODE=_D;FUNCTION=_I;CATEGORY=_E def load(A,name,number,min,max,**B):return number, class RiceRoundStrToFloatNode: def __init__(A):0 @classmethod - def INPUT_TYPES(A):return{_E:{_G:(_B,{_A:_M}),_Q:(_B,)}} - RETURN_TYPES=_J,;RETURN_NAMES=_F,;OUTPUT_NODE=_D;FUNCTION=_I;CATEGORY=_C + def INPUT_TYPES(A):return{_C:{_G:(_A,{_B:_M}),_Q:(_A,)}} + RETURN_TYPES=_J,;RETURN_NAMES=_F,;OUTPUT_NODE=_D;FUNCTION=_I;CATEGORY=_E def load(A,name,str,**B):return float(str), class RiceRoundBooleanNode: @classmethod - def INPUT_TYPES(A):return{_E:{_G:(_B,{_A:'开关'}),_F:(_R,{_A:False})}} - RETURN_TYPES=_R,;RETURN_NAMES=_F,;FUNCTION='execute';CATEGORY=_C + def INPUT_TYPES(A):return{_C:{_G:(_A,{_B:'开关'}),_F:(_R,{_B:False})}} + RETURN_TYPES=_R,;RETURN_NAMES=_F,;FUNCTION='execute';CATEGORY=_E def execute(A,name,value):return value, class RiceRoundStrToBooleanNode: def __init__(A):0 @classmethod - def INPUT_TYPES(A):return{_E:{_G:(_B,{_A:'开关'}),_Q:(_B,)}} - RETURN_TYPES=_R,;RETURN_NAMES=_F,;OUTPUT_NODE=_D;FUNCTION=_I;CATEGORY=_C + def INPUT_TYPES(A):return{_C:{_G:(_A,{_B:'开关'}),_Q:(_A,)}} + RETURN_TYPES=_R,;RETURN_NAMES=_F,;OUTPUT_NODE=_D;FUNCTION=_I;CATEGORY=_E def load(A,name,str,**B):return str.lower()=='true', class RiceRoundInputTextNode: def __init__(A):0 @classmethod - def INPUT_TYPES(A):return{_E:{'text_info':(_B,{'multiline':_D,_O:'The text to be encoded.'})}} - RETURN_TYPES=_B,;OUTPUT_NODE=_D;FUNCTION=_I;CATEGORY=_C + def INPUT_TYPES(A):return{_C:{'text_info':(_A,{'multiline':_D,_O:'The text to be encoded.'})}} + RETURN_TYPES=_A,;OUTPUT_NODE=_D;FUNCTION=_I;CATEGORY=_E def load(D,text_info,**E): B=text_info;A='' try:C=json.loads(B);A=C.get('content','') @@ -137,14 +138,20 @@ class RiceRoundInputTextNode: class RiceRoundRandomSeedNode: def __init__(A):0 @classmethod - def INPUT_TYPES(A):return{_E:{},_K:{},_N:{}} - RETURN_TYPES=_H,;FUNCTION='random';CATEGORY=_C + def INPUT_TYPES(A):return{_C:{},_K:{},_N:{}} + RETURN_TYPES=_H,;FUNCTION='random';CATEGORY=_E @classmethod def IS_CHANGED(A):return random.randint(0,999999) def random(B):A=random.randint(0,999999);print('产生随机数 ',A);return A, class RiceRoundDebugNode: def __init__(A):0 @classmethod - def INPUT_TYPES(A):return{_E:{'text':(_B,{_A:'Hello, World!'})}} - RETURN_TYPES=_B,;FUNCTION='debug';CATEGORY=_C - def debug(A,text):print(f"RiceDebugNode text: {text}");return text, \ No newline at end of file + def INPUT_TYPES(A):return{_C:{'text':(_A,{_B:'Hello, World!'})}} + RETURN_TYPES=_A,;FUNCTION='debug';CATEGORY=_Y + def debug(A,text):print(f"RiceDebugNode text: {text}");return text, +class RiceRoundTempTokenNode: + def __init__(A):0 + @classmethod + def INPUT_TYPES(A):return{_C:{'text':(_A,{_B:''})}} + RETURN_TYPES=_A,;FUNCTION='temp_token';CATEGORY=_Y + def temp_token(A,text):return text, \ No newline at end of file diff --git a/rice_prompt_handler.py b/rice_prompt_handler.py index 7db3cf8..33970c9 100644 --- a/rice_prompt_handler.py +++ b/rice_prompt_handler.py @@ -1,5 +1,8 @@ -_A='inputs' +_C='inputs' +_B='class_type' +_A='prompt' import json,os,random,tempfile,time +from.auth_unit import AuthUnit from.utils import get_local_app_path from.rice_prompt_info import RicePromptInfo class RiceRoundPromptHandler: @@ -10,30 +13,36 @@ class RiceRoundPromptHandler: def __init__(A): if not A._initialized:A.client_id='';A.task_uuid='';A._initialized=True def onprompt_handler(B,json_data): - '\n 处理传入的 JSON 数据\n :param json_data: 输入的 JSON 数据,包含各种任务信息\n ';H='input';G='template';F='task_uuid';E='client_id';D='prompt';A=json_data;RicePromptInfo().clear() + '\n 处理传入的 JSON 数据\n :param json_data: 输入的 JSON 数据,包含各种任务信息\n ';G='input';F='template';E='task_uuid';D='client_id';A=json_data;RicePromptInfo().clear();B.search_and_set_temp_token(A) + if D not in A:return A + B.client_id=A[D] if E not in A:return A - B.client_id=A[E] - if F not in A:return A - B.task_uuid=A[F] - if D not in A:raise Exception("Warning: 'prompt' is missing.") - if G not in A:raise Exception("Warning: 'template' is missing.") - print(f"RiceRoundPromptHandler self.client_id={B.client_id!r} self.task_uuid={B.task_uuid!r}");I=A[H]if H in A else{};C=A[D];C=B.replace_output_prompt(C);J,K=B.parse_template(A[G]);C=B.replace_input_prompt(C,I,J,K);print(f"RiceRoundPromptHandler prompt_data={C!r}");A[D]=C;return A + B.task_uuid=A[E] + if _A not in A:raise Exception("Warning: 'prompt' is missing.") + if F not in A:raise Exception("Warning: 'template' is missing.") + print(f"RiceRoundPromptHandler self.client_id={B.client_id!r} self.task_uuid={B.task_uuid!r}");H=A[G]if G in A else{};C=A[_A];C=B.replace_output_prompt(C);I,J=B.parse_template(A[F]);C=B.replace_input_prompt(C,H,I,J);print(f"RiceRoundPromptHandler prompt_data={C!r}");A[_A]=C;return A + def search_and_set_temp_token(E,json_data): + D=json_data.get(_A,{});B='';C=None + for(F,A)in D.items(): + if A.get(_B)=='RiceRoundTempTokenNode':B=A.get(_C,{}).get('text','') + if A.get(_B)=='RiceRoundDecryptNode':C=A + if B and C:AuthUnit().set_temp_token(B) def parse_template(E,template_data): B={};C={};D=template_data['elements'] for A in D:id=A['id'];C[id]=A['node_id'];B[id]=A['type'] return B,C - def replace_output_prompt(D,prompt_data): - '\n 替换输出节点中的 task_id\n :param prompt_data: 任务的 prompt 数据\n :return: 替换后的 prompt 数据\n ';C='class_type';B=prompt_data - for(E,A)in B.items(): - if A.get(C)=='RiceRoundOutputImageNode':A[_A]['task_id']=D.task_uuid - elif A.get(C)=='RiceRoundRandomSeedNode':A[_A]['seed']=random.randint(0,999999) + def replace_output_prompt(C,prompt_data): + '\n 替换输出节点中的 task_id\n :param prompt_data: 任务的 prompt 数据\n :return: 替换后的 prompt 数据\n ';B=prompt_data + for(D,A)in B.items(): + if A.get(_B)=='RiceRoundOutputImageNode':A[_C]['task_id']=C.task_uuid + elif A.get(_B)=='RiceRoundRandomSeedNode':A[_C]['seed']=random.randint(0,999999) return B def replace_input_prompt(F,prompt_data,input_data,id_type_map,node_id_map): B='str';A=prompt_data;G={'text':'text_info','image_upload':'image_url','mask_upload':'mask_url','number_int':B,'number_float':B,'choice':'default','switch':B} for(C,H)in input_data.items(): D=id_type_map.get(C,'');E=G.get(D) if not E:print(f"RiceRoundPromptHandler replace_input_prompt unknown input_type {D}");continue - I=A[node_id_map[C]];I[_A][E]=str(H) + I=A[node_id_map[C]];I[_C][E]=str(H) if os.environ.get('DEBUG')=='true': J=tempfile.gettempdir() with open(f"{J}//{F.task_uuid}_prompt_data.json",'w')as K:json.dump(A,K,indent=4)