Spaces:
Runtime error
Runtime error
| import os | |
| import json | |
| import base64 | |
| import logging | |
| from datetime import datetime | |
| from fastapi import FastAPI, HTTPException | |
| from fastapi.responses import FileResponse | |
| from fastapi.staticfiles import StaticFiles | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from pydantic import BaseModel | |
| from typing import Optional | |
| import gradio as gr | |
| # 載入自訂的生圖與後製模組 | |
| from image_generator import generate_good_morning_image | |
| logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s") | |
| logger = logging.getLogger(__name__) | |
| GEMINI_API_KEY = os.environ.get("GOOGLE_API_KEY") | |
| HF_TOKEN = os.environ.get("HF_TOKEN") | |
| app = FastAPI(title="長輩圖產生器整合版") | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| DATA_DIR = os.path.abspath("data") | |
| STATIC_DIR = os.path.abspath("static") | |
| HISTORY_FILE = os.path.join(DATA_DIR, "history.json") | |
| os.makedirs(DATA_DIR, exist_ok=True) | |
| os.makedirs(STATIC_DIR, exist_ok=True) | |
| def read_history(): | |
| if not os.path.exists(HISTORY_FILE): | |
| return [] | |
| try: | |
| with open(HISTORY_FILE, "r", encoding="utf-8") as f: | |
| return json.load(f) | |
| except Exception as e: | |
| logger.error(f"讀取歷史紀錄時發生錯誤: {e}") | |
| return [] | |
| def write_history(history_data): | |
| try: | |
| with open(HISTORY_FILE, "w", encoding="utf-8") as f: | |
| json.dump(history_data, f, ensure_ascii=False, indent=2) | |
| except Exception as e: | |
| logger.error(f"寫入歷史紀錄時發生錯誤: {e}") | |
| # --- [1. 原本的 FastAPI API 功能保留] --- | |
| class GenerateRequest(BaseModel): | |
| message: str | |
| custom_greeting: Optional[str] = None | |
| async def generate_endpoint(payload: GenerateRequest): | |
| logger.info(f"收到生圖命令請求: {payload.message}") | |
| # 直接呼叫,生圖模組會自動去抓你設定在 Hugging Face Secrets 裡的安全金鑰 | |
| result = generate_good_morning_image( | |
| user_input=payload.message, | |
| custom_greeting=payload.custom_greeting | |
| ) | |
| if not result.get("success"): | |
| raise HTTPException(status_code=500, detail=result.get("error", "早安圖流水線運行崩潰")) | |
| image_path = result["image_path"] | |
| try: | |
| with open(image_path, "rb") as img_file: | |
| base64_data = base64.b64encode(img_file.read()).decode("utf-8") | |
| base64_uri = f"data:image/jpeg;base64,{base64_data}" | |
| except Exception as e: | |
| logger.error(f"圖檔轉碼為 Base64 時發生錯誤: {e}") | |
| base64_uri = "" | |
| history = read_history() | |
| new_entry = { | |
| "id": result["id"], | |
| "query": result["query"], | |
| "greeting_text": result["greeting_text"], | |
| "expanded_prompt": result["expanded_prompt"], | |
| "filename": result["filename"], | |
| "created_at": datetime.now().isoformat() | |
| } | |
| history.insert(0, new_entry) | |
| write_history(history) | |
| return { | |
| "success": True, | |
| "id": result["id"], | |
| "query": result["query"], | |
| "greeting_text": result["greeting_text"], | |
| "expanded_prompt": result["expanded_prompt"], | |
| "image_url": f"/api/image/{result['id']}", | |
| "image_base64": base64_uri | |
| } | |
| async def get_latest_image(): | |
| history = read_history() | |
| if not history: | |
| raise HTTPException(status_code=404, detail="目前歷史資料庫為空,請先生成一張圖片。") | |
| latest = history[0] | |
| image_path = os.path.join(DATA_DIR, latest["filename"]) | |
| if not os.path.exists(image_path): | |
| raise HTTPException(status_code=404, detail="圖片成品檔案已不存在。") | |
| return FileResponse(image_path, media_type="image/jpeg", filename=latest["filename"]) | |
| async def get_image_by_id(image_id: str): | |
| image_path = os.path.join(DATA_DIR, f"{image_id}.jpg") | |
| if not os.path.exists(image_path): | |
| raise HTTPException(status_code=404, detail="找不到指定 ID 的圖片成品") | |
| return FileResponse(image_path, media_type="image/jpeg", filename=f"{image_id}.jpg") | |
| async def get_history(): | |
| return read_history() | |
| # --- [2. 恢復:原本的自訂網頁首頁與靜態檔案夾掛載] --- | |
| async def root(): | |
| index_path = os.path.join(STATIC_DIR, "index.html") | |
| if os.path.exists(index_path): | |
| return FileResponse(index_path) | |
| return {"message": "伺服器成功啟動!請將 index.html 放入 static 目錄下以開啟模擬器介面。"} | |
| # 掛載 static 靜態檔案夾,讓 index.html 可以順利讀取 style.css 與 app.js | |
| app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static") | |
| # --- [3. Gradio 後台測試介面 - 改掛載到 /gradio] --- | |
| def gradio_test_interface(user_query): | |
| if not user_query: | |
| return None, "請輸入主題" | |
| result = generate_good_morning_image(user_input=user_query) | |
| if result["success"]: | |
| return result["image_path"], result["greeting_text"] | |
| else: | |
| return None, f"生成失敗: {result.get('error')}" | |
| demo = gr.Interface( | |
| fn=gradio_test_interface, | |
| inputs=gr.Textbox(label="輸入圖片主題 (例如:貓咪、蓮花)", placeholder="想看什麼主題的長輩圖?"), | |
| outputs=[ | |
| gr.Image(label="生成的早安圖成品"), | |
| gr.Textbox(label="生成的賀詞") | |
| ], | |
| title="🌸 超俗早安長輩圖產生器 (後台測試版) 🌸", | |
| description="這是獨立的測試專區。若要造訪原本跑的模擬器,請直接前往主網頁路徑。", | |
| flagging_mode="never" | |
| ) | |
| # 關鍵:將 path 設定為 "/gradio" | |
| app = gr.mount_gradio_app(app, demo, path="/gradio") | |
| if __name__ == "__main__": | |
| import uvicorn | |
| port = int(os.environ.get("PORT", 7860)) | |
| uvicorn.run(app, host="0.0.0.0", port=port) |