File size: 5,980 Bytes
173561a
 
 
 
 
a499cd3
 
173561a
 
 
 
 
a499cd3
 
173561a
 
 
 
 
 
 
a499cd3
173561a
a499cd3
173561a
 
 
 
 
 
 
 
 
 
8799348
 
a499cd3
8799348
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173561a
 
 
 
 
 
 
8799348
 
f19800f
8799348
 
 
 
 
173561a
8799348
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173561a
 
 
8799348
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a499cd3
 
 
 
 
 
 
 
 
 
 
 
8799348
a499cd3
 
 
 
8799348
 
 
85a6c18
a499cd3
8799348
 
173561a
 
 
 
a499cd3
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
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  

@app.post("/api/generate")
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
    }

@app.get("/api/latest")
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"])

@app.get("/api/image/{image_id}")
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")

@app.get("/api/history")
async def get_history():
    return read_history()

# --- [2. 恢復:原本的自訂網頁首頁與靜態檔案夾掛載] ---

@app.get("/")
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)