ginipick commited on
Commit
8d9dabe
Β·
verified Β·
1 Parent(s): 90337b7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +40 -58
app.py CHANGED
@@ -1,13 +1,15 @@
1
  #!/usr/bin/env python3
2
  # -*- coding: utf-8 -*-
3
  """
4
- 3D Flipbook Viewer (Gradio) – 전체 μ†ŒμŠ€ (SyntaxError μˆ˜μ • 버전)
5
  μ΅œμ’… μˆ˜μ •: 2025-05-18
 
 
 
 
 
6
  """
7
 
8
- # ────────────────────────────
9
- # κΈ°λ³Έ λͺ¨λ“ˆ
10
- # ────────────────────────────
11
  import os
12
  import shutil
13
  import uuid
@@ -17,7 +19,6 @@ 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
@@ -26,28 +27,28 @@ import fitz # PyMuPDF
26
  # λ‘œκΉ… μ„€μ •
27
  # ────────────────────────────
28
  logging.basicConfig(
29
- level=logging.INFO, # ν•„μš”ν•˜λ©΄ DEBUG
30
  format="%(asctime)s [%(levelname)s] %(message)s",
31
- filename="app.log", # 동일 디렉터리에 둜그 파일 생성
32
  filemode="a",
33
  )
34
  logging.info("πŸš€ Flipbook app started")
35
 
36
  # ────────────────────────────
37
- # μƒμˆ˜ / 경둜
38
  # ────────────────────────────
39
  TEMP_DIR = "temp"
40
  UPLOAD_DIR = os.path.join(TEMP_DIR, "uploads")
41
  OUTPUT_DIR = os.path.join(TEMP_DIR, "output")
42
  THUMBS_DIR = os.path.join(OUTPUT_DIR, "thumbs")
43
- HTML_DIR = os.path.join("public", "flipbooks") # μ›ΉμœΌλ‘œ λ…ΈμΆœλ˜λŠ” μœ„μΉ˜
44
 
45
- # 디렉터리 보μž₯
46
  for d in [TEMP_DIR, UPLOAD_DIR, OUTPUT_DIR, THUMBS_DIR, HTML_DIR]:
47
  os.makedirs(d, exist_ok=True)
48
 
49
  # ────────────────────────────
50
- # μœ ν‹Έ ν•¨μˆ˜
51
  # ────────────────────────────
52
  def create_thumbnail(src: str, dst: str, size=(300, 300)) -> Optional[str]:
53
  """원본 이미지λ₯Ό μΈλ„€μΌλ‘œ μ €μž₯ (이미지 μ—΄κΈ° μ‹€νŒ¨ μ‹œ None 리턴)"""
