ginipick commited on
Commit
7b72a7e
Β·
verified Β·
1 Parent(s): 6fd87e6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +163 -115
app.py CHANGED
@@ -5,6 +5,7 @@ import tempfile
5
  import uuid
6
  from pathlib import Path
7
  import json
 
8
  from PIL import Image
9
  import fitz # PyMuPDF for PDF handling
10
 
@@ -13,9 +14,10 @@ TEMP_DIR = "temp"
13
  UPLOAD_DIR = os.path.join(TEMP_DIR, "uploads")
14
  OUTPUT_DIR = os.path.join(TEMP_DIR, "output")
15
  THUMBS_DIR = os.path.join(OUTPUT_DIR, "thumbs")
 
16
 
17
  # Ensure directories exist
18
- for dir_path in [TEMP_DIR, UPLOAD_DIR, OUTPUT_DIR, THUMBS_DIR]:
19
  os.makedirs(dir_path, exist_ok=True)
20
 
21
  def create_thumbnail(image_path, output_path, size=(300, 300)):
@@ -53,39 +55,29 @@ def process_pdf(pdf_path, session_id):
53
  thumb_path = os.path.join(thumbs_folder, f"thumb_{page_num + 1}.png")
54
  create_thumbnail(image_path, thumb_path)
55
 
56
- # Add interactive content to first page as an example
57
  html_content = ""
58
- items = []
59
 
60
  if page_num == 0: # First page example
61
  html_content = """
62
  <div style="position: absolute; top: 50px; left: 50px; background-color: rgba(255,255,255,0.7); padding: 10px; border-radius: 5px;">
63
- <h2 style="color: #333;">Interactive Flipbook Example</h2>
64
- <p style="color: #666;">This page demonstrates interactive content capabilities.</p>
65
- <a href="#" data-page="2" style="color: blue; text-decoration: underline;">Go to page 2</a>
66
  </div>
67
  """
68
- elif page_num == 1: # Second page example with items
69
- items = [
70
- {
71
- "type": "link",
72
- "x": 50,
73
- "y": 50,
74
- "width": 200,
75
- "height": 50,
76
- "page": 1,
77
- "title": "Back to first page"
78
- }
79
- ]
80
 
81
  # Add page info with interactive content
82
  pages_info.append({
83
- "src": os.path.join("output", session_id, f"page_{page_num + 1}.png"),
84
- "thumb": os.path.join("thumbs", session_id, f"thumb_{page_num + 1}.png"),
85
- "title": f"Page {page_num + 1}",
86
- "htmlContent": html_content if html_content else None,
87
- "items": items if items else None
88
  })
 
89
 
90
  return pages_info
91
  except Exception as e:
@@ -129,16 +121,20 @@ def process_images(image_paths, session_id):
129
  </div>
