ginipick commited on
Commit
a7a02d1
Β·
verified Β·
1 Parent(s): 0c676dc

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +107 -35
app.py CHANGED
@@ -57,7 +57,7 @@ h2{margin:0;font-size:1.5rem;font-weight:600}
57
  .grid{display:grid;grid-template-columns:repeat(auto-fill,180px);gap:16px;margin-top:24px}
58
  .card{
59
  background:#fff url('/static/book2.jpg') no-repeat center center;
60
- background-size: cover;
61
  border:1px solid #ccc;
62
  border-radius:6px;
63
  cursor:pointer;
@@ -71,24 +71,45 @@ h2{margin:0;font-size:1.5rem;font-weight:600}
71
  justify-content: center;
72
  }
73
  .card img{
74
- width:75%;
75
  height:auto;
76
  object-fit:contain;
77
- position:relative;
78
- display:block;
79
- margin:0 auto;
 
80
  border: 1px solid #ddd;
81
  box-shadow: 0 2px 5px rgba(0,0,0,0.2);
82
  }
83
  .card p{
84
  text-align:center;
85
  margin:6px 0;
 
 
 
 
86
  background: rgba(255, 255, 255, 0.7);
87
  padding: 4px 8px;
88
  border-radius: 4px;
 
 
 
 
 
89
  }
