File size: 13,143 Bytes
3a7c74e
 
5a5897a
3a7c74e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
416f8cd
 
40c50e8
 
075ab3a
 
 
 
416f8cd
665635e
 
075ab3a
665635e
416f8cd
 
 
 
 
 
 
 
 
 
 
075ab3a
3a7c74e
 
bace505
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3a7c74e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cb69982
 
 
 
4e28f4d
3a7c74e
 
 
cb69982
3a7c74e
 
cb69982
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3a7c74e
4e28f4d
 
 
 
3a7c74e
69180d0
3a7c74e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69180d0
b99b373
48a7e1f
3a7c74e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ad27445
7bf7b07
ad27445
 
3a7c74e
ad27445
 
 
97fe1c9
ad27445
 
69180d0
7958529
3a7c74e
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
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)
    
    # Lấy tên file ảnh và đặt tên cho file txt
    image_filename = os.path.basename(image_input)
    txt_filename = os.path.splitext(image_filename)[0] + ".txt"
    txt_filepath = os.path.join("outputs", txt_filename)

    # Ghi thông tin metadata vào file txt
    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.")

            # Kiểm tra loại generator
            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)


# Hàm hủy bỏ sao chép
def cancel_copy():
    try:
        pyperclip.copy("")  # Xóa clipboard
        return (
            gr.update(value=False),  # Uncheck all checkboxes
            gr.update(value=False),
            gr.update(value=False),
            gr.Info("Đã hủy bỏ sao chép!", duration=2)
        )
    except pyperclip.PyperclipException:
        # Nếu không thể sử dụng clipboard trên hệ thống, hiển thị thông báo
        return (
            gr.update(value=False),  # Uncheck all checkboxes
            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:
        # Đọc dữ liệu từ ảnh
        prompt, negative_prompt, model, loras, seed, sampler, formatted_sampler_params, other_metadata = read_image_metadata(image_input)
        
        # Kiểm tra xem dữ liệu có bị thiếu không cho từng trường hợp
        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:
        # In ra lỗi và trả về nội dung lỗi
        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
    )

    # Sau khi kiểm tra kích thước, nếu hợp lệ, đọc metadata từ ảnh
    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=[prompt_output, negative_prompt_output, seed_output, copy_prompt, copy_neg_prompt, copy_seed], 
        outputs=message_output
    )

    # Gắn sự kiện click cho nút hủy sao chép
    cancel_button.click(
        cancel_copy, 
        inputs=None, 
        outputs=[copy_prompt, copy_neg_prompt, copy_seed, message_output]
    )
    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)

    # Gắn sự kiện click cho nút download
    download_button.click(
        fn=notify_download,
        inputs=None,  # Không cần inputs
        outputs=message_output  # Chỉ hiển thị thông báo
        show_progress=False
    )






if __name__ == "__main__":
    demo.launch(inbrowser=True)