@@ -61,10 +62,10 @@ def create_thumbnail(src: str, dst: str, size=(300, 300)) -> Optional[str]:
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,7 +75,7 @@ def process_pdf(pdf_path: str, session_id: str) -> List[Dict]:
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")
@@ -83,7 +84,7 @@ def process_pdf(pdf_path: str, session_id: str) -> List[Dict]:
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;
@@ -116,10 +117,10 @@ def process_pdf(pdf_path: str, session_id: str) -> List[Dict]:
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)
@@ -134,7 +135,7 @@ def process_images(img_paths: List[str], session_id: str) -> List[Dict]:
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;
@@ -188,8 +189,11 @@ def generate_flipbook_html(
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)
@@ -198,7 +202,6 @@ def generate_flipbook_html(
198
  html_file = f"flipbook_{session_id}.html"
199
  html_path = os.path.join(HTML_DIR, html_file)
200
 
201
- # f-string λ‚΄λΆ€μ—μ„œ JS용 { }λ₯Ό ν‘œν˜„ν•˜λ €λ©΄ {{ }} 둜 써야 함
202
  html = f"""
203
  <!DOCTYPE html>
204
  <html lang="ko">
@@ -232,7 +235,6 @@ def generate_flipbook_html(
232
 
233
  <script>
234
  document.addEventListener('DOMContentLoaded', () => {{
235
- // hide() ν•¨μˆ˜: λ‘œλ”© λ©”μ‹œμ§€ 제거
236
  const hide = () => {{
237
  document.getElementById('loading').style.display = 'none';
238
  }};
@@ -270,6 +272,7 @@ document.addEventListener('DOMContentLoaded', () => {{
270
  Path(html_path).write_text(html, encoding="utf-8")
271
  public_url = f"/public/flipbooks/{html_file}"
272
 
 
273
  return f"""
274
  <div style="text-align:center;padding:20px;background:#f9f9f9;border-radius:5px">
275
  <h2 style="margin:0;color:#333">ν”Œλ¦½λΆμ΄ μ€€λΉ„λ˜μ—ˆμŠ΅λ‹ˆλ‹€!</h2>
@@ -283,7 +286,7 @@ document.addEventListener('DOMContentLoaded', () => {{
283
  """
284
 
285
  # ────────────────────────────
286
- # 콜백: PDF μ—…λ‘œλ“œ
287
  # ────────────────────────────
288
  def create_flipbook_from_pdf(
289
  pdf_file: Optional[gr.File],
@@ -300,24 +303,24 @@ def create_flipbook_from_pdf(
300
  )
301
 
302
  try:
303
- # Gradioκ°€ λ„˜κ²¨μ€€ μž„μ‹œ PDF 경둜
304
  uploaded_temp_path = pdf_file.name
305
 
306
- # μ„œλ²„ λ‚΄ μž„μ‹œ μ—…λ‘œλ“œ 폴더에 μ•ˆμ „ν•˜κ²Œ 볡사
307
  filename_only = os.path.basename(uploaded_temp_path)
308
  pdf_path = os.path.join(UPLOAD_DIR, filename_only)
309
  shutil.copyfile(uploaded_temp_path, pdf_path)
310
 
311
  debug.append(f"Copied PDF to: {pdf_path}")
312
 
313
- # PDF β†’ νŽ˜μ΄μ§€ 이미지 λ³€ν™˜
314
  pages_info = process_pdf(pdf_path, session_id)
315
  debug.append(f"Extracted pages: {len(pages_info)}")
316
 
317
  if not pages_info:
318
  raise RuntimeError("PDF 처리 κ²°κ³Όκ°€ λΉ„μ–΄ μžˆμŠ΅λ‹ˆλ‹€.")
319
 
320
- # ν”Œλ¦½λΆ HTML 생성
321
  html_block = generate_flipbook_html(pages_info, session_id, view_mode, skin)
322
  return html_block, "\n".join(debug)
323
 
@@ -331,7 +334,7 @@ def create_flipbook_from_pdf(
331
  )
332
 
333
  # ────────────────────────────
334
- # 콜백: 이미지 μ—…λ‘œλ“œ
335
  # ────────────────────────────
336
  def create_flipbook_from_images(
337
  images: Optional[List[gr.File]],
@@ -348,10 +351,9 @@ def create_flipbook_from_images(
348
  )
349
 
350
  try:
351
- # Gradioκ°€ λ„˜κ²¨μ€€ μž„μ‹œ 이미지 κ²½λ‘œλ“€
352
  img_paths = []
353
  for fobj in images:
354
- # μ•ˆμ „ν•˜κ²Œ temp 폴더에 볡사
355
  uploaded_temp_path = fobj.name
356
  filename_only = os.path.basename(uploaded_temp_path)
357
  local_img_path = os.path.join(UPLOAD_DIR, filename_only)
@@ -360,14 +362,14 @@ def create_flipbook_from_images(
360
 
361
  debug.append(f"Images: {img_paths}")
362
 
363
- # 이미지 β†’ νŽ˜μ΄μ§€ 정보 λ³€ν™˜
364
  pages_info = process_images(img_paths, session_id)
365
  debug.append(f"Processed: {len(pages_info)}")
366
 
367
  if not pages_info:
368
  raise RuntimeError("이미지 처리 μ‹€νŒ¨")
369
 
370
- # ν”Œλ¦½λΆ HTML 생성
371
  html_block = generate_flipbook_html(pages_info, session_id, view_mode, skin)
372
  return html_block, "\n".join(debug)
373
 
@@ -391,16 +393,8 @@ with gr.Blocks(title="3D Flipbook Viewer") as demo:
391
  with gr.TabItem("PDF μ—…λ‘œλ“œ"):
392
  pdf_file = gr.File(label="PDF 파일", file_types=[".pdf"])
393
  with gr.Accordion("κ³ κΈ‰ μ„€μ •", open=False):
394
- pdf_view = gr.Radio(
395
- ["webgl", "3d", "2d", "swipe"],
396
- value="2d",
397
- label="λ·° λͺ¨λ“œ",
398
- )
399
- pdf_skin = gr.Radio(
400
- ["light", "dark", "gradient"],
401
- value="light",
402
- label="슀��",
403
- )
404
  pdf_btn = gr.Button("PDF β†’ ν”Œλ¦½λΆ", variant="primary")
405
  pdf_out = gr.HTML()
406
  pdf_dbg = gr.Textbox(label="디버그", lines=10)
@@ -413,22 +407,10 @@ with gr.Blocks(title="3D Flipbook Viewer") as demo:
413
 
414
  # 이미지 νƒ­
415
  with gr.TabItem("이미지 μ—…λ‘œλ“œ"):
416
- imgs = gr.File(
417
- label="이미지 νŒŒμΌλ“€",
418
- file_types=["image"],
419
- file_count="multiple",
420
- )
421
  with gr.Accordion("κ³ κΈ‰ μ„€μ •", open=False):
422
- img_view = gr.Radio(
423
- ["webgl", "3d", "2d", "swipe"],
424
- value="2d",
425
- label="λ·° λͺ¨λ“œ",
426
- )
427
- img_skin = gr.Radio(
428
- ["light", "dark", "gradient"],
429
- value="light",
430
- label="μŠ€ν‚¨",
431
- )
432
  img_btn = gr.Button("이미지 β†’ ν”Œλ¦½λΆ", variant="primary")
433
  img_out = gr.HTML()
434
  img_dbg = gr.Textbox(label="디버그", lines=10)
@@ -450,5 +432,5 @@ with gr.Blocks(title="3D Flipbook Viewer") as demo:
450
  # μ‹€ν–‰
451
  # ────────────────────────────
452
  if __name__ == "__main__":
453
- # ν•„μš”ν•œ 경우 share=True λ“± 인자둜 μΆ”κ°€ κ°€λŠ₯
454
  demo.launch(debug=True)
 
1
  #!/usr/bin/env python3
2
  # -*- coding: utf-8 -*-
3
  """
4
+ 3D Flipbook Viewer (Gradio) – μ΅œμ’… μˆ˜μ • 버전
5
  μ΅œμ’… μˆ˜μ •: 2025-05-18
6
+
7
+ - f-string κ΅¬λ¬Έμ—μ„œ JS의 { }λ₯Ό {{ }}둜 μ΄μŠ€μΌ€μ΄ν”„ 처리
8
+ - Python 3.9 μ΄ν•˜ ν˜Έν™˜(typing.Optional, typing.List)
9
+ - Gradioκ°€ μƒμ„±ν•œ μž„μ‹œ 파일 β†’ temp/uploads ν΄λ”λ‘œ 볡사 ν›„ 처리
10
+ - /public 폴더에 μ΅œμ’… HTML 생성 (정적 μ„œλΉ™ 별도 μ„€μ • ν•„μš”)
11
  """
12
 
 
 
 
13
  import os
14
  import shutil
15
  import uuid
 
19
  from pathlib import Path
20
  from typing import Optional, List, Dict
21
 
 
22
  import gradio as gr
23
  from PIL import Image
24
  import fitz # PyMuPDF
 
27
  # λ‘œκΉ… μ„€μ •
28
  # ────────────────────────────
29
  logging.basicConfig(
30
+ level=logging.INFO,
31
  format="%(asctime)s [%(levelname)s] %(message)s",
32
+ filename="app.log",
33
  filemode="a",
34
  )
35
  logging.info("πŸš€ Flipbook app started")
36
 
37
  # ────────────────────────────
38
+ # 폴더 경둜 μ„€μ •
39
  # ────────────────────────────
40
  TEMP_DIR = "temp"
41
  UPLOAD_DIR = os.path.join(TEMP_DIR, "uploads")
42
  OUTPUT_DIR = os.path.join(TEMP_DIR, "output")
43
  THUMBS_DIR = os.path.join(OUTPUT_DIR, "thumbs")
44
+ HTML_DIR = os.path.join("public", "flipbooks")
45
 
46
+ # 폴더 생성
47
  for d in [TEMP_DIR, UPLOAD_DIR, OUTPUT_DIR, THUMBS_DIR, HTML_DIR]:
48
  os.makedirs(d, exist_ok=True)
49
 
50
  # ────────────────────────────
51
+ # μœ ν‹Έ ν•¨μˆ˜: 이미지 썸넀일
52
  # ────────────────────────────
53
  def create_thumbnail(src: str, dst: str, size=(300, 300)) -> Optional[str]:
54
  """원본 이미지λ₯Ό μΈλ„€μΌλ‘œ μ €μž₯ (이미지 μ—΄κΈ° μ‹€νŒ¨ μ‹œ None 리턴)"""
 
62
  return None
63
 
64
  # ────────────────────────────
65
+ # PDF 처리 ν•¨μˆ˜: PDF β†’ 이미지
66
  # ────────────────────────────
67
  def process_pdf(pdf_path: str, session_id: str) -> List[Dict]:
68
+ """PDF νŒŒμΌμ„ νŽ˜μ΄μ§€ 별 PNG둜 λ³€ν™˜ν•˜κ³ , νŽ˜μ΄μ§€ 정보 리슀트 λ°˜ν™˜"""
69
  pages_info = []
70
  out_dir = os.path.join(OUTPUT_DIR, session_id)
71
  th_dir = os.path.join(THUMBS_DIR, session_id)
 
75
  try:
76
  pdf_doc = fitz.open(pdf_path)
77
  for idx, page in enumerate(pdf_doc):
78
+ # 해상도(1.5λ°° 정도) - ν•„μš”μ‹œ 쑰절
79
  mat = fitz.Matrix(1.5, 1.5)
80
  pix = page.get_pixmap(matrix=mat)
81
  img_path = os.path.join(out_dir, f"page_{idx+1}.png")
 
84
  thumb_path = os.path.join(th_dir, f"thumb_{idx+1}.png")
85
  create_thumbnail(img_path, thumb_path)
86
 
87
+ # 첫 νŽ˜μ΄μ§€ μ˜ˆμ‹œλ‘œ μ˜€λ²„λ ˆμ΄ HTML
88
  html_overlay = (
89
  """
90
  <div style="position:absolute;top:50px;left:50px;
 
117
  return []
118
 
119
  # ────────────────────────────
120
+ # 이미지 처리 ν•¨μˆ˜
121
  # ────────────────────────────
122
  def process_images(img_paths: List[str], session_id: str) -> List[Dict]:
123
+ """이미지듀을 temp/output으둜 볡사, 썸넀일 생성, νŽ˜μ΄μ§€ 정보 λ°˜ν™˜"""
124
  pages_info = []
125
  out_dir = os.path.join(OUTPUT_DIR, session_id)
126
  th_dir = os.path.join(THUMBS_DIR, session_id)
 
135
  thumb = os.path.join(th_dir, f"thumb_{i+1}.png")
136
  create_thumbnail(dst, thumb)
137
 
138
+ # νŽ˜μ΄μ§€λ³„ μ˜€λ²„λ ˆμ΄ μ˜ˆμ‹œ
139
  if i == 0:
140
  html_overlay = """
141
  <div style="position:absolute;top:50px;left:50px;
 
189
  view_mode: str,
190
  skin: str
191
  ) -> str:
192
+ """
193
+ 3D Flipbook 용 HTML 파일 생성 ν›„, HTML 링크(λ²„νŠΌ) 블둝을 λ°˜ν™˜
194
+ - f-string μ•ˆμ—μ„œ JS의 { }λŠ” {{ }}둜 μ΄μŠ€μΌ€μ΄ν”„
195
+ """
196
+ # htmlContent=None 제거
197
  for p in pages_info:
198
  if p.get("htmlContent") is None:
199
  p.pop("htmlContent", None)
 
202
  html_file = f"flipbook_{session_id}.html"
203
  html_path = os.path.join(HTML_DIR, html_file)
204
 
 
205
  html = f"""
206
  <!DOCTYPE html>
207
  <html lang="ko">
 
235
 
236
  <script>
237
  document.addEventListener('DOMContentLoaded', () => {{
 
238
  const hide = () => {{
239
  document.getElementById('loading').style.display = 'none';
240
  }};
 
272
  Path(html_path).write_text(html, encoding="utf-8")
273
  public_url = f"/public/flipbooks/{html_file}"
274
 
275
+ # μ‚¬μš©μžμ—κ²Œ λŒλ €μ€„ HTML 블둝
276
  return f"""
277
  <div style="text-align:center;padding:20px;background:#f9f9f9;border-radius:5px">
278
  <h2 style="margin:0;color:#333">ν”Œλ¦½λΆμ΄ μ€€λΉ„λ˜μ—ˆμŠ΅λ‹ˆλ‹€!</h2>
 
286
  """
287
 
288
  # ────────────────────────────
289
+ # PDF μ—…λ‘œλ“œ 콜백
290
  # ────────────────────────────
291
  def create_flipbook_from_pdf(
292
  pdf_file: Optional[gr.File],
 
303
  )
304
 
305
  try:
306
+ # Gradio μž„μ‹œ 경둜
307
  uploaded_temp_path = pdf_file.name
308
 
309
+ # temp/uploads 폴더에 볡사
310
  filename_only = os.path.basename(uploaded_temp_path)
311
  pdf_path = os.path.join(UPLOAD_DIR, filename_only)
312
  shutil.copyfile(uploaded_temp_path, pdf_path)
313
 
314
  debug.append(f"Copied PDF to: {pdf_path}")
315
 
316
+ # PDF 처리
317
  pages_info = process_pdf(pdf_path, session_id)
318
  debug.append(f"Extracted pages: {len(pages_info)}")
319
 
320
  if not pages_info:
321
  raise RuntimeError("PDF 처리 κ²°κ³Όκ°€ λΉ„μ–΄ μžˆμŠ΅λ‹ˆλ‹€.")
322
 
323
+ # ν”Œλ¦½λΆ HTML
324
  html_block = generate_flipbook_html(pages_info, session_id, view_mode, skin)
325
  return html_block, "\n".join(debug)
326
 
 
334
  )
335
 
336
  # ────────────────────────────
337
+ # 이미지 μ—…λ‘œλ“œ 콜백
338
  # ────────────────────────────
339
  def create_flipbook_from_images(
340
  images: Optional[List[gr.File]],
 
351
  )
352
 
353
  try:
354
+ # μž„μ‹œ 이미지 κ²½λ‘œλ“€
355
  img_paths = []
356
  for fobj in images:
 
357
  uploaded_temp_path = fobj.name
358
  filename_only = os.path.basename(uploaded_temp_path)
359
  local_img_path = os.path.join(UPLOAD_DIR, filename_only)
 
362
 
363
  debug.append(f"Images: {img_paths}")
364
 
365
+ # 이미지 처리
366
  pages_info = process_images(img_paths, session_id)
367
  debug.append(f"Processed: {len(pages_info)}")
368
 
369
  if not pages_info:
370
  raise RuntimeError("이미지 처리 μ‹€νŒ¨")
371
 
372
+ # ν”Œλ¦½λΆ HTML
373
  html_block = generate_flipbook_html(pages_info, session_id, view_mode, skin)
374
  return html_block, "\n".join(debug)
375
 
 
393
  with gr.TabItem("PDF μ—…λ‘œλ“œ"):
394
  pdf_file = gr.File(label="PDF 파일", file_types=[".pdf"])
395
  with gr.Accordion("κ³ κΈ‰ μ„€μ •", open=False):
396
+ pdf_view = gr.Radio(["webgl", "3d", "2d", "swipe"], value="2d", label="λ·° λͺ¨λ“œ")
397
+ pdf_skin = gr.Radio(["light", "dark", "gradient"], value="light", label="μŠ€ν‚¨")
 
 
 
 
 
 
 
 
398
  pdf_btn = gr.Button("PDF β†’ ν”Œλ¦½λΆ", variant="primary")
399
  pdf_out = gr.HTML()
400
  pdf_dbg = gr.Textbox(label="디버그", lines=10)
 
407
 
408
  # 이미지 νƒ­
409
  with gr.TabItem("이미지 μ—…λ‘œλ“œ"):
410
+ imgs = gr.File(label="이미지 νŒŒμΌλ“€", file_types=["image"], file_count="multiple")
 
 
 
 
411
  with gr.Accordion("κ³ κΈ‰ μ„€μ •", open=False):
412
+ img_view = gr.Radio(["webgl", "3d", "2d", "swipe"], value="2d", label="λ·° λͺ¨λ“œ")
413
+ img_skin = gr.Radio(["light", "dark", "gradient"], value="light", label="μŠ€ν‚¨")
 
 
 
 
 
 
 
 
414
  img_btn = gr.Button("이미지 β†’ ν”Œλ¦½λΆ", variant="primary")
415
  img_out = gr.HTML()
416
  img_dbg = gr.Textbox(label="디버그", lines=10)
 
432
  # μ‹€ν–‰
433
  # ────────────────────────────
434
  if __name__ == "__main__":
435
+ # share=True λ“± μ˜΅μ…˜μ„ λ„£μ–΄ 배포/곡유 κ°€λŠ₯
436
  demo.launch(debug=True)