AI-BOOK / app.py
ginipick's picture
Update app.py
fd3060e verified
raw
history blame
4.83 kB
# app.py
# FastAPI μ„œλ²„ + Real3D FlipBook 정적 μ‚¬μ΄νŠΈ
# λͺ¨λ“  정적 파일(JS / CSS / MP3 / 이미지 λ“±)κ³Ό 이 νŒŒμΌμ„ 같은 폴더에 λ‘‘λ‹ˆλ‹€.
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
import pathlib, os, uvicorn
BASE = pathlib.Path(__file__).parent
app = FastAPI()
# ─────────────────────────────────────────────────────────────
# 1) /static β†’ ν˜„μž¬ 폴더(라이브러리·MP3·이미지) μ„œλΉ™
# ─────────────────────────────────────────────────────────────
app.mount("/static", StaticFiles(directory=BASE), name="static")
# ─────────────────────────────────────────────────────────────
# 2) index.html 원문 (μ˜€λ””μ˜€ 경둜 /static/turnPage2.mp3 둜 μˆ˜μ •)
# ─────────────────────────────────────────────────────────────
INDEX_HTML = """
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>FlipBook – μ—…λ‘œλ“œ + μ‚¬μš΄λ“œ</title>
<link rel="stylesheet" href="/static/flipbook.css">
<!-- ν•„μˆ˜ JS μˆœμ„œ -->
<script src="/static/three.js"></script>
<script src="/static/iscroll.js"></script>
<script src="/static/mark.js"></script>
<script src="/static/mod3d.js"></script>
<script src="/static/flipbook.js"></script>
<script src="/static/flipbook.book3.js"></script>
<script src="/static/flipbook.scroll.js"></script>
<script src="/static/flipbook.swipe.js"></script>
<script src="/static/flipbook.webgl.js"></script>
<style>
body{margin:0;font-family:sans-serif;background:#f4f4f4}
h2{text-align:center;margin:24px 0}
#viewer{width:900px;height:600px;margin:0 auto 40px;background:#fff;border:1px solid #ccc}
.upload-wrapper{display:flex;justify-content:center}
#uploadBtn{
all:unset;width:44px;height:44px;line-height:44px;text-align:center;
font-size:26px;border-radius:50%;cursor:pointer;
background:#ffb84d;color:#fff;box-shadow:0 2px 4px rgba(0,0,0,.2);
}
#fileInput{display:none}
</style>
</head>
<body>
<h2>Real3D FlipBook – 이미지 μ—…λ‘œλ“œ πŸ“·βœ¨</h2>
<div id="viewer"></div>
<div class="upload-wrapper">
<button id="uploadBtn" title="이미지 μ—…λ‘œλ“œ">πŸ“·</button>
<input id="fileInput" type="file" accept="image/*" multiple>
</div>
<script>
let book=null;
const viewer=document.getElementById('viewer');
document.getElementById('uploadBtn').onclick=()=>document.getElementById('fileInput').click();
document.getElementById('fileInput').onchange=e=>{
if(!e.target.files.length) return;
const files=[...e.target.files];
const pages=[], tot=files.length; let read=0;
files.forEach((f,i)=>{
const r=new FileReader();
r.onload=ev=>{
pages[i]={src:ev.target.result,thumb:ev.target.result};
if(++read===tot) createBook(pages);
};
r.readAsDataURL(f);
});
};
function createBook(pages){
if(book){ book.destroy(); viewer.innerHTML=''; }
book=new FlipBook(viewer,{
pages,
viewMode:'webgl',
autoSize:true,
flipDuration:800,
backgroundColor:'#ffffff',
/* πŸ”Š μ‚¬μš΄λ“œ – 정적 경둜 μ§€μ • */
sound:true,
assets:{
flipMp3 : '/static/turnPage2.mp3',
hardFlipMp3 : '/static/turnPage2.mp3'
},
controlsProps:{enableFullscreen:true,thumbnails:true}
});
}
</script>
</body>
</html>
"""
# ─────────────────────────────────────────────────────────────
# 3) 루트 경둜 β†’ index.html λ°˜ν™˜
# ─────────────────────────────────────────────────────────────
@app.get("/", response_class=HTMLResponse)
async def root():
return INDEX_HTML
# ─────────────────────────────────────────────────────────────
# 4) Hugging Face Spacesκ°€ python app.py 둜 μ‹€ν–‰ μ‹œ μ„œλ²„ 기동
# ─────────────────────────────────────────────────────────────
if __name__ == "__main__":
port = int(os.environ.get("PORT", 7860))
uvicorn.run("app:app", host="0.0.0.0", port=port, reload=False)