130
  """
131
 
 
 
 
 
132
  # Create a simpler page structure to avoid potential compatibility issues
133
  page_info = {
134
- "src": dest_path.replace("\\", "/"), # Ensure forward slashes for web paths
135
- "thumb": thumb_path.replace("\\", "/"),
136
  "title": f"이미지 {i + 1}",
137
  "htmlContent": html_content if html_content else None
138
  }
139
 
140
  pages_info.append(page_info)
141
- print(f"Processed image {i+1}: {dest_path}")
142
 
143
  except Exception as e:
144
  print(f"Error processing image {img_path}: {e}")
@@ -166,10 +162,11 @@ def create_flipbook_from_pdf(pdf_file, view_mode="2d", skin="light"):
166
  if not pages_info:
167
  return """<div style="color: red; padding: 20px;">PDF 파일 처리 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€. λ‹€μ‹œ μ‹œλ„ν•΄μ£Όμ„Έμš”.</div>""", "No pages processed"
168
 
169
- # Create and return HTML for the flipbook
170
- flipbook_html = generate_flipbook_html(pages_info, session_id, view_mode, skin)
171
- debug_info += f"HTML generated with view mode: {view_mode}, skin: {skin}\n"
172
- return flipbook_html, debug_info
 
173
 
174
  except Exception as e:
175
  error_msg = f"Error creating flipbook from PDF: {e}"
@@ -196,10 +193,11 @@ def create_flipbook_from_images(images, view_mode="2d", skin="light"):
196
  if not pages_info:
197
  return """<div style="color: red; padding: 20px;">이미지 처리 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€. λ‹€μ‹œ μ‹œλ„ν•΄μ£Όμ„Έμš”.</div>""", "No images processed"
198
 
199
- # Create and return HTML for the flipbook
200
- flipbook_html = generate_flipbook_html(pages_info, session_id, view_mode, skin)
201
- debug_info += f"HTML generated with view mode: {view_mode}, skin: {skin}\n"
202
- return flipbook_html, debug_info
 
203
 
204
  except Exception as e:
205
  error_msg = f"Error creating flipbook from images: {e}"
@@ -207,7 +205,7 @@ def create_flipbook_from_images(images, view_mode="2d", skin="light"):
207
  return f"""<div style="color: red; padding: 20px;">였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€: {str(e)}</div>""", error_msg
208
 
209
  def generate_flipbook_html(pages_info, session_id, view_mode, skin):
210
- """Generate HTML for the flipbook."""
211
  # Clean up pages_info to remove None values for JSON serialization
212
  for page in pages_info:
213
  if "htmlContent" in page and page["htmlContent"] is None:
@@ -218,93 +216,143 @@ def generate_flipbook_html(pages_info, session_id, view_mode, skin):
218
  # Convert pages_info to JSON for JavaScript
219
  pages_json = json.dumps(pages_info)
220
 
221
- # Create a custom ID for this flipbook
222
- flipbook_id = f"flipbook_{session_id}"
 
223
 
224
- # HTML template with embedded CSS and JavaScript
225
- html = f"""
226
- <div style="height:700px; width:100%; position:relative; overflow:hidden; border:1px solid #ccc;">
227
- <div id="{flipbook_id}" style="width:100%; height:100%;"></div>
228
-
229
- <script>
230
- // Function to load scripts sequentially
231
- function loadScript(src, callback) {{
232
- let script = document.createElement('script');
233
- script.src = src;
234
- script.onload = callback;
235
- document.head.appendChild(script);
 
 
 
236
  }}
237
-
238
- // Function to add CSS
239
- function loadCSS(href) {{
240
- let link = document.createElement('link');
241
- link.rel = 'stylesheet';
242
- link.type = 'text/css';
243
- link.href = href;
244
- document.head.appendChild(link);
245
  }}
246
-
247
- // Load CSS first
248
- loadCSS('flipbook.css');
249
-
250
- // Load scripts in sequence
251
- loadScript('flipbook.js', function() {{
252
- loadScript('flipbook.webgl.js', function() {{
253
- loadScript('flipbook.swipe.js', function() {{
254
- loadScript('flipbook.scroll.js', function() {{
255
- loadScript('flipbook.book3.js', function() {{
256
- // All scripts loaded, now initialize flipbook
257
- const options = {{
258
- pages: {pages_json},
259
- viewMode: '{view_mode}',
260
- skin: '{skin}',
261
- responsiveView: true,
262
- singlePageMode: false,
263
- singlePageModeIfMobile: true,
264
- pageFlipDuration: 1,
265
- sound: true,
266
- backgroundMusic: false,
267
- thumbnailsOnStart: true,
268
- btnThumbs: {{ enabled: true }},
269
- btnPrint: {{ enabled: true }},
270
- btnDownloadPages: {{ enabled: true }},
271
- btnDownloadPdf: {{ enabled: true }},
272
- btnShare: {{ enabled: true }},
273
- btnSound: {{ enabled: true }},
274
- btnExpand: {{ enabled: true }},
275
- rightToLeft: false,
276
- autoplayOnStart: false,
277
- autoplayInterval: 3000
278
- }};
279
-
280
- try {{
281
- const container = document.getElementById('{flipbook_id}');
282
- if (container) {{
283
- console.log("Container found, initializing flipbook");
284
- new FlipBook(container, options);
285
- }} else {{
286
- console.error("Container not found: #{flipbook_id}");
287
- }}
288
- }} catch (error) {{
289
- console.error("Error initializing flipbook:", error);
290
- }}
291
- }});
292
- }});
293
- }});
294
- }});
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
295
  }});
296
  </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
297
  </div>
