ginipick commited on
Commit
82e2b16
Β·
verified Β·
1 Parent(s): 08e6637

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +85 -51
app.py CHANGED
@@ -1,7 +1,7 @@
1
  #!/usr/bin/env python3
2
  # -*- coding: utf-8 -*-
3
  """
4
- 3D Flipbook Viewer (Gradio) – 전체 μ†ŒμŠ€
5
  μ΅œμ’… μˆ˜μ •: 2025-05-18
6
  """
7
 
@@ -15,11 +15,12 @@ import json
15
  import logging
16
  import traceback
17
  from pathlib import Path
 
18
 
19
  # μ™ΈλΆ€ 라이브러리
20
  import gradio as gr
21
  from PIL import Image
22
- import fitz # PyMuPDF
23
 
24
  # ────────────────────────────
25
  # λ‘œκΉ… μ„€μ •
@@ -48,8 +49,8 @@ for d in [TEMP_DIR, UPLOAD_DIR, OUTPUT_DIR, THUMBS_DIR, HTML_DIR]:
48
  # ────────────────────────────
49
  # μœ ν‹Έ ν•¨μˆ˜
50
  # ────────────────────────────
51
- def create_thumbnail(src: str, dst: str, size=(300, 300)) -> str | None:
52
- """원본 이미지λ₯Ό μΈλ„€μΌλ‘œ μ €μž₯"""
53
  try:
54
  with Image.open(src) as im:
55
  im.thumbnail(size, Image.LANCZOS)
@@ -59,11 +60,11 @@ def create_thumbnail(src: str, dst: str, size=(300, 300)) -> str | None:
59
  logging.error("Thumbnail error: %s", e)
60
  return None
61
 
62
-
63
  # ────────────────────────────
64
  # PDF β†’ 이미지
65
  # ────────────────────────────
66
- def process_pdf(pdf_path: str, session_id: str) -> list[dict]:
 
67
  pages_info = []
68
  out_dir = os.path.join(OUTPUT_DIR, session_id)
69
  th_dir = os.path.join(THUMBS_DIR, session_id)
@@ -73,13 +74,16 @@ def process_pdf(pdf_path: str, session_id: str) -> list[dict]:
73
  try:
74
  pdf_doc = fitz.open(pdf_path)
75
  for idx, page in enumerate(pdf_doc):
76
- pix = page.get_pixmap(matrix=fitz.Matrix(2, 2)) # 2Γ— 해상도
 
 
77
  img_path = os.path.join(out_dir, f"page_{idx+1}.png")
78
  pix.save(img_path)
79
 
80
  thumb_path = os.path.join(th_dir, f"thumb_{idx+1}.png")
81
  create_thumbnail(img_path, thumb_path)
