|
import pyperclip |
|
import os |
|
import io |
|
from PIL import Image |
|
from sd_parsers import ParserManager |
|
import gradio as gr |
|
import re |
|
import json |
|
|
|
|
|
parser_manager = ParserManager() |
|
|
|
TITLE = "<h1><center>Workflow Info Reader - By Andy N Le 0908 23 11 81 </center></h1>\n" |
|
|
|
def extract_lora_info(prompt): |
|
lora_pattern = r"<lora:(.*?):(.*?)>" |
|
loras = re.findall(lora_pattern, prompt) |
|
if not loras: |
|
return "Không xác định" |
|
lora_info = [f" {name} - Weight: {weight}" for name, weight in loras] |
|
return "\n".join(lora_info) |
|
|
|
def generate_workflow_from_metadata(metadata): |
|
nodes = [] |
|
connections = [] |
|
|
|
if 'nodes' in metadata: |
|
for node in metadata['nodes']: |
|
nodes.append({ |
|
'id': node['id'], |
|
'title': node.get('title', 'Unnamed Node'), |
|
'pos': node.get('pos', [0, 0]), |
|
'type': node.get('type', 'basic/node') |
|
}) |
|
|
|
if 'connections' in metadata: |
|
for conn in metadata['connections']: |
|
connections.append({ |
|
'from': conn['from'], |
|
'to': conn['to'] |
|
}) |
|
|
|
return { |
|
'nodes': nodes, |
|
'connections': connections |
|
} |
|
|
|
def format_sampler_params(sampler_params): |
|
try: |
|
params = json.loads(sampler_params) |
|
except (json.JSONDecodeError, TypeError): |
|
return "Không xác định" |
|
|
|
scheduler = params.get("scheduler", "Không xác định") |
|
cfg_scale = params.get("cfg_scale", "Không xác định") |
|
steps = params.get("steps", "Không xác định") |
|
return f"scheduler: {scheduler}\nCFG: {cfg_scale}\nSteps: {steps}" |
|
|
|
download_file = gr.File(label="Tải file đã tạo", visible=False) |
|
|
|
def save_metadata_to_file(image_input, prompt, neg_prompt, model, loras, seed, sampler, sampler_params, metadata): |
|
if not any([prompt, neg_prompt, model, loras, seed, sampler, sampler_params, metadata]): |
|
return None, gr.Info("Không có thông tin để lưu!", duration=2) |
|
if image_input is None: |
|
return None, gr.Info("Không thể lưu tệp: Không có ảnh nào được tải lên.", duration=2) |
|
|
|
|
|
image_filename = os.path.basename(image_input) |
|
txt_filename = os.path.splitext(image_filename)[0] + ".txt" |
|
txt_filepath = os.path.join("outputs", txt_filename) |
|
|
|
|
|
with open(txt_filepath, "w", encoding="utf-8") as f: |
|
f.write(f"Prompt: {prompt}\n") |
|
f.write(f"Negative Prompt: {neg_prompt}\n") |
|
f.write(f"Model: {model}\n") |
|
f.write(f"Loras: {loras}\n") |
|
f.write(f"Seed: {seed}\n") |
|
f.write(f"Sampler: {sampler}\n") |
|
f.write(f"Sampler Parameters: {sampler_params}\n") |
|
f.write(f"Other Metadata:\n{metadata}\n") |
|
|
|
return txt_filepath, gr.Info(f"Đã lưu thông tin vào file: {txt_filename}", duration=2) |
|
|
|
|
|
|
|
def read_image_metadata(image_path): |
|
try: |
|
if not image_path: |
|
raise ValueError("Không có ảnh được tải lên.") |
|
|
|
with Image.open(image_path) as img: |
|
prompt_info = parser_manager.parse(img) |
|
|
|
if not prompt_info: |
|
raise ValueError("Không thể đọc thông tin từ ảnh.") |
|
|
|
|
|
if prompt_info.generator == "AUTOMATIC1111": |
|
return handle_automatic1111(prompt_info) |
|
elif prompt_info.generator == "ComfyUI": |
|
return handle_comfyui(prompt_info) |
|
else: |
|
raise ValueError("Loại generator không được hỗ trợ.") |
|
|
|
except Exception as e: |
|
print(f"Lỗi khi xử lý ảnh: {str(e)}") |
|
return "Không tìm thấy thông tin!", "", "", "", "", "", "", "" |
|
|
|
def handle_automatic1111(prompt_info): |
|
prompt = prompt_info.full_prompt |
|
negative_prompt = prompt_info.full_negative_prompt if prompt_info.full_negative_prompt else "N/A" |
|
|
|
models_list = list(prompt_info.models) if isinstance(prompt_info.models, set) else prompt_info.models |
|
model = models_list[0].name if models_list else "Không xác định" |
|
|
|
loras = extract_lora_info(prompt) |
|
|
|
samplers_list = list(prompt_info.samplers) if isinstance(prompt_info.samplers, set) else prompt_info.samplers |
|
if samplers_list and len(samplers_list) > 0: |
|
sampler = samplers_list[0].name if samplers_list[0].name else "N/A" |
|
sampler_params = json.dumps(samplers_list[0].parameters) if samplers_list[0].parameters else "N/A" |
|
seed = samplers_list[0].parameters.get('seed', 'Không xác định') |
|
else: |
|
sampler = "N/A" |
|
sampler_params = "N/A" |
|
seed = "Không xác định" |
|
|
|
formatted_sampler_params = format_sampler_params(sampler_params) |
|
|
|
other_metadata = "\n".join([ |
|
f"{key}: {value}" for key, value in prompt_info.metadata.items() |
|
if isinstance(key, str) and not key.startswith("Module") |
|
]) |
|
|
|
return prompt, negative_prompt, model, loras, seed, sampler, formatted_sampler_params, other_metadata |
|
|
|
def format_metadata(metadata): |
|
formatted_output = "" |
|
for key, value in metadata.items(): |
|
node_title, node_id = key |
|
formatted_output += f"Node '{node_title}'\n" |
|
for prop_key, prop_value in value.items(): |
|
formatted_output += f" {prop_key}: {prop_value}\n" |
|
return formatted_output |
|
|
|
def handle_comfyui(prompt_info): |
|
prompt = prompt_info.full_prompt or "Không xác định" |
|
negative_prompt = prompt_info.full_negative_prompt if prompt_info.full_negative_prompt else "N/A" |
|
|
|
models_list = list(prompt_info.models) if isinstance(prompt_info.models, set) else prompt_info.models |
|
model = models_list[0].name if models_list else "Không xác định" |
|
|
|
samplers_list = list(prompt_info.samplers) if isinstance(prompt_info.samplers, set) else prompt_info.samplers |
|
if samplers_list and len(samplers_list) > 0: |
|
sampler = samplers_list[0].name if samplers_list[0].name else "N/A" |
|
sampler_params = json.dumps(samplers_list[0].parameters) if samplers_list[0].parameters else "N/A" |
|
seed = samplers_list[0].parameters.get('seed', 'Không xác định') |
|
else: |
|
sampler = "N/A" |
|
sampler_params = "N/A" |
|
seed = "Không xác định" |
|
|
|
formatted_sampler_params = format_sampler_params(sampler_params) |
|
|
|
|
|
converted_metadata = {} |
|
for key, value in prompt_info.metadata.items(): |
|
if isinstance(key, tuple): |
|
key = str(key) |
|
converted_metadata[key] = value |
|
|
|
other_metadata = format_metadata(prompt_info.metadata) |
|
|
|
if not other_metadata: |
|
other_metadata = "Không có metadata bổ sung." |
|
|
|
return prompt, negative_prompt, model, None, seed, sampler, formatted_sampler_params, other_metadata |
|
|
|
output_dir = "outputs" |
|
if not os.path.exists(output_dir): |
|
os.mkdir(output_dir) |
|
|
|
def copy_to_clipboard(prompt, neg_prompt, seed, copy_prompt, copy_neg_prompt, copy_seed): |
|
copied_text = "" |
|
if copy_prompt: |
|
copied_text += f"Prompt: {prompt}\n" |
|
if copy_neg_prompt: |
|
copied_text += f"Negative Prompt: {neg_prompt}\n" |
|
if copy_seed: |
|
copied_text += f"Seed: {seed}\n" |
|
|
|
if copied_text: |
|
try: |
|
pyperclip.copy(copied_text) |
|
return gr.Info("Sao chép thành công!", duration=2) |
|
except pyperclip.PyperclipException: |
|
return gr.Info("Không hỗ trợ tính năng này trong bản demo. Vui lòng liên hệ tác giả để biết chi tiết !", duration=2) |
|
else: |
|
return gr.Info("Không có gì để sao chép!", duration=2) |
|
|
|
|
|
|
|
def cancel_copy(): |
|
try: |
|
pyperclip.copy("") |
|
return ( |
|
gr.update(value=False), |
|
gr.update(value=False), |
|
gr.update(value=False), |
|
gr.Info("Đã hủy bỏ sao chép!", duration=2) |
|
) |
|
except pyperclip.PyperclipException: |
|
|
|
return ( |
|
gr.update(value=False), |
|
gr.update(value=False), |
|
gr.update(value=False), |
|
gr.Info("Hệ thống không hỗ trợ chức năng sao chép tự động.", duration=2) |
|
) |
|
|
|
|
|
output_dir = "outputs" |
|
if not os.path.exists(output_dir): |
|
os.mkdir(output_dir) |
|
|
|
|
|
|
|
|
|
|
|
def check_image_size(image_input): |
|
try: |
|
if not image_input: |
|
raise ValueError("Không có ảnh được tải lên.") |
|
|
|
with Image.open(image_input) as img: |
|
width, height = img.size |
|
if width > 5000 or height > 5000: |
|
raise ValueError("Kích thước ảnh vượt quá 5000 px ở chiều ngang hoặc chiều dọc.") |
|
return image_input, gr.Info("Ảnh hợp lệ.", duration=1) |
|
except Exception as e: |
|
return None, gr.Info(f"Lỗi khi xử lý ảnh: {str(e)}", duration=2) |
|
|
|
|
|
def gradio_interface(image_input): |
|
try: |
|
|
|
prompt, negative_prompt, model, loras, seed, sampler, formatted_sampler_params, other_metadata = read_image_metadata(image_input) |
|
|
|
|
|
if prompt == "Không thể đọc thông tin" or not any([prompt, negative_prompt, model, seed]): |
|
raise ValueError("Thiếu dữ liệu khi xử lý ảnh.") |
|
|
|
return prompt, negative_prompt, model, loras, seed, sampler, formatted_sampler_params, other_metadata |
|
except Exception as e: |
|
|
|
print(f"Lỗi trong gradio_interface: {str(e)}") |
|
return "Không tìm thấy thông tin ", "", "", "", "", "", "", "" |
|
|
|
|
|
js_func = """ |
|
function refresh() { |
|
const url = new URL(window.location); |
|
|
|
if (url.searchParams.get('__theme') !== 'dark') { |
|
url.searchParams.set('__theme', 'dark'); |
|
window.location.href = url.href; |
|
} |
|
} |
|
|
|
""" |
|
with gr.Blocks(js = js_func) as demo: |
|
gr.HTML(TITLE) |
|
with gr.Row(): |
|
with gr.Column(): |
|
image_input = gr.Image(type="filepath", label="Tải lên hình ảnh") |
|
read_button = gr.Button("Đọc thông tin") |
|
copy_prompt = gr.Checkbox(label="Sao chép lời mô tả", value=False) |
|
copy_neg_prompt = gr.Checkbox(label="Sao chép mô tả loại trừ", value=False) |
|
copy_seed = gr.Checkbox(label="Sao Chép Seed", value=False) |
|
with gr.Row(): |
|
copy_button = gr.Button("Sao chép") |
|
cancel_button = gr.Button("Hủy sao chép") |
|
download_button = gr.Button("Lưu file .txt") |
|
message_output = gr.HTML() |
|
|
|
|
|
with gr.Column(): |
|
prompt_output = gr.Textbox(label="Lời mô tả (prompt)") |
|
negative_prompt_output = gr.Textbox(label="Mô tả loại trừ (Negative Prompt)") |
|
model_output = gr.Textbox(label="Mô hình (Model)") |
|
lora_output = gr.Textbox(label="Lora (Tên & Trọng số)") |
|
seed_output = gr.Textbox(label="Seed") |
|
sampler_output = gr.Textbox(label="Phương pháp lấy mẫu") |
|
sampler_params_output = gr.Textbox(label="Thông số lấy mẫu") |
|
other_metadata_output = gr.Textbox(label="Thông tin khác", lines=10) |
|
download_file = gr.File(label="Tải file đã tạo", visible=False) |
|
|
|
|
|
read_button.click( |
|
fn=check_image_size, |
|
inputs=image_input, |
|
outputs=[image_input, message_output], |
|
show_progress=False |
|
) |
|
|
|
|
|
read_button.click( |
|
fn=gradio_interface, |
|
inputs=image_input, |
|
outputs=[prompt_output, negative_prompt_output, model_output, lora_output, seed_output, sampler_output, sampler_params_output, other_metadata_output] |
|
) |
|
copy_button.click( |
|
copy_to_clipboard, |
|
inputs=None, |
|
outputs=message_output, |
|
show_progress=False |
|
) |
|
|
|
|
|
cancel_button.click( |
|
cancel_copy, |
|
inputs=None, |
|
outputs=message_output, |
|
show_progress=False |
|
) |
|
def notify_download(): |
|
return gr.Info("Không hỗ trợ tính năng này trong bản demo. Vui lòng liên hệ tác giả để biết chi tiết!", duration=3) |
|
|
|
|
|
download_button.click( |
|
fn=notify_download, |
|
inputs=None, |
|
outputs=message_output, |
|
show_progress=False |
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
demo.launch(inbrowser=True) |
|
|