298
- <div style="margin-top:20px; padding:10px; background-color:#f5f5f5; border-radius:5px;">
299
- <p><strong>μ°Έκ³ :</strong> ν”Œλ¦½λΆμ΄ 보이지 μ•ŠλŠ” 경우:</p>
300
- <ol>
301
- <li>νŽ˜μ΄μ§€λ₯Ό μƒˆλ‘œκ³ μΉ¨ν•˜μ„Έμš”.</li>
302
- <li>λ‹€λ₯Έ λ·° λͺ¨λ“œλ₯Ό μ„ νƒν•΄λ³΄μ„Έμš” (μ›ΉGL λŒ€μ‹  2D λ˜λŠ” 3D).</li>
303
- <li>λΈŒλΌμš°μ € μ½˜μ†”μ—μ„œ 였λ₯˜ λ©”μ‹œμ§€λ₯Ό ν™•μΈν•˜μ„Έμš”.</li>
304
- </ol>
 
 
 
305
  </div>
306
  """
307
- return html
 
308
 
309
  # Define the Gradio interface
310
  with gr.Blocks(title="3D Flipbook Viewer") as demo:
@@ -397,4 +445,4 @@ with gr.Blocks(title="3D Flipbook Viewer") as demo:
397
 
398
  # Launch the app
399
  if __name__ == "__main__":
400
- demo.launch()
 
5
  import uuid
6
  from pathlib import Path
7
  import json
8
+ import base64
9
  from PIL import Image
10
  import fitz # PyMuPDF for PDF handling
11
 
 
14
  UPLOAD_DIR = os.path.join(TEMP_DIR, "uploads")
15
  OUTPUT_DIR = os.path.join(TEMP_DIR, "output")
16
  THUMBS_DIR = os.path.join(OUTPUT_DIR, "thumbs")
17
+ HTML_DIR = os.path.join(OUTPUT_DIR, "html")
18
 
19
  # Ensure directories exist
20
+ for dir_path in [TEMP_DIR, UPLOAD_DIR, OUTPUT_DIR, THUMBS_DIR, HTML_DIR]:
21
  os.makedirs(dir_path, exist_ok=True)
22
 
23
  def create_thumbnail(image_path, output_path, size=(300, 300)):
 
55
  thumb_path = os.path.join(thumbs_folder, f"thumb_{page_num + 1}.png")
56
  create_thumbnail(image_path, thumb_path)
57
 
58
+ # Add simple interactive content to first page
59
  html_content = ""
 
60
 
61
  if page_num == 0: # First page example
62
  html_content = """
63
  <div style="position: absolute; top: 50px; left: 50px; background-color: rgba(255,255,255,0.7); padding: 10px; border-radius: 5px;">
64
+ <div style="color: #333; font-size: 18px; font-weight: bold;">μΈν„°λž™ν‹°λΈŒ ν”Œλ¦½λΆ 예제</div>
65
+ <div style="color: #666; margin-top: 5px;">이 νŽ˜μ΄μ§€λŠ” μΈν„°λž™ν‹°λΈŒ 컨텐츠 κΈ°λŠ₯을 λ³΄μ—¬μ€λ‹ˆλ‹€.</div>
 
66
  </div>
67
  """
68
+
69
+ # Get relative web paths for the HTML file
70
+ rel_image_path = os.path.relpath(image_path, HTML_DIR).replace("\\", "/")
71
+ rel_thumb_path = os.path.relpath(thumb_path, HTML_DIR).replace("\\", "/")
 
 
 
 
 
 
 
 
72
 
73
  # Add page info with interactive content
74
  pages_info.append({
75
+ "src": rel_image_path,
76
+ "thumb": rel_thumb_path,
77
+ "title": f"νŽ˜μ΄μ§€ {page_num + 1}",
78
+ "htmlContent": html_content if html_content else None
 
79
  })
80
+ print(f"Processed PDF page {page_num+1}: {rel_image_path}")
81
 
82
  return pages_info
83
  except Exception as e:
 
121
  </div>
122
  """
123
 
124
+ # Get relative web paths for the HTML file
125
+ rel_image_path = os.path.relpath(dest_path, HTML_DIR).replace("\\", "/")
126
+ rel_thumb_path = os.path.relpath(thumb_path, HTML_DIR).replace("\\", "/")
127
+
128
  # Create a simpler page structure to avoid potential compatibility issues
129
  page_info = {
130
+ "src": rel_image_path,
131
+ "thumb": rel_thumb_path,
132
  "title": f"이미지 {i + 1}",
133
  "htmlContent": html_content if html_content else None
134
  }
135
 
136
  pages_info.append(page_info)
137
+ print(f"Processed image {i+1}: {rel_image_path}")
138
 
139
  except Exception as e:
140
  print(f"Error processing image {img_path}: {e}")
 