90
  button.upload{all:unset;cursor:pointer;border:1px solid #bbb;padding:8px 14px;border-radius:6px;background:#fff;margin:0 8px}
91
- #viewer{width:100%;max-width:1200px;height:80vh;margin:24px auto;background:#fff;border:1px solid #ccc}
 
 
 
 
 
 
 
 
 
 
 
92
  </style></head><body>
93
 
94
  <header>
@@ -129,7 +150,13 @@ function addCard(i,thumb,title){
129
  const d=document.createElement('div');
130
  d.className='card';
131
  d.onclick=()=>open(i);
132
- d.innerHTML=`<img src="${thumb}"><p>${title || 'ν”„λ‘œμ νŠΈ ' + (i+1)}</p>`;
 
 
 
 
 
 
133
  grid.appendChild(d);
134
  }
135
 
@@ -203,43 +230,70 @@ function open(i){
203
  if(pages[0].path) {
204
  // μ„œλ²„ PDF 파일 λ‘œλ“œ
205
  fetch(`/api/pdf-content?path=${encodeURIComponent(pages[0].path)}`)
206
- .then(response => response.arrayBuffer())
 
 
 
 
 
207
  .then(pdfData => {
208
- pdfjsLib.getDocument({data: pdfData}).promise.then(async pdf => {
209
- const pdfPages = [];
210
- for(let p = 1; p <= pdf.numPages; p++) {
211
- const pg = await pdf.getPage(p), vp = pg.getViewport({scale: 1});
212
- const c = document.createElement('canvas');
213
- c.width = vp.width;
214
- c.height = vp.height;
215
- await pg.render({canvasContext: c.getContext('2d'), viewport: vp}).promise;
216
- pdfPages.push({src: c.toDataURL(), thumb: c.toDataURL()});
217
- }
 
218
 
219
- createFlipBook(pdfPages);
220
- });
 
 
 
 
 
 
 
 
 
 
221
  })
222
  .catch(error => {
223
- console.error('PDF λ‘œλ“œ μ‹€νŒ¨:', error);
 
224
  });
225
  } else {
226
  // μ—…λ‘œλ“œλœ ν”„λ‘œμ νŠΈ 보기
 
227
  createFlipBook(pages);
228
  }
229
  }
230
 
231
  function createFlipBook(pages) {
232
- fb = new FlipBook(viewer, {
233
- pages,
234
- viewMode: 'webgl',
235
- autoSize: true,
236
- flipDuration: 800,
237
- backgroundColor: '#fff',
238
- /* πŸ”Š λ‚΄μž₯ μ‚¬μš΄λ“œ */
239
- sound: true,
240
- assets: {flipMp3: 'static/turnPage2.mp3', hardFlipMp3: 'static/turnPage2.mp3'},
241
- controlsProps: {enableFullscreen: true, thumbnails: true}
242
- });
 
 
 
 
 
 
 
 
 
243
  }
244
 
245
  /* ── λ„€λΉ„κ²Œμ΄μ…˜ ── */
@@ -285,14 +339,32 @@ async def get_pdf_thumbnail(path: str):
285
  except Exception as e:
286
  return {"error": str(e), "thumbnail": None}
287
 
288
- # API μ—”λ“œν¬μΈνŠΈ: PDF μ½˜ν…μΈ  κ°€μ Έμ˜€κΈ°
289
  @app.get("/api/pdf-content")
290
  async def get_pdf_content(path: str):
291
  try:
 
 
 
 
 
 
292
  with open(path, "rb") as pdf_file:
293
- return pdf_file.read()
 
 
 
 
 
 
 
 
 
 
294
  except Exception as e:
295
- return {"error": str(e)}
 
 
 
296
 
297
  @app.get("/", response_class=HTMLResponse)
298
  async def root():
 
57
  .grid{display:grid;grid-template-columns:repeat(auto-fill,180px);gap:16px;margin-top:24px}
58
  .card{
59
  background:#fff url('/static/book2.jpg') no-repeat center center;
60
+ background-size: 130%; /* λ°°κ²½ 이미지 30% ν™•λŒ€ */
61
  border:1px solid #ccc;
62
  border-radius:6px;
63
  cursor:pointer;
 
71
  justify-content: center;
72
  }
73
  .card img{
74
+ width:65%; /* 썸넀일 크기 μ‘°μ • */
75
  height:auto;
76
  object-fit:contain;
77
+ position:absolute; /* μ ˆλŒ€ μœ„μΉ˜λ‘œ λ³€κ²½ */
78
+ top:50%; /* μƒλ‹¨μ—μ„œ 50% */
79
+ left:50%; /* μ’ŒμΈ‘μ—μ„œ 50% */
80
+ transform: translate(-50%, -50%); /* 정쀑앙 배치 */
81
  border: 1px solid #ddd;
82
  box-shadow: 0 2px 5px rgba(0,0,0,0.2);
83
  }
84
  .card p{
85
  text-align:center;
86
  margin:6px 0;
87
+ position: absolute;
88
+ bottom: 10px;
89
+ left: 50%;
90
+ transform: translateX(-50%);
91
  background: rgba(255, 255, 255, 0.7);
92
  padding: 4px 8px;
93
  border-radius: 4px;
94
+ width: 85%;
95
+ white-space: nowrap;
96
+ overflow: hidden;
97
+ text-overflow: ellipsis;
98
+ max-width: 150px;
99
  }
100
  button.upload{all:unset;cursor:pointer;border:1px solid #bbb;padding:8px 14px;border-radius:6px;background:#fff;margin:0 8px}
101
+ #viewer{
102
+ width:100%;
103
+ height:100vh; /* 전체 ν™”λ©΄ λ†’μ΄λ‘œ ν™•μž₯ */
104
+ max-width:100%; /* μ΅œλŒ€ λ„ˆλΉ„ μ œν•œ ν•΄μ œ */
105
+ margin:0; /* λ§ˆμ§„ 제거 */
106
+ background:#fff;
107
+ border:none; /* ν…Œλ‘λ¦¬ 제거 */
108
+ position:fixed; /* κ³ μ • μœ„μΉ˜ */
109
+ top:0;
110
+ left:0;
111
+ z-index:1000; /* μ΅œμƒμœ„ ν‘œμ‹œ */
112
+ }
113
  </style></head><body>
114
 
115
  <header>
 
150
  const d=document.createElement('div');
151
  d.className='card';
152
  d.onclick=()=>open(i);
153
+
154
+ // 제λͺ© 10κΈ€μž μ œν•œ 및 λ§μ€„μž„ν‘œ 처리
155
+ const displayTitle = title ?
156
+ (title.length > 10 ? title.substring(0, 10) + '...' : title) :
157
+ 'ν”„λ‘œμ νŠΈ ' + (i+1);
158
+
159
+ d.innerHTML=`<img src="${thumb}"><p title="${title || 'ν”„λ‘œμ νŠΈ ' + (i+1)}">${displayTitle}</p>`;
160
  grid.appendChild(d);
161
  }
