Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
Update app.py
Browse files
app.py
CHANGED
@@ -1,124 +1,134 @@
|
|
1 |
-
# app.py
|
2 |
-
# FastAPI μλ² + Real3D FlipBook μ μ μ¬μ΄νΈ
|
3 |
-
# λͺ¨λ μ μ νμΌ(JS / CSS / MP3 / μ΄λ―Έμ§ λ±)κ³Ό μ΄ νμΌμ κ°μ ν΄λμ λ‘λλ€.
|
4 |
-
|
5 |
from fastapi import FastAPI
|
6 |
from fastapi.responses import HTMLResponse
|
7 |
from fastapi.staticfiles import StaticFiles
|
8 |
import pathlib, os, uvicorn
|
9 |
|
10 |
BASE = pathlib.Path(__file__).parent
|
11 |
-
|
12 |
app = FastAPI()
|
13 |
-
|
14 |
-
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
15 |
-
# 1) /static β νμ¬ ν΄λ(λΌμ΄λΈλ¬λ¦¬Β·MP3Β·μ΄λ―Έμ§) μλΉ
|
16 |
-
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
17 |
app.mount("/static", StaticFiles(directory=BASE), name="static")
|
18 |
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
INDEX_HTML = """
|
23 |
-
<!DOCTYPE html>
|
24 |
-
<html lang="ko">
|
25 |
-
<head>
|
26 |
-
<meta charset="UTF-8">
|
27 |
-
<title>FlipBook β μ
λ‘λ + μ¬μ΄λ</title>
|
28 |
-
|
29 |
<link rel="stylesheet" href="/static/flipbook.css">
|
30 |
-
|
31 |
-
<!-- νμ JS μμ -->
|
32 |
<script src="/static/three.js"></script>
|
33 |
<script src="/static/iscroll.js"></script>
|
34 |
<script src="/static/mark.js"></script>
|
35 |
<script src="/static/mod3d.js"></script>
|
36 |
-
|
37 |
<script src="/static/flipbook.js"></script>
|
38 |
<script src="/static/flipbook.book3.js"></script>
|
39 |
<script src="/static/flipbook.scroll.js"></script>
|
40 |
<script src="/static/flipbook.swipe.js"></script>
|
41 |
<script src="/static/flipbook.webgl.js"></script>
|
42 |
-
|
43 |
<style>
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
}
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
<
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
66 |
|
67 |
<script>
|
68 |
-
let
|
69 |
-
const viewer
|
70 |
-
|
71 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
72 |
|
73 |
-
|
74 |
-
|
75 |
-
const files=[...e.target.files];
|
76 |
-
const pages=[],
|
77 |
-
files.forEach((f,i)=>{
|
78 |
-
|
79 |
-
r.onload=ev=>{
|
80 |
-
pages[i]={src:ev.target.result,thumb:ev.target.result};
|
81 |
-
if(++read===tot) createBook(pages);
|
82 |
-
};
|
83 |
-
r.readAsDataURL(f);
|
84 |
-
});
|
85 |
};
|
86 |
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
96 |
|
97 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
98 |
sound:true,
|
99 |
-
assets:{
|
100 |
-
|
101 |
-
|
102 |
-
},
|
103 |
|
104 |
-
|
105 |
-
|
|
|
|
|
|
|
|
|
106 |
}
|
107 |
</script>
|
108 |
-
</body>
|
109 |
-
</html>
|
110 |
"""
|
111 |
|
112 |
-
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
113 |
-
# 3) λ£¨νΈ κ²½λ‘ β index.html λ°ν
|
114 |
-
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
115 |
@app.get("/", response_class=HTMLResponse)
|
116 |
async def root():
|
117 |
-
return
|
118 |
|
119 |
-
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
120 |
-
# 4) Hugging Face Spacesκ° python app.py λ‘ μ€ν μ μλ² κΈ°λ
|
121 |
-
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
122 |
if __name__ == "__main__":
|
123 |
-
|
124 |
-
uvicorn.run("app:app", host="0.0.0.0", port=port, reload=False)
|
|
|
|
|
|
|
|
|
|
|
1 |
from fastapi import FastAPI
|
2 |
from fastapi.responses import HTMLResponse
|
3 |
from fastapi.staticfiles import StaticFiles
|
4 |
import pathlib, os, uvicorn
|
5 |
|
6 |
BASE = pathlib.Path(__file__).parent
|
|
|
7 |
app = FastAPI()
|
|
|
|
|
|
|
|
|
8 |
app.mount("/static", StaticFiles(directory=BASE), name="static")
|
9 |
|
10 |
+
HTML = """
|
11 |
+
<!doctype html><html lang="ko"><head>
|
12 |
+
<meta charset="utf-8"><title>FlipBook Space</title>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
<link rel="stylesheet" href="/static/flipbook.css">
|
|
|
|
|
14 |
<script src="/static/three.js"></script>
|
15 |
<script src="/static/iscroll.js"></script>
|
16 |
<script src="/static/mark.js"></script>
|
17 |
<script src="/static/mod3d.js"></script>
|
18 |
+
<script src="/static/pdf.js"></script>
|
19 |
<script src="/static/flipbook.js"></script>
|
20 |
<script src="/static/flipbook.book3.js"></script>
|
21 |
<script src="/static/flipbook.scroll.js"></script>
|
22 |
<script src="/static/flipbook.swipe.js"></script>
|
23 |
<script src="/static/flipbook.webgl.js"></script>
|
|
|
24 |
<style>
|
25 |
+
body{margin:0;background:#f0f0f0;font-family:sans-serif}
|
26 |
+
header{max-width:960px;margin:0 auto;padding:18px 20px;display:flex;align-items:center}
|
27 |
+
#homeBtn{display:none;width:38px;height:38px;border:none;border-radius:50%;cursor:pointer;
|
28 |
+
background:#0077c2;color:#fff;font-size:20px;margin-right:12px}
|
29 |
+
#homeBtn:hover{background:#005999}
|
30 |
+
h2{margin:0;font-size:1.5rem;font-weight:600}
|
31 |
+
#home,#viewerPage{max-width:960px;margin:0 auto;padding:0 20px 40px}
|
32 |
+
.grid{display:grid;grid-template-columns:repeat(auto-fill,180px);gap:16px;margin-top:24px}
|
33 |
+
.card{background:#fff;border:1px solid #ccc;border-radius:6px;cursor:pointer;box-shadow:0 2px 4px rgba(0,0,0,.12)}
|
34 |
+
.card img{width:100%;height:140px;object-fit:cover}
|
35 |
+
.card p{text-align:center;margin:6px 0}
|
36 |
+
button.upload{all:unset;cursor:pointer;border:1px solid #bbb;padding:8px 14px;border-radius:6px;background:#fff;margin:0 8px}
|
37 |
+
#viewer{width:100%;max-width:1200px;height:80vh;margin:24px auto;background:#fff;border:1px solid #ccc}
|
38 |
+
</style></head><body>
|
39 |
+
|
40 |
+
<header>
|
41 |
+
<button id="homeBtn" title="νμΌλ‘">β</button>
|
42 |
+
<h2>My FlipBook Projects</h2>
|
43 |
+
</header>
|
44 |
+
|
45 |
+
<section id="home">
|
46 |
+
<div>
|
47 |
+
<label class="upload">π· μ΄λ―Έμ§ <input id="imgInput" type="file" accept="image/*" multiple hidden></label>
|
48 |
+
<label class="upload">π PDF <input id="pdfInput" type="file" accept="application/pdf" hidden></label>
|
49 |
+
</div>
|
50 |
+
<div class="grid" id="grid"></div>
|
51 |
+
</section>
|
52 |
+
|
53 |
+
<section id="viewerPage" style="display:none">
|
54 |
+
<div id="viewer"></div>
|
55 |
+
</section>
|
56 |
|
57 |
<script>
|
58 |
+
let projects=[], fb=null;
|
59 |
+
const grid=$id('grid'), viewer=$id('viewer');
|
60 |
+
pdfjsLib.GlobalWorkerOptions.workerSrc='/static/pdf.worker.js';
|
61 |
+
|
62 |
+
/* π μ€λμ€ unlock β λ΄μ₯ Audio μ κ°μ MP3 κ²½λ‘ μ¬μ© */
|
63 |
+
['click','touchstart'].forEach(evt=>{
|
64 |
+
document.addEventListener(evt,function u(){new Audio('static/turnPage2.mp3')
|
65 |
+
.play().then(a=>a.pause()).catch(()=>{});document.removeEventListener(evt,u,{capture:true});},
|
66 |
+
{once:true,capture:true});
|
67 |
+
});
|
68 |
+
|
69 |
+
/* ββ μ νΈ ββ */
|
70 |
+
function $id(id){return document.getElementById(id)}
|
71 |
+
function addCard(i,thumb){
|
72 |
+
const d=document.createElement('div');d.className='card';d.onclick=()=>open(i);
|
73 |
+
d.innerHTML=`<img src="${thumb}"><p>νλ‘μ νΈ ${i+1}</p>`;grid.appendChild(d);
|
74 |
+
}
|
75 |
|
76 |
+
/* ββ μ΄λ―Έμ§ μ
λ‘λ ββ */
|
77 |
+
$id('imgInput').onchange=e=>{
|
78 |
+
const files=[...e.target.files]; if(!files.length) return;
|
79 |
+
const pages=[],tot=files.length;let done=0;
|
80 |
+
files.forEach((f,i)=>{const r=new FileReader();r.onload=x=>{pages[i]={src:x.target.result,thumb:x.target.result};
|
81 |
+
if(++done===tot) save(pages);};r.readAsDataURL(f);});
|
|
|
|
|
|
|
|
|
|
|
|
|
82 |
};
|
83 |
|
84 |
+
/* ββ PDF μ
λ‘λ ββ */
|
85 |
+
$id('pdfInput').onchange=e=>{
|
86 |
+
const file=e.target.files[0]; if(!file) return;
|
87 |
+
const fr=new FileReader();
|
88 |
+
fr.onload=v=>{
|
89 |
+
pdfjsLib.getDocument({data:v.target.result}).promise.then(async pdf=>{
|
90 |
+
const pages=[];
|
91 |
+
for(let p=1;p<=pdf.numPages;p++){
|
92 |
+
const pg=await pdf.getPage(p), vp=pg.getViewport({scale:1});
|
93 |
+
const c=document.createElement('canvas');c.width=vp.width;c.height=vp.height;
|
94 |
+
await pg.render({canvasContext:c.getContext('2d'),viewport:vp}).promise;
|
95 |
+
pages.push({src:c.toDataURL(),thumb:c.toDataURL()});
|
96 |
+
}
|
97 |
+
save(pages);
|
98 |
+
});
|
99 |
+
};fr.readAsArrayBuffer(file);
|
100 |
+
};
|
101 |
|
102 |
+
/* ββ νλ‘μ νΈ μ μ₯ ββ */
|
103 |
+
function save(pages){const id=projects.push(pages)-1;addCard(id,pages[0].thumb);}
|
104 |
+
|
105 |
+
/* ββ μΉ΄λ β FlipBook ββ */
|
106 |
+
function open(i){
|
107 |
+
toggle(false);
|
108 |
+
const pages=projects[i];
|
109 |
+
if(fb){fb.destroy();viewer.innerHTML='';}
|
110 |
+
fb=new FlipBook(viewer,{
|
111 |
+
pages,viewMode:'webgl',autoSize:true,flipDuration:800,backgroundColor:'#fff',
|
112 |
+
/* π λ΄μ₯ μ¬μ΄λ */
|
113 |
sound:true,
|
114 |
+
assets:{flipMp3:'static/turnPage2.mp3',hardFlipMp3:'static/turnPage2.mp3'},
|
115 |
+
controlsProps:{enableFullscreen:true,thumbnails:true}});
|
116 |
+
}
|
|
|
117 |
|
118 |
+
/* ββ λ€λΉκ²μ΄μ
ββ */
|
119 |
+
$id('homeBtn').onclick=()=>toggle(true);
|
120 |
+
function toggle(showHome){
|
121 |
+
$id('home').style.display=showHome?'block':'none';
|
122 |
+
$id('viewerPage').style.display=showHome?'none':'block';
|
123 |
+
$id('homeBtn').style.display=showHome?'none':'inline-block';
|
124 |
}
|
125 |
</script>
|
126 |
+
</body></html>
|
|
|
127 |
"""
|
128 |
|
|
|
|
|
|
|
129 |
@app.get("/", response_class=HTMLResponse)
|
130 |
async def root():
|
131 |
+
return HTML
|
132 |
|
|
|
|
|
|
|
133 |
if __name__ == "__main__":
|
134 |
+
uvicorn.run("app:app", host="0.0.0.0", port=int(os.getenv("PORT", 7860)))
|
|