162
  if not pages_info:
163
  return """<div style="color: red; padding: 20px;">PDF 파일 처리 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€. λ‹€μ‹œ μ‹œλ„ν•΄μ£Όμ„Έμš”.</div>""", "No pages processed"
164
 
165
+ # Generate HTML file and return iframe HTML
166
+ iframe_html = generate_flipbook_html(pages_info, session_id, view_mode, skin)
167
+ debug_info += f"HTML file generated with view mode: {view_mode}, skin: {skin}\n"
168
+
169
+ return iframe_html, debug_info
170
 
171
  except Exception as e:
172
  error_msg = f"Error creating flipbook from PDF: {e}"
 
193
  if not pages_info:
194
  return """<div style="color: red; padding: 20px;">이미지 처리 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€. λ‹€μ‹œ μ‹œλ„ν•΄μ£Όμ„Έμš”.</div>""", "No images processed"
195
 
196
+ # Generate HTML file and return iframe HTML
197
+ iframe_html = generate_flipbook_html(pages_info, session_id, view_mode, skin)
198
+ debug_info += f"HTML file generated with view mode: {view_mode}, skin: {skin}\n"
199
+
200
+ return iframe_html, debug_info
201
 
202
  except Exception as e:
203
  error_msg = f"Error creating flipbook from images: {e}"
 
205
  return f"""<div style="color: red; padding: 20px;">였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€: {str(e)}</div>""", error_msg
206
 
207
  def generate_flipbook_html(pages_info, session_id, view_mode, skin):
208
+ """Generate a standalone HTML file for the flipbook and return iframe HTML."""
209
  # Clean up pages_info to remove None values for JSON serialization
210
  for page in pages_info:
211
  if "htmlContent" in page and page["htmlContent"] is None:
 
216
  # Convert pages_info to JSON for JavaScript
217
  pages_json = json.dumps(pages_info)
218
 
219
+ # Create a unique filename for this session
220
+ html_filename = f"flipbook_{session_id}.html"
221
+ html_path = os.path.join(HTML_DIR, html_filename)
222
 
