File size: 7,239 Bytes
a4157a2
a5ce452
a4157a2
 
 
 
 
 
 
 
 
a5ce452
 
 
 
a4157a2
 
 
a5ce452
a4157a2
 
a5ce452
a4157a2
 
a5ce452
 
 
 
 
a4157a2
 
bd711e3
a4157a2
 
bd711e3
e9f4bee
a4157a2
bd711e3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a4157a2
 
 
 
 
 
 
 
a5ce452
 
a4157a2
 
 
bd711e3
 
 
 
 
 
a4157a2
 
a5ce452
 
 
 
 
bd711e3
a5ce452
 
 
 
 
 
 
 
 
a4157a2
a5ce452
 
 
 
 
 
 
 
a4157a2
a5ce452
 
a4157a2
 
a5ce452
a4157a2
a5ce452
 
a4157a2
a5ce452
 
a4157a2
a5ce452
a4157a2
bd711e3
a4157a2
a5ce452
bd711e3
a5ce452
bd711e3
a5ce452
 
 
 
 
 
 
 
 
 
 
 
 
 
bd711e3
a5ce452
 
 
 
 
 
 
a4157a2
 
a5ce452
a4157a2
 
 
 
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
import os
import re
import gradio as ui
from huggingface_hub import HfApi, hf_hub_url

REPO_ID = "Crocody/mymuse"
REPO_TYPE = "dataset"
HF_TOKEN = os.getenv("HF_TOKEN") 

api = HfApi(token=HF_TOKEN)

def natural_sort_key(s):
    return [int(text) if text.isdigit() else text.lower() for text in re.split(r'(\d+)', s)]

def get_lora_samples(sort_desc=False):
    try:
        files = api.list_repo_files(repo_id=REPO_ID, repo_type=REPO_TYPE)
        samples = [f for f in files if f.startswith("LoRA_Samples/") and f.lower().endswith(('.png', '.jpg', '.jpeg'))]
        samples.sort(key=lambda x: natural_sort_key(os.path.basename(x)), reverse=sort_desc)
        return samples
    except Exception as e:
        print(f"ํŒŒ์ผ ๋กœ๋“œ ์‹คํŒจ: {e}")
        return []

def update_gallery_data(sort_desc=False):
    paths = get_lora_samples(sort_desc=sort_desc)
    gallery_val = [(hf_hub_url(repo_id=REPO_ID, repo_type=REPO_TYPE, filename=p), os.path.splitext(os.path.basename(p))[0]) for p in paths]
    return gallery_val, paths

def generate_download_links(image_path):
    if not image_path:
        return "<div style='color: #94a3b8; text-align: center; margin-top: 140px; font-size: 15px;'>์™ผ์ชฝ ๋ชฉ๋ก์—์„œ ๋กœ๋ผ ์นด๋“œ๋ฅผ ํด๋ฆญํ•˜๋ฉด<br>๋ฒ„์ „๋ณ„ ์ƒํƒœ ์ •๋ณด ๋ฐ ๋‹ค์šด๋กœ๋“œ ์ฐฝ์ด ํ™œ์„ฑํ™”๋ฉ๋‹ˆ๋‹ค.</div>"
    
    filename = os.path.splitext(os.path.basename(image_path))[0]
    img_url = hf_hub_url(repo_id=REPO_ID, repo_type=REPO_TYPE, filename=image_path)
    target_folders = ["Qwen2509", "Qwen2512", "Zit", "Krea2"]
    
    # 1. ์ƒ๋‹จ ์ด๋ฏธ์ง€ ๋ฐฐ์น˜ ๋ฐ 2. ํ•˜๋‹จ ํ…์ŠคํŠธ ๋‚ด์šฉ ์œ ๊ธฐ์  ๊ฒฐํ•ฉ (HTML ๋ ˆ์ด์•„์›ƒ)
    html_output = f"""

    <div style='display: flex; flex-direction: column; align-items: center; gap: 20px;'>

        <div style='width: 100%; max-width: 280px; aspect-ratio: 3/4; overflow: hidden; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);'>

            <img src='{img_url}' style='width: 100%; height: 100%; object-fit: cover;' alt='{filename} preview'/>

        </div>

        

        <div style='width: 100%;'>

            <h3 style='margin: 10px 0 5px 0; font-size: 18px; color: #1e293b; display: flex; align-items: center; gap: 6px;'>๐Ÿ“ฆ {filename} LoRA ๋‹ค์šด๋กœ๋“œ ๋งํฌ</h3>

            <table class='download-table'>

                <thead>

                    <tr><th>๋ฒ„์ „ / ํด๋”</th><th>๋‹ค์šด๋กœ๋“œ ๋งํฌ</th></tr>

                </thead>

                <tbody>

    """
    
    all_files = api.list_repo_files(repo_id=REPO_ID, repo_type=REPO_TYPE)
    
    for folder in target_folders:
        target_file = f"{folder}/{filename}.safetensors"
        match = [f for f in all_files if f.lower() == target_file.lower()]
        
        if match:
            download_url = hf_hub_url(repo_id=REPO_ID, repo_type=REPO_TYPE, filename=match[0])
            html_output += f"<tr><td><b>{folder}</b></td><td><a href='{download_url}' target='_blank' class='btn-download'>๋‹ค์šด๋กœ๋“œ ({filename}.safetensors)</a></td></tr>"
        else:
            html_output += f"<tr><td><b>{folder}</b></td><td style='color: #ef4444;'>ํŒŒ์ผ ์—†์Œ</td></tr>"
            
    html_output += """

                </tbody>

            </table>

        </div>

    </div>

    """
    return html_output

