Enka-Api / app.py
Teddpathhh's picture
Upload 3 files
08bf391 verified
Raw
History Blame Contribute Delete
5.51 kB
from fastapi import FastAPI, HTTPException
from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles
from enkacard import encbanner
import asyncio
import os
import re
import time
from pathlib import Path
import httpx
app = FastAPI(title="Genshin Enka API")
OUTDIR = Path("output")
OUTDIR.mkdir(exist_ok=True)
app.mount("/static", StaticFiles(directory=OUTDIR), name="static")
# JSON cache memory
JSON_CACHE = {}
CACHE_EXPIRE = 300 # 5 menit
# Enka data cache
ENKA_CACHE = {}
ENKA_CACHE_EXPIRE = 300 # 5 menit
# Image cache expire
IMAGE_EXPIRE = 3600 # 1 jam
def safe_name(s: str):
return re.sub(r'[^A-Za-z0-9_.-]', '_', str(s))
# --- Cleanup old images ---
def cleanup_images():
now = time.time()
for f in OUTDIR.glob("*.png"):
if now - f.stat().st_mtime > IMAGE_EXPIRE:
try:
f.unlink()
except:
pass
# --- JSON cache helpers ---
def get_json_cache(uid):
if uid in JSON_CACHE:
data, ts = JSON_CACHE[uid]
if time.time() - ts < CACHE_EXPIRE:
return data
return None
def save_json_cache(uid, data):
JSON_CACHE[uid] = (data, time.time())
# --- Enka cache helpers ---
def get_enka_cache(uid):
if uid in ENKA_CACHE:
data, ts = ENKA_CACHE[uid]
if time.time() - ts < ENKA_CACHE_EXPIRE:
return data
return None
def save_enka_cache(uid, data):
ENKA_CACHE[uid] = (data, time.time())
# --- Fetch Enka API ---
async def fetch_enka(uid):
cached = get_enka_cache(uid)
if cached:
return cached
url = f"https://enka.network/api/uid/{uid}"
async with httpx.AsyncClient(timeout=20) as client:
r = await client.get(url)
if r.status_code != 200:
return None
data = r.json()
save_enka_cache(uid, data)
return data
# --- Update enka data background ---
async def background_update():
try:
print("Updating Enka data...")
await encbanner.update()
print("Update selesai")
except Exception as e:
print("Update gagal:", e)
@app.on_event("startup")
async def startup_event():
asyncio.create_task(background_update())
# --- Generate banner ---
async def make_banner(uid: str):
cleanup_images()
existing = list(OUTDIR.glob(f"banner_{uid}_*.png"))
if existing:
return existing
async with encbanner.ENC(uid=uid) as encard:
result = await encard.creat()
if not result or not getattr(result, "card", None):
return None
saved_files = []
for i, card in enumerate(result.card, start=1):
fname = OUTDIR / f"banner_{uid}_{i}_{safe_name(card.name)}.png"
card.card.save(fname)
saved_files.append(fname)
return saved_files
# --- Generate profile ---
async def make_profile(uid: str):
cleanup_images()
existing = list(OUTDIR.glob(f"profile_{uid}_*.png"))
if existing:
return existing[0]
try:
async with encbanner.ENC(uid=uid) as encard:
result = await encard.profile(card=True)
if not result or not getattr(result, "card", None):
raise Exception("Profile card missing")
fname = OUTDIR / f"profile_{uid}_{safe_name(result.player.name)}.png"
result.card.save(fname)
return fname
except Exception as e:
print("Avatar error fallback:", e)
# fallback traveller
fallback = OUTDIR / "traveller.png"
if fallback.exists():
return fallback
return None
@app.get("/")
async def root():
return {"status": "Citedd -- API aktif "}
# --- JSON API ---
@app.get("/api/genshin/{uid}")
async def genshin(uid: str):
cached = get_json_cache(uid)
if cached:
return cached
try:
banners = await make_banner(uid)
profile = await make_profile(uid)
enka_data = await fetch_enka(uid)
if not banners:
raise HTTPException(404, "UID tidak ditemukan / private")
# default values
nickname = None
ar = None
abyss = None
if enka_data:
p = enka_data.get("playerInfo", {})
nickname = p.get("nickname")
ar = p.get("level")
floor = p.get("towerFloorIndex")
level = p.get("towerLevelIndex")
if floor and level:
abyss = f"{floor}-{level}"
result = {
"uid": uid,
"nickname": nickname,
"adventure_rank": ar,
"spiral_abyss": abyss,
"profile_card": f"/static/{profile.name}" if profile else None,
"character_cards": [f"/static/{b.name}" for b in banners]
}
save_json_cache(uid, result)
return result
except Exception as e:
raise HTTPException(500, str(e))
# --- Direct image endpoints ---
@app.get("/banner")
async def banner(uid: str):
files = await make_banner(uid)
if not files:
raise HTTPException(404, "Banner tidak ada")
return FileResponse(files[0])
@app.get("/profile")
async def profile(uid: str):
fname = await make_profile(uid)
if not fname:
raise HTTPException(404, "Profile tidak ada")
return FileResponse(fname)
if __name__ == "__main__":
import uvicorn
uvicorn.run(
"app:app",
host="0.0.0.0",
port=int(os.environ.get("SERVER_PORT", 5000)),
reload=False
)