223
+ # Create the full HTML file content
224
+ html_content = f"""
225
+ <!DOCTYPE html>
226
+ <html lang="ko">
227
+ <head>
228
+ <meta charset="UTF-8">
229
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
230
+ <title>3D ν”Œλ¦½λΆ</title>
231
+ <link rel="stylesheet" type="text/css" href="../../../flipbook.css">
232
+ <style>
233
+ body, html {{
234
+ margin: 0;
235
+ padding: 0;
236
+ height: 100%;
237
+ overflow: hidden;
238
  }}
239
+ #flipbook-container {{
240
+ width: 100%;
241
+ height: 100%;
242
+ position: absolute;
243
+ top: 0;
244
+ left: 0;
 
 
245
  }}
246
+ .loading {{
247
+ position: absolute;
248
+ top: 50%;
249
+ left: 50%;
250
+ transform: translate(-50%, -50%);
251
+ text-align: center;
252
+ font-family: Arial, sans-serif;
253
+ }}
254
+ .loading .spinner {{
255
+ width: 50px;
256
+ height: 50px;
257
+ border: 5px solid #f3f3f3;
258
+ border-top: 5px solid #3498db;
259
+ border-radius: 50%;
260
+ animation: spin 1s linear infinite;
261
+ margin: 0 auto 20px;
262
+ }}
263
+ @keyframes spin {{
264
+ 0% {{ transform: rotate(0deg); }}
265
+ 100% {{ transform: rotate(360deg); }}
266
+ }}
267
+ </style>
268
+ <script src="../../../flipbook.js"></script>
269
+ <script src="../../../flipbook.webgl.js"></script>
270
+ <script src="../../../flipbook.swipe.js"></script>
271
+ <script src="../../../flipbook.scroll.js"></script>
272
+ <script src="../../../flipbook.book3.js"></script>
273
+ </head>
274
+ <body>
275
+ <div id="flipbook-container"></div>
276
+ <div id="loading" class="loading">
277
+ <div class="spinner"></div>
278
+ <div>ν”Œλ¦½λΆ λ‘œλ”© 쀑...</div>
279
+ </div>
280
+
281
+ <script>
282
+ document.addEventListener('DOMContentLoaded', function() {{
283
+ // Hide loading when everything is ready
284
+ function hideLoading() {{
285
+ document.getElementById('loading').style.display = 'none';
286
+ }}
287
+
288
+ try {{
289
+ const options = {{
290
+ pages: {pages_json},
291
+ viewMode: '{view_mode}',
292
+ skin: '{skin}',
293
+ responsiveView: true,
294
+ singlePageMode: false,
295
+ singlePageModeIfMobile: true,
296
+ pageFlipDuration: 1,
297
+ sound: true,
298
+ backgroundMusic: false,
299
+ thumbnailsOnStart: true,
300
+ btnThumbs: {{ enabled: true }},
301
+ btnPrint: {{ enabled: true }},
302
+ btnDownloadPages: {{ enabled: true }},
303
+ btnDownloadPdf: {{ enabled: true }},
304
+ btnShare: {{ enabled: true }},
305
+ btnSound: {{ enabled: true }},
306
+ btnExpand: {{ enabled: true }},
307
+ rightToLeft: false,
308
+ autoplayOnStart: false,
309
+ autoplayInterval: 3000
310
+ }};
311
+
312
+ const container = document.getElementById('flipbook-container');
313
+ if (container) {{
314
+ console.log('Initializing flipbook...');
315
+ new FlipBook(container, options);
316
+ setTimeout(hideLoading, 1000); // Give it time to render
317
+ }} else {{
318
+ console.error('Flipbook container not found');
319
+ alert('였λ₯˜: ν”Œλ¦½λΆ μ»¨ν…Œμ΄λ„ˆλ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.');
320
+ }}
321
+ }} catch (error) {{
322
+ console.error('Error initializing flipbook:', error);
323
+ alert('ν”Œλ¦½λΆ μ΄ˆκΈ°ν™” 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€: ' + error.message);
324
+ document.getElementById('loading').innerHTML = '<div>였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.</div>';
325
+ }}
326
  }});
327
  </script>
328
+ </body>
329
+ </html>
330
+ """
331
+
332
+ # Write the HTML file
333
+ with open(html_path, 'w', encoding='utf-8') as f:
334
+ f.write(html_content)
335
+
336
+ # Return iframe HTML to embed in Gradio
337
+ iframe_height = 700
338
+ iframe_html = f"""
339
+ <div style="width:100%; height:{iframe_height}px; border:1px solid #ddd; border-radius:5px; overflow:hidden;">
340
+ <iframe src="file/{html_path}" width="100%" height="100%" frameborder="0" allowfullscreen></iframe>
341
  </div>
342
+ <div style="margin-top:15px; padding:15px; background-color:#f5f5f5; border-radius:5px; line-height:1.5;">
343
+ <h3 style="margin-top:0; color:#333;">μ‚¬μš© 팁:</h3>
344
+ <ul style="margin:10px 0; padding-left:20px;">
345
+ <li>νŽ˜μ΄μ§€ λͺ¨μ„œλ¦¬λ₯Ό λ“œλž˜κ·Έν•˜μ—¬ λ„˜κΈΈ 수 μžˆμŠ΅λ‹ˆλ‹€.</li>
346
+ <li>ν•˜λ‹¨ νˆ΄λ°”μ˜ μ•„μ΄μ½˜μ„ μ‚¬μš©ν•˜μ—¬ λ‹€μ–‘ν•œ κΈ°λŠ₯을 ν™œμš©ν•˜μ„Έμš”.</li>
347
+ <li>전체화면 λ²„νŠΌμ„ ν΄λ¦­ν•˜μ—¬ 더 큰 ν™”λ©΄μœΌλ‘œ λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.</li>
348
+ </ul>
349
+ <div style="margin-top:10px; padding:10px; background-color:#e8f4fd; border-left:4px solid #2196F3; border-radius:2px;">
350
+ <strong>μ°Έκ³ :</strong> ν”Œλ¦½λΆμ΄ 보이지 μ•ŠλŠ” 경우 <a href="{html_path}" target="_blank">μ—¬κΈ°λ₯Ό 클릭</a>ν•˜μ—¬ μƒˆ μ°½μ—μ„œ μ—΄μ–΄λ³΄μ„Έμš”.
351
+ </div>
352
  </div>
353
  """
354
+
355
+ return iframe_html
356
 
357
  # Define the Gradio interface
358
  with gr.Blocks(title="3D Flipbook Viewer") as demo:
 
445
 
446
  # Launch the app
447
  if __name__ == "__main__":
448
+ demo.launch(share=True) # Set share=True to create a public link