# --- ์Šคํƒ€์ผ์‹œํŠธ ---
custom_css = """

body, .gradio-container { background-color: #f4f6f9 !important; font-family: sans-serif !important; }

#lora-gallery { background: transparent !important; border: none !important; box-shadow: none !important; }

#lora-gallery .gallery-item { background: #ffffff !important; border-radius: 12px !important; border: 1px solid #e2e8f0 !important; padding: 14px !important; }

#info-panel { background: #ffffff !important; border-radius: 16px !important; border: 1px solid #e2e8f0 !important; padding: 24px !important; min-height: 500px; }

.download-table { width: 100%; border-collapse: separate; border-spacing: 0; margin-top: 15px; border-radius: 8px; overflow: hidden; border: 1px solid #e2e8f0; }

.download-table th { background-color: #f8fafc; color: #64748b; padding: 12px 16px; font-size: 14px; border-bottom: 1px solid #e2e8f0; }

.download-table td { padding: 14px 16px; font-size: 14px; border-bottom: 1px solid #e2e8f0; color: #334155; }

.btn-download { color: #2563eb !important; font-weight: 600; text-decoration: none; }

.btn-download:hover { text-decoration: underline; }

.nav-btn { font-weight: 600 !important; background: #ffffff !important; border: 1px solid #e2e8f0 !important; border-radius: 8px !important; }

"""

with ui.Blocks(css=custom_css) as demo:
    with ui.Row():
        ui.Markdown("# โœจ mymuse LoRA Management Center")
        with ui.Row(elem_id="action-bar"):
            btn_asc = ui.Button("โฌ‡๏ธ ์˜ค๋ฆ„์ฐจ์ˆœ", elem_classes="nav-btn", scale=1)
            btn_desc = ui.Button("โฌ†๏ธ ๋‚ด๋ฆผ์ฐจ์ˆœ", elem_classes="nav-btn", scale=1)
            btn_refresh = ui.Button("๐Ÿ”„ ์ƒˆ๋กœ๊ณ ์นจ", elem_classes="nav-btn", scale=1)

    with ui.Row():
        # ์™ผ์ชฝ ๊ทธ๋ฆฌ๋“œ ์˜์—ญ
        with ui.Column(scale=2):
            initial_paths = get_lora_samples(sort_desc=False)
            initial_gallery = [(hf_hub_url(repo_id=REPO_ID, repo_type=REPO_TYPE, filename=p), os.path.splitext(os.path.basename(p))[0]) for p in initial_paths]
            
            gallery = ui.Gallery(
                value=initial_gallery,
                columns=4, 
                object_fit="cover",
                height=750,
                allow_preview=False,
                show_label=False,
                elem_id="lora-gallery"
            )
            paths_holder = ui.State(value=initial_paths)

        # ์˜ค๋ฅธ์ชฝ ์ •๋ณด ์ถœ๋ ฅ ์˜์—ญ (์ƒ๋‹จ ์ด๋ฏธ์ง€ + ํ•˜๋‹จ ์ •๋ณด ๊ตฌ์กฐ ๋ฐ˜์˜)
        with ui.Column(scale=1):
            with ui.HTML(elem_id="info-panel") as output_area:
                output_area.value = "<div style='color: #94a3b8; text-align: center; margin-top: 180px; font-size: 15px;'>์™ผ์ชฝ ๋ชฉ๋ก์—์„œ ๋กœ๋ผ ์นด๋“œ๋ฅผ ํด๋ฆญํ•˜๋ฉด<br>๋ฒ„์ „๋ณ„ ์ƒํƒœ ์ •๋ณด ๋ฐ ๋‹ค์šด๋กœ๋“œ ์ฐฝ์ด ํ™œ์„ฑํ™”๋ฉ๋‹ˆ๋‹ค.</div>"

    # --- ์ด๋ฒคํŠธ ํ•ธ๋“ค๋ง ---
    btn_asc.click(
        fn=lambda: update_gallery_data(sort_desc=False),
        inputs=[],
        outputs=[gallery, paths_holder]
    )
    
    btn_desc.click(
        fn=lambda: update_gallery_data(sort_desc=True),
        inputs=[],
        outputs=[gallery, paths_holder]
    )
    
    def refresh_all():
        gallery_val, paths = update_gallery_data(sort_desc=False)
        reset_html = "<div style='color: #94a3b8; text-align: center; margin-top: 180px; font-size: 15px;'>์™ผ์ชฝ ๋ชฉ๋ก์—์„œ ๋กœ๋ผ ์นด๋“œ๋ฅผ ํด๋ฆญํ•˜๋ฉด<br>๋ฒ„์ „๋ณ„ ์ƒํƒœ ์ •๋ณด ๋ฐ ๋‹ค์šด๋กœ๋“œ ์ฐฝ์ด ํ™œ์„ฑํ™”๋ฉ๋‹ˆ๋‹ค.</div>"
        return gallery_val, paths, reset_html

    btn_refresh.click(
        fn=refresh_all,
        inputs=[],
        outputs=[gallery, paths_holder, output_area]
    )

    def on_select(evt: ui.SelectData, paths):
        return generate_download_links(paths[evt.index])

    gallery.select(fn=on_select, inputs=[paths_holder], outputs=[output_area])

demo.launch()