svg / app.py
openfree's picture
Update app.py
6c2404b verified
raw
history blame
6 kB
import gradio as gr
from datasets import load_dataset
from rapidfuzz import process, fuzz
# ──────────────────────────────────────────────────────────
# 1) 데이터셋 로드 (스트리밍) ─ 메모리에 메타만 저장
# ──────────────────────────────────────────────────────────
ds = load_dataset(
"nyuuzyou/clker-svg",
split="train",
streaming=True, # .jsonl.zst → 자동 스트리밍 해제
)
records = []
for ex in ds:
records.append(
{
"id": ex["id"],
"title": ex["title"] or "",
"tags": " ".join(ex["tags"] or []),
"svg": ex["svg_content"],
"url": ex["download_url"],
}
)
# ──────────────────────────────────────────────────────────
# 2) 검색 함수
# ──────────────────────────────────────────────────────────
def search_svg(query: str, top_k: int):
if not query.strip():
return "⚠️ 검색어를 입력하세요.", None
# choices: index(int) ➜ title+tags 한 줄 문자열
choices = {i: f"{r['title']} {r['tags']}" for i, r in enumerate(records)}
# Rapidfuzz: (choice_text, score, key) 반환
matched = process.extract(
query,
choices,
scorer=fuzz.WRatio,
limit=int(top_k),
)
html_snippets = []
html_start = '<div class="gallery-grid">'
html_end = '</div>'
for _, score, idx in matched: # idx 가 실제 list 인덱스
r = records[idx]
svg_html = (
'<div class="gallery-item">'
f'<div class="svg-container">{r["svg"]}</div>'
f'<div class="item-details">'
f'<h3>{r["title"]}</h3>'
f'<div class="score">매칭 점수: {score}</div>'
f'<div class="tags">{r["tags"]}</div>'
f'<a href="{r["url"]}" target="_blank" class="download-link">원본 다운로드</a>'
f'</div>'
'</div>'
)
html_snippets.append(svg_html)
if not html_snippets:
return "검색 결과가 없습니다.", None
return "", html_start + ''.join(html_snippets) + html_end
# ──────────────────────────────────────────────────────────
# 3) Gradio UI
# ──────────────────────────────────────────────────────────
TITLE = "🔍 Clker SVG"
DESCRIPTION = """
이 애플리케이션은 "nyuuzyou/clker-svg" 데이터셋을 사용하여 퍼블릭 도메인 SVG 클립아트를 빠르게 검색할 수 있습니다.
퍼지 매칭을 통해 제목과 태그에서 유사한 항목을 찾아 시각적 갤러리로 표시합니다.
"""
DISCORD_BADGE = """<p style="text-align:center; margin-top: -10px;"><a href="https://discord.gg/openfreeai" target="_blank"> <img src="https://img.shields.io/static/v1?label=Discord&message=Openfree%20AI&color=%230000ff&labelColor=%23800080&logo=discord&logoColor=white&style=for-the-badge" alt="badge"></a></p>"""
CSS = """
.gallery-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 20px;
margin-top: 20px;
}
.gallery-item {
border: 1px solid #ddd;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
transition: transform 0.3s;
background: white;
}
.gallery-item:hover {
transform: translateY(-5px);
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
}
.svg-container {
height: 180px;
display: flex;
align-items: center;
justify-content: center;
padding: 10px;
background: #f9f9f9;
}
.svg-container svg {
max-width: 100%;
max-height: 160px;
}
.item-details {
padding: 15px;
}
.item-details h3 {
margin: 0 0 10px 0;
font-size: 16px;
color: #333;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.score {
font-size: 14px;
color: #666;
margin-bottom: 5px;
}
.tags {
font-size: 12px;
color: #888;
margin-bottom: 10px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.download-link {
display: inline-block;
padding: 5px 10px;
background: #4a90e2;
color: white;
text-decoration: none;
border-radius: 4px;
font-size: 12px;
}
.download-link:hover {
background: #3a7bc8;
}
"""
with gr.Blocks(title=TITLE, css=CSS) as demo:
gr.Markdown(f"# {TITLE}")
gr.Markdown(DESCRIPTION)
gr.HTML(DISCORD_BADGE)
with gr.Row():
with gr.Column(scale=4):
query_box = gr.Textbox(
label="검색어",
placeholder="예: cat, tree, house, computer, flower...",
show_label=True
)
with gr.Column(scale=1):
top_slider = gr.Slider(
minimum=1,
maximum=50,
value=12,
step=3,
label="결과 개수"
)
with gr.Row():
search_button = gr.Button("검색", variant="primary")
warning_md = gr.Markdown()
output_html = gr.HTML()
search_button.click(
fn=search_svg,
inputs=[query_box, top_slider],
outputs=[warning_md, output_html],
)
query_box.submit(
fn=search_svg,
inputs=[query_box, top_slider],
outputs=[warning_md, output_html],
)
if __name__ == "__main__":
demo.launch()