82
 
 
83
  html_overlay = (
84
  """
85
  <div style="position:absolute;top:50px;left:50px;
@@ -111,11 +115,11 @@ def process_pdf(pdf_path: str, session_id: str) -> list[dict]:
111
  logging.error("process_pdf() failed: %s", e)
112
  return []
113
 
114
-
115
  # ────────────────────────────
116
  # 이미지 μ—…λ‘œλ“œ 처리
117
  # ────────────────────────────
118
- def process_images(img_paths: list[str], session_id: str) -> list[dict]:
 
119
  pages_info = []
120
  out_dir = os.path.join(OUTPUT_DIR, session_id)
121
  th_dir = os.path.join(THUMBS_DIR, session_id)
@@ -128,8 +132,9 @@ def process_images(img_paths: list[str], session_id: str) -> list[dict]:
128
  shutil.copy(src, dst)
129
 
130
  thumb = os.path.join(th_dir, f"thumb_{i+1}.png")
131
- create_thumbnail(src, thumb)
132
 
 
133
  if i == 0:
134
  html_overlay = """
135
  <div style="position:absolute;top:50px;left:50px;
@@ -174,14 +179,17 @@ def process_images(img_paths: list[str], session_id: str) -> list[dict]:
174
 
175
  return pages_info
176
 
177
-
178
  # ────────────────────────────
179
  # ν”Œλ¦½λΆ HTML 생성
180
  # ────────────────────────────
181
  def generate_flipbook_html(
182
- pages_info: list[dict], session_id: str, view_mode: str, skin: str
 
 
 
183
  ) -> str:
184
- # None 값은 JSON 직렬화 전에 제거
 
185
  for p in pages_info:
186
  if p.get("htmlContent") is None:
187
  p.pop("htmlContent", None)
@@ -198,6 +206,7 @@ def generate_flipbook_html(
198
  <meta name="viewport" content="width=device-width,initial-scale=1">
199
  <title>3D Flipbook</title>
200
 
 
201
  <link rel="stylesheet" href="/public/libs/flipbook/css/flipbook.style.css">
202
  <script src="/public/libs/flipbook/js/flipbook.min.js"></script>
203
  <script src="/public/libs/flipbook/js/flipbook.webgl.min.js"></script>
@@ -224,33 +233,40 @@ def generate_flipbook_html(
224
  document.addEventListener('DOMContentLoaded',()=>{
225
  const hide=()=>{{document.getElementById('loading').style.display='none'}};
226
  try{{
227
- const options={{pages:{pages_json},
228
- viewMode:"{view_mode}",
229
- skin:"{skin}",
230
- responsiveView:true,
231
- singlePageMode:false,
232
- singlePageModeIfMobile:true,
233
- pageFlipDuration:1,
234
- thumbnailsOnStart:true,
235
- btnThumbs:{{enabled:true}},
236
- btnPrint:{{enabled:true}},
237
- btnDownloadPages:{{enabled:true}},
238
- btnDownloadPdf:{{enabled:true}},
239
- btnShare:{{enabled:true}},
240
- btnSound:{{enabled:true}},
241
- btnExpand:{{enabled:true}} }};
242
- new FlipBook(document.getElementById('flipbook-container'),options);
243
- setTimeout(hide,1000);
244
- }}catch(e){{console.error(e);alert('ν”Œλ¦½λΆ μ΄ˆκΈ°ν™” 였λ₯˜:'+e.message);}}
 
 
 
 
 
245
  });
246
  </script>
247
- </body></html>
 
248
  """
249
 
250
  Path(html_path).write_text(html, encoding="utf-8")
 
251
  public_url = f"/public/flipbooks/{html_file}"
252
 
253
- # μ‚¬μš©μžμ—κ²Œ λŒλ €μ€„ 링크 덩어리
254
  return f"""
255
  <div style="text-align:center;padding:20px;background:#f9f9f9;border-radius:5px">
256
  <h2 style="margin:0;color:#333">ν”Œλ¦½λΆμ΄ μ€€λΉ„λ˜μ—ˆμŠ΅λ‹ˆλ‹€!</h2>
@@ -263,15 +279,16 @@ document.addEventListener('DOMContentLoaded',()=>{
263
  </div>
264
  """
265
 
266
-
267
  # ────────────────────────────
268
  # 콜백: PDF μ—…λ‘œλ“œ
269
  # ────────────────────────────
270
  def create_flipbook_from_pdf(
271
- pdf_file: gr.File | None, view_mode="2d", skin="light"
 
 
272
  ):
273
  session_id = str(uuid.uuid4())
274
- debug: list[str] = []
275
 
276
  if not pdf_file:
277
  return (
@@ -280,18 +297,25 @@ def create_flipbook_from_pdf(
280
  )
281
 
282
  try:
283
- pdf_path = pdf_file.name
284
- debug.append(f"PDF path: {pdf_path}")
 
 
 
 
 
285
 
 
 
 
286
  pages_info = process_pdf(pdf_path, session_id)
287
  debug.append(f"Extracted pages: {len(pages_info)}")
288
 
289
  if not pages_info:
290
  raise RuntimeError("PDF 처리 κ²°κ³Όκ°€ λΉ„μ–΄ μžˆμŠ΅λ‹ˆλ‹€.")
291
 
292
- html_block = generate_flipbook_html(
293
- pages_info, session_id, view_mode, skin
294
- )
295
  return html_block, "\n".join(debug)
296
 
297
  except Exception as e:
@@ -303,15 +327,16 @@ def create_flipbook_from_pdf(
303
  "\n".join(debug),
304
  )
305
 
306
-
307
  # ────────────────────────────
308
  # 콜백: 이미지 μ—…λ‘œλ“œ
309
  # ────────────────────────────
310
  def create_flipbook_from_images(
311
- images: list[gr.File] | None, view_mode="2d", skin="light"
 
 
312
  ):
313
  session_id = str(uuid.uuid4())
314
- debug: list[str] = []
315
 
316
  if not images:
317
  return (
@@ -320,18 +345,27 @@ def create_flipbook_from_images(
320
  )
321
 
322
  try:
323
- img_paths = [f.name for f in images]
 
 
 
 
 
 
 
 
 
324
  debug.append(f"Images: {img_paths}")
325
 
 
326
  pages_info = process_images(img_paths, session_id)
327
  debug.append(f"Processed: {len(pages_info)}")
328
 
329
  if not pages_info:
330
  raise RuntimeError("이미지 처리 μ‹€νŒ¨")
331
 
332
- html_block = generate_flipbook_html(
333
- pages_info, session_id, view_mode, skin
334
- )
335
  return html_block, "\n".join(debug)
336
 
337
  except Exception as e:
@@ -343,7 +377,6 @@ def create_flipbook_from_images(
343
  "\n".join(debug),
344
  )
345
 
346
-
347
  # ────────────────────────────
348
  # Gradio UI
349
  # ────────────────────────────
@@ -413,5 +446,6 @@ with gr.Blocks(title="3D Flipbook Viewer") as demo:
413
  # ────────────────────────────
414
  # μ‹€ν–‰
415
  # ────────────────────────────
416
- if __name__ == \"__main__\":
417
- demo.launch(debug=True) # share=True ν•„μš” μ‹œ μΆ”κ°€
 
 
1
  #!/usr/bin/env python3
2
  # -*- coding: utf-8 -*-
3
  """
4
+ 3D Flipbook Viewer (Gradio) – 전체 μ†ŒμŠ€ (μˆ˜μ • 버전)
5
  μ΅œμ’… μˆ˜μ •: 2025-05-18
6
  """
7
 
 
15
  import logging
16
  import traceback
17
  from pathlib import Path
18
+ from typing import Optional, List, Dict
19
 
20
  # μ™ΈλΆ€ 라이브러리
21
  import gradio as gr
22
  from PIL import Image
23
+ import fitz # PyMuPDF
24
 
25
  # ────────────────────────────
26
  # λ‘œκΉ… μ„€μ •
 
49
  # ────────────────────────────
50
  # μœ ν‹Έ ν•¨μˆ˜
51
  # ────────────────────────────
52
+ def create_thumbnail(src: str, dst: str, size=(300, 300)) -> Optional[str]:
53
+ """원본 이미지λ₯Ό μΈλ„€μΌλ‘œ μ €μž₯ (이미지 μ—΄κΈ° μ‹€νŒ¨ μ‹œ None 리턴)"""
54
  try:
55
  with Image.open(src) as im:
56
  im.thumbnail(size, Image.LANCZOS)
 
60
  logging.error("Thumbnail error: %s", e)
61
  return None
62
 
 
63
  # ────────────────────────────
64
  # PDF β†’ 이미지
65
  # ────────────────────────────
66
+ def process_pdf(pdf_path: str, session_id: str) -> List[Dict]:
67
+ """PDF νŒŒμΌμ„ νŽ˜μ΄μ§€ 별 PNG둜 λ³€ν™˜ν•˜κ³  νŽ˜μ΄μ§€ 정보λ₯Ό 리슀트둜 리턴"""
68
  pages_info = []
69
  out_dir = os.path.join(OUTPUT_DIR, session_id)
70
  th_dir = os.path.join(THUMBS_DIR, session_id)
 
74
  try:
75
  pdf_doc = fitz.open(pdf_path)
76
  for idx, page in enumerate(pdf_doc):
77
+ # 해상도 ν–₯상을 μœ„ν•΄ 맀트릭슀 μ‚¬μš© (1.5λ°° 정도)
78
+ mat = fitz.Matrix(1.5, 1.5)
79
+ pix = page.get_pixmap(matrix=mat)
80
  img_path = os.path.join(out_dir, f"page_{idx+1}.png")
81
  pix.save(img_path)
82
 
83
  thumb_path = os.path.join(th_dir, f"thumb_{idx+1}.png")
84
  create_thumbnail(img_path, thumb_path)
85
 
86
+ # 첫 νŽ˜μ΄μ§€μ—λ§Œ μ˜ˆμ‹œλ‘œ μ˜€λ²„λ ˆμ΄ HTML 제곡
87
  html_overlay = (
88
  """
89
  <div style="position:absolute;top:50px;left:50px;
 
115
  logging.error("process_pdf() failed: %s", e)
116
  return []
117
 
 
118
  # ────────────────────────────
119
  # 이미지 μ—…λ‘œλ“œ 처리
120
  # ────────────────────────────
121
+ def process_images(img_paths: List[str], session_id: str) -> List[Dict]:
122
+ """μ—…λ‘œλ“œλœ 이미지λ₯Ό 볡사/썸넀일 생성 ν›„ νŽ˜μ΄μ§€ μ •λ³΄λ‘œ 리턴"""
123
  pages_info = []
124
  out_dir = os.path.join(OUTPUT_DIR, session_id)
125
  th_dir = os.path.join(THUMBS_DIR, session_id)
 
132
  shutil.copy(src, dst)
133
 
134
  thumb = os.path.join(th_dir, f"thumb_{i+1}.png")
135
+ create_thumbnail(dst, thumb)
136
 
137
+ # νŽ˜μ΄μ§€λ³„ κ°„λ‹¨ν•œ μ˜€λ²„λ ˆμ΄ μ˜ˆμ‹œ
138
  if i == 0:
139
  html_overlay = """
140
  <div style="position:absolute;top:50px;left:50px;
 
179
 
180
  return pages_info
181
 
 
182
  # ────────────────────────────
183
  # ν”Œλ¦½λΆ HTML 생성
184
  # ────────────────────────────
185
  def generate_flipbook_html(
186
+ pages_info: List[Dict],
187
+ session_id: str,
188
+ view_mode: str,
189
+ skin: str
190
  ) -> str:
191
+ """νŽ˜μ΄μ§€ 정보λ₯Ό 3D Flipbook에 μ μš©ν•  HTML 파일둜 λ§Œλ“€κ³  링크 λ°˜ν™˜"""
192
+ # htmlContentκ°€ None인 κ²½μš°λŠ” JSONμ—μ„œ 제거
193
  for p in pages_info:
194
  if p.get("htmlContent") is None:
195
  p.pop("htmlContent", None)
 
206
  <meta name="viewport" content="width=device-width,initial-scale=1">
207
  <title>3D Flipbook</title>
208
 
209
+ <!-- 3D Flipbook κ΄€λ ¨ CSS/JS -->
210
  <link rel="stylesheet" href="/public/libs/flipbook/css/flipbook.style.css">
211
  <script src="/public/libs/flipbook/js/flipbook.min.js"></script>
212
  <script src="/public/libs/flipbook/js/flipbook.webgl.min.js"></script>
 
233
  document.addEventListener('DOMContentLoaded',()=>{
234
  const hide=()=>{{document.getElementById('loading').style.display='none'}};
235
  try{{
236
+ const options = {{
237
+ pages: {pages_json},
238
+ viewMode: "{view_mode}",
239
+ skin: "{skin}",
240
+ responsiveView: true,
241
+ singlePageMode: false,
242
+ singlePageModeIfMobile: true,
243
+ pageFlipDuration: 1,
244
+ thumbnailsOnStart: true,
245
+ btnThumbs: {{enabled:true}},
246
+ btnPrint: {{enabled:true}},
247
+ btnDownloadPages: {{enabled:true}},
248
+ btnDownloadPdf: {{enabled:true}},
249
+ btnShare: {{enabled:true}},
250
+ btnSound: {{enabled:true}},
251
+ btnExpand: {{enabled:true}}
252
+ }};
253
+ new FlipBook(document.getElementById('flipbook-container'), options);
254
+ setTimeout(hide, 1000);
255
+ }} catch(e) {{
256
+ console.error(e);
257
+ alert('ν”Œλ¦½λΆ μ΄ˆκΈ°ν™” 였λ₯˜:' + e.message);
258
+ }}
259
  });
260
  </script>
261
+ </body>
262
+ </html>
263
  """
264
 
265
  Path(html_path).write_text(html, encoding="utf-8")
266
+
267
  public_url = f"/public/flipbooks/{html_file}"
268
 
269
+ # μ‚¬μš©μžμ—κ²Œ λŒλ €μ€„ λ²„νŠΌ ν˜•νƒœ 링크
270
  return f"""
271
  <div style="text-align:center;padding:20px;background:#f9f9f9;border-radius:5px">
272
  <h2 style="margin:0;color:#333">ν”Œλ¦½λΆμ΄ μ€€λΉ„λ˜μ—ˆμŠ΅λ‹ˆλ‹€!</h2>
 
279
  </div>
280
  """
281
 
 
282
  # ────────────────────────────
283
  # 콜백: PDF μ—…λ‘œλ“œ
284
  # ────────────────────────────
285
  def create_flipbook_from_pdf(
286
+ pdf_file: Optional[gr.File],
287
+ view_mode: str = "2d",
288
+ skin: str = "light"
289
  ):
290
  session_id = str(uuid.uuid4())
291
+ debug: List[str] = []
292
 
293
  if not pdf_file:
294
  return (
 
297
  )
298
 
299
  try:
300
+ # Gradioκ°€ λ„˜κ²¨μ€€ μž„μ‹œ PDF 경둜
301
+ uploaded_temp_path = pdf_file.name
302
+
303
+ # μ„œλ²„ λ‚΄ μž„μ‹œ μ—…λ‘œλ“œ 폴더에 μ•ˆμ „ν•˜κ²Œ 볡사
304
+ filename_only = os.path.basename(uploaded_temp_path)
305
+ pdf_path = os.path.join(UPLOAD_DIR, filename_only)
306
+ shutil.copyfile(uploaded_temp_path, pdf_path)
307
 
308
+ debug.append(f"Copied PDF to: {pdf_path}")
309
+
310
+ # PDF β†’ νŽ˜μ΄μ§€ 이미지 λ³€ν™˜
311
  pages_info = process_pdf(pdf_path, session_id)
312
  debug.append(f"Extracted pages: {len(pages_info)}")
313
 
314
  if not pages_info:
315
  raise RuntimeError("PDF 처리 κ²°κ³Όκ°€ λΉ„μ–΄ μžˆμŠ΅λ‹ˆλ‹€.")
316
 
317
+ # ν”Œλ¦½λΆ HTML 생성
318
+ html_block = generate_flipbook_html(pages_info, session_id, view_mode, skin)
 
319
  return html_block, "\n".join(debug)
320
 
321
  except Exception as e:
 
327
  "\n".join(debug),
328
  )
329
 
 
330
  # ────────────────────────────
331
  # 콜백: 이미지 μ—…λ‘œλ“œ
332
  # ────────────────────────────
333
  def create_flipbook_from_images(
334
+ images: Optional[List[gr.File]],
335
+ view_mode: str = "2d",
336
+ skin: str = "light"
337
  ):
338
  session_id = str(uuid.uuid4())
339
+ debug: List[str] = []
340
 
341
  if not images:
342
  return (
 
345
  )
346
 
347
  try:
348
+ # Gradioκ°€ λ„˜κ²¨μ€€ μž„μ‹œ 이미지 κ²½λ‘œλ“€
349
+ img_paths = []
350
+ for fobj in images:
351
+ # μ•ˆμ „ν•˜κ²Œ temp 폴더에 볡사
352
+ uploaded_temp_path = fobj.name
353
+ filename_only = os.path.basename(uploaded_temp_path)
354
+ local_img_path = os.path.join(UPLOAD_DIR, filename_only)
355
+ shutil.copyfile(uploaded_temp_path, local_img_path)
356
+ img_paths.append(local_img_path)
357
+
358
  debug.append(f"Images: {img_paths}")
359
 
360
+ # 이미지 β†’ νŽ˜μ΄μ§€ 정보 λ³€ν™˜
361
  pages_info = process_images(img_paths, session_id)
362
  debug.append(f"Processed: {len(pages_info)}")
363
 
364
  if not pages_info:
365
  raise RuntimeError("이미지 처리 μ‹€νŒ¨")
366
 
367
+ # ν”Œλ¦½λΆ HTML 생성
368
+ html_block = generate_flipbook_html(pages_info, session_id, view_mode, skin)
 
369
  return html_block, "\n".join(debug)
370
 
371
  except Exception as e:
 
377
  "\n".join(debug),
378
  )
379
 
 
380
  # ────────────────────────────
381
  # Gradio UI
382
  # ────────────────────────────
 
446
  # ────────────────────────────
447
  # μ‹€ν–‰
448
  # ────────────────────────────
449
+ if __name__ == "__main__":
450
+ # ν•„μš”ν•œ 경우 share=True λ“± 인자둜 μΆ”κ°€ κ°€λŠ₯
451
+ demo.launch(debug=True)