162
 
 
230
  if(pages[0].path) {
231
  // μ„œλ²„ PDF 파일 λ‘œλ“œ
232
  fetch(`/api/pdf-content?path=${encodeURIComponent(pages[0].path)}`)
233
+ .then(response => {
234
+ if (!response.ok) {
235
+ throw new Error('PDF λ‘œλ“œ μ‹€νŒ¨: ' + response.statusText);
236
+ }
237
+ return response.arrayBuffer();
238
+ })
239
  .then(pdfData => {
240
+ // PDF 데이터 λ‘œλ“œ 확인 λ‘œκΉ…
241
+ console.log('PDF 데이터 λ‘œλ“œ μ™„λ£Œ:', pdfData.byteLength + ' λ°”μ΄νŠΈ');
242
+
243
+ return pdfjsLib.getDocument({data: pdfData}).promise;
244
+ })
245
+ .then(async pdf => {
246
+ console.log('PDF λ¬Έμ„œ λ‘œλ“œ μ™„λ£Œ. νŽ˜μ΄μ§€ 수:', pdf.numPages);
247
+
248
+ const pdfPages = [];
249
+ for(let p = 1; p <= pdf.numPages; p++) {
250
+ console.log('νŽ˜μ΄μ§€ λ Œλ”λ§ 쀑:', p + '/' + pdf.numPages);
251
 
252
+ const pg = await pdf.getPage(p);
253
+ const vp = pg.getViewport({scale: 1});
254
+ const c = document.createElement('canvas');
255
+ c.width = vp.width;
256
+ c.height = vp.height;
257
+
258
+ await pg.render({canvasContext: c.getContext('2d'), viewport: vp}).promise;
259
+ pdfPages.push({src: c.toDataURL(), thumb: c.toDataURL()});
260
+ }
261
+
262
+ console.log('λͺ¨λ“  νŽ˜μ΄μ§€ λ Œλ”λ§ μ™„λ£Œ:', pdfPages.length);
263
+ createFlipBook(pdfPages);
264
  })
265
  .catch(error => {
266
+ console.error('PDF 처리 쀑 였λ₯˜ λ°œμƒ:', error);
267
+ alert('PDFλ₯Ό λ‘œλ“œν•˜λŠ” 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€: ' + error.message);
268
  });
269
  } else {
270
  // μ—…λ‘œλ“œλœ ν”„λ‘œμ νŠΈ 보기
271
+ console.log('둜컬 μ—…λ‘œλ“œλœ ν”„λ‘œμ νŠΈ λ Œλ”λ§:', pages.length + 'νŽ˜μ΄μ§€');
272
  createFlipBook(pages);
273
  }
274
  }
275
 
276
  function createFlipBook(pages) {
277
+ console.log('FlipBook 생성 μ‹œμž‘. νŽ˜μ΄μ§€ 수:', pages.length);
278
+
279
+ try {
280
+ fb = new FlipBook(viewer, {
281
+ pages: pages,
282
+ viewMode: 'webgl',
283
+ autoSize: true,
284
+ flipDuration: 800,
285
+ backgroundColor: '#fff',
286
+ /* πŸ”Š λ‚΄μž₯ μ‚¬μš΄λ“œ */
287
+ sound: true,
288
+ assets: {flipMp3: 'static/turnPage2.mp3', hardFlipMp3: 'static/turnPage2.mp3'},
289
+ controlsProps: {enableFullscreen: true, thumbnails: true}
290
+ });
291
+
292
+ console.log('FlipBook 생성 μ™„λ£Œ');
293
+ } catch (error) {
294
+ console.error('FlipBook 생성 쀑 였λ₯˜ λ°œμƒ:', error);
295
+ alert('FlipBook을 μƒμ„±ν•˜λŠ” 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€: ' + error.message);
296
+ }
297
  }
298
 
299
  /* ── λ„€λΉ„κ²Œμ΄μ…˜ ── */
 
339
  except Exception as e:
340
  return {"error": str(e), "thumbnail": None}
341
 
 
342
  @app.get("/api/pdf-content")
343
  async def get_pdf_content(path: str):
344
  try:
345
+ # 파일 쑴재 μ—¬λΆ€ 확인
346
+ pdf_path = pathlib.Path(path)
347
+ if not pdf_path.exists():
348
+ return {"error": f"νŒŒμΌμ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€: {path}"}, 404
349
+
350
+ # 파일 읽기
351
  with open(path, "rb") as pdf_file:
352
+ content = pdf_file.read()
353
+
354
+ # 응닡 헀더 μ„€μ • - PDF νŒŒμΌμž„μ„ λͺ…μ‹œ
355
+ headers = {
356
+ "Content-Type": "application/pdf",
357
+ "Content-Disposition": f"inline; filename={pdf_path.name}"
358
+ }
359
+
360
+ # 파일 μ½˜ν…μΈ  λ°˜ν™˜
361
+ from fastapi.responses import Response
362
+ return Response(content=content, headers=headers)
363
  except Exception as e:
364
+ import traceback
365
+ error_details = traceback.format_exc()
366
+ print(f"PDF μ½˜ν…μΈ  λ‘œλ“œ 였λ₯˜: {str(e)}\n{error_details}")
367
+ return {"error": str(e)}, 500
368
 
369
  @app.get("/", response_class=HTMLResponse)
370
  async def root():