import random import requests from PIL import Image from io import BytesIO import gradio as gr # --- 상수 및 설정 --- MEDIA_TYPE_TO_API = { "none": None, "illustrations": "illustration", "photos": "photo", "vectors": "vector" } DEFAULT_LANGUAGE = "ko" # --- 백엔드 함수 --- def get_best_image_url(hit): """최상의 화질 순서대로 이미지 URL을 반환""" return hit.get("fullHDURL") or hit.get("imageURL") or hit.get("largeImageURL") or hit.get("webformatURL") def fetch_image(url): """URL에서 이미지를 불러와 PIL 객체로 반환""" try: response = requests.get(url) response.raise_for_status() return Image.open(BytesIO(response.content)) except: return None def pixabay_search(api_key, media_type, keyword, width, height): """API 호출, 이미지 검색 및 리사이즈를 한 번에 처리""" if not api_key or not keyword: return [], None params = {"key": api_key, "q": keyword, "lang": DEFAULT_LANGUAGE, "per_page": 200} if media_type != "none": params["image_type"] = MEDIA_TYPE_TO_API.get(media_type) try: response = requests.get("https://pixabay.com/api/", params=params) response.raise_for_status() data = response.json() if not data.get("hits"): return [], None image_urls = [get_best_image_url(hit) for hit in data["hits"] if get_best_image_url(hit)] if not image_urls: return [], None url = random.choice(image_urls) img = fetch_image(url) if img and width and height and int(width) > 0 and int(height) > 0: img = img.resize((int(width), int(height))) return image_urls, img except (requests.exceptions.RequestException, Exception): return [], None def pixabay_next_image(current_images, width, height): """저장된 목록에서 다음 이미지를 가져와 리사이즈""" if not current_images: return None url = random.choice(current_images) img = fetch_image(url) if isinstance(width, int) and isinstance(height, int) and width > 0 and height > 0: img = img.resize((int(width), int(height))) return img # --- Gradio 인터페이스 구성 --- with gr.Blocks(title="Pixabay 이미지 검색기") as demo: gr.Markdown("# Pixabay 이미지 검색 데모") # 이미지 URL 목록을 저장하는 보이지 않는 상태 변수 image_list_state = gr.State([]) with gr.Row(): with gr.Column(scale=1): # --- 1. 검색 기능 그룹 --- gr.Markdown("### 1. 이미지 검색") api_key_input = gr.Textbox(label="Pixabay API Key", type="password") media_type_input = gr.Radio(choices=["none", "illustrations", "photos", "vectors"], label="미디어 유형", value="none") keyword_input = gr.Textbox(label="검색 키워드") # --- 2. 리사이즈 옵션 --- with gr.Accordion("이미지 리사이즈 (선택 사항)", open=False): with gr.Row(): width_input = gr.Number(label="가로(px)", step=1, value=None) height_input = gr.Number(label="세로(px)", step=1, value=None) with gr.Row(): search_btn = gr.Button("검색") next_btn = gr.Button("다른 이미지 보기") with gr.Column(scale=2): # 📌 핵심: show_download_button=True 로 이미지 자체 다운로드 버튼 활성화 image_output = gr.Image(type="pil", label="결과 이미지", height=600, show_download_button=True) # --- 버튼 이벤트 핸들러 --- search_btn.click( fn=pixabay_search, inputs=[api_key_input, media_type_input, keyword_input, width_input, height_input], outputs=[image_list_state, image_output] ) next_btn.click( fn=pixabay_next_image, inputs=[image_list_state, width_input, height_input], outputs=[image_output] ) # Gradio 앱 실행 if __name__ == "__main__": demo.launch()