tester343 commited on
Commit
3dd5234
·
verified ·
1 Parent(s): d52a719

Update app_enhanced.py

Browse files
Files changed (1) hide show
  1. app_enhanced.py +26 -29
app_enhanced.py CHANGED
@@ -42,11 +42,10 @@ def sanitize_json(obj):
42
  return obj
43
 
44
  # ======================================================
45
- # 🧠 CORE GPU GENERATOR (ALL AI IMPORTS INSIDE)
46
  # ======================================================
47
  @spaces.GPU(duration=300)
48
  def generate_comic_gpu(video_path, user_dir, frames_dir, metadata_path, target_pages):
49
- # Heavy AI imports inside function to avoid startup timeout
50
  from backend.keyframes.keyframes import black_bar_crop
51
  from backend.simple_color_enhancer import SimpleColorEnhancer
52
  from backend.subtitles.subs_real import get_real_subtitles
@@ -59,7 +58,6 @@ def generate_comic_gpu(video_path, user_dir, frames_dir, metadata_path, target_p
59
  duration = total_frames / fps
60
  cap.release()
61
 
62
- # 1. Subtitles
63
  user_srt = os.path.join(user_dir, 'subs.srt')
64
  try:
65
  get_real_subtitles(video_path)
@@ -71,7 +69,6 @@ def generate_comic_gpu(video_path, user_dir, frames_dir, metadata_path, target_p
71
  try: all_subs = list(srt.parse(f.read()))
72
  except: all_subs = []
73
 
74
- # 2. Logic for 5 Panels Per Page
75
  panels_per_page = 5
76
  target_pages = int(target_pages)
77
  total_needed = target_pages * panels_per_page
@@ -86,7 +83,6 @@ def generate_comic_gpu(video_path, user_dir, frames_dir, metadata_path, target_p
86
  indices = np.linspace(0, len(all_subs) - 1, total_needed, dtype=int)
87
  moments = [{'text': all_subs[i].content, 'start': all_subs[i].start.total_seconds()} for i in indices]
88
 
89
- # 3. Frame Extraction
90
  frame_metadata = {}
91
  cap = cv2.VideoCapture(video_path)
92
  frame_files = []
@@ -107,7 +103,6 @@ def generate_comic_gpu(video_path, user_dir, frames_dir, metadata_path, target_p
107
  try: black_bar_crop()
108
  except: pass
109
 
110
- # 4. Enhance & Assemble
111
  se = SimpleColorEnhancer()
112
  pages_data = []
113
  for p_idx in range(target_pages):
@@ -136,7 +131,7 @@ def generate_comic_gpu(video_path, user_dir, frames_dir, metadata_path, target_p
136
  return sanitize_json(pages_data)
137
 
138
  # ======================================================
139
- # 🔧 APP SETUP
140
  # ======================================================
141
  app = Flask(__name__)
142
 
@@ -184,46 +179,51 @@ def get_output(sid, filename):
184
  return send_from_directory(os.path.join(BASE_USER_DIR, sid, 'output'), filename)
185
 
186
  # ======================================================
187
- # 🌐 UI HTML (USING YOUR EXACT DRAGGED COORDINATES)
188
  # ======================================================
189
  INDEX_HTML = '''
190
  <!DOCTYPE html><html lang="en">
191
  <head>
192
- <meta charset="UTF-8"><title>Elite Pro Comic Generator</title>
193
  <script src="https://cdnjs.cloudflare.com/ajax/libs/html-to-image/1.11.11/html-to-image.min.js"></script>
194
  <link href="https://fonts.googleapis.com/css2?family=Comic+Neue:wght@700&family=Lato:wght@400;900&display=swap" rel="stylesheet">
195
  <style>
196
- /* ⚡ YOUR FINAL COORDINATES APPLIED ⚡ */
197
  :root {
198
  --w: 1000px;
199
  --h: 700px;
200
  --tierY: 350px;
201
- --gut: 5.25px; /* (10.5px gutter / 2) */
202
 
203
  --r1-topX: 641.2px;
204
  --r1-botX: 588.2px;
205
-
206
  --r2L-topX: 284.2px;
207
  --r2L-botX: 314.2px;
208
-
209
  --r2R-topX: 618.2px;
210
  --r2R-botX: 678.2px;
211
  }
212
 
213
  body { background: #0a0a0b; font-family: 'Lato', sans-serif; margin: 0; padding: 20px; color: white; }
214
- .setup-view { max-width: 450px; margin: 80px auto; background: white; padding: 40px; border-radius: 12px; color: black; text-align: center; }
215
 
216
- /* THE PERFECT GEOMETRIC PAGE */
217
  .comic-page {
218
  background: white; width: var(--w); height: var(--h); margin: 40px auto;
219
- border: 10px solid black; position: relative; overflow: hidden;
220
  display: block; box-shadow: 0 0 50px rgba(0,0,0,0.5);
221
  }
222
 
223
  .panel { position: absolute; background: #000; overflow: hidden; cursor: pointer; border: 1px solid black; }
224
- .panel img { width: 115%; height: 115%; object-fit: cover; position: absolute; top: -7.5%; left: -7.5%; object-position: center 15%; pointer-events: none; }
 
 
 
 
 
 
 
 
 
 
225
 
226
- /* MAPPING POLYGONS TO YOUR DRAG COORDINATES */
227
  /* ROW 1 */
228
  .p1 { top: 0; left: 0; width: 100%; height: var(--tierY); clip-path: polygon(0 0, calc(var(--r1-topX) - var(--gut)) 0, calc(var(--r1-botX) - var(--gut)) var(--tierY), 0 var(--tierY)); }
229
  .p2 { top: 0; left: 0; width: 100%; height: var(--tierY); clip-path: polygon(calc(var(--r1-topX) + var(--gut)) 0, var(--w) 0, var(--w) var(--tierY), calc(var(--r1-botX) + var(--gut)) var(--tierY)); }
@@ -233,11 +233,11 @@ INDEX_HTML = '''
233
  .p4 { top: calc(var(--tierY) + var(--gut)); left: 0; width: 100%; height: calc(var(--h) - var(--tierY)); clip-path: polygon(calc(var(--r2L-topX) + var(--gut)) 0, calc(var(--r2R-topX) - var(--gut)) 0, calc(var(--r2R-botX) - var(--gut)) 100%, calc(var(--r2L-botX) + var(--gut)) 100%); }
234
  .p5 { top: calc(var(--tierY) + var(--gut)); left: 0; width: 100%; height: calc(var(--h) - var(--tierY)); clip-path: polygon(calc(var(--r2R-topX) + var(--gut)) 0, var(--w) 0, var(--w) 100%, calc(var(--r2R-botX) + var(--gut)) 100%); }
235
 
236
- /* BUBBLE STYLE - CAPSULE WITH BEAKED TAIL */
237
  .bubble {
238
  position: absolute; background: white; border: 2.5px solid black; border-radius: 25px;
239
  padding: 10px 18px; font-family: 'Comic Neue'; font-weight: bold; font-size: 15px;
240
  color: black; min-width: 110px; text-align: center; cursor: move; z-index: 100;
 
241
  }
242
  .bubble::after { content: ""; position: absolute; bottom: -15px; left: 30px; border-left: 10px solid transparent; border-right: 10px solid transparent; border-top: 15px solid black; }
243
  .bubble::before { content: ""; position: absolute; bottom: -11px; left: 31px; border-left: 9px solid transparent; border-right: 9px solid transparent; border-top: 14px solid white; z-index: 2; }
@@ -247,23 +247,21 @@ INDEX_HTML = '''
247
  .btn-gen { background: #00d2ff; color: black; }
248
  .hidden { display: none; }
249
  .loader { border: 5px solid #333; border-top: 5px solid #00d2ff; border-radius: 50%; width: 35px; height: 35px; animation: spin 1s linear infinite; margin: 20px auto; }
250
- @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
251
  </style>
252
  </head>
253
  <body>
254
- <div id="u-zone" class="setup-view">
255
- <h1>🎬 Elite Comic Maker</h1>
256
- <p>100% Precision Geometry Engine</p>
257
  <input type="file" id="vid" accept="video/mp4"><br><br>
258
  <label>Total Pages: </label><input type="number" id="pg" value="2" style="width:40px"><br><br>
259
  <button class="btn-gen" onclick="start()">🚀 GENERATE COMIC</button>
260
- <div id="loading" class="hidden"><div class="loader"></div><p id="st">Processing Composition...</p></div>
261
  </div>
262
 
263
  <div id="editor-zone" class="hidden">
264
  <div id="output"></div>
265
  <div class="controls">
266
- <h4 style="margin:0; color:#00d2ff;">EDITOR</h4>
267
  <button onclick="addB()" style="background:#2ecc71; color:white;">💬 Add Bubble</button>
268
  <button onclick="exportPNG()" style="background:#3498db; color:white;">📥 Download PNGs</button>
269
  <button onclick="location.reload()" style="background:#e74c3c; color:white;">🏠 Reset</button>
@@ -284,12 +282,11 @@ INDEX_HTML = '''
284
  const r = await fetch(`/status?sid=${sid}`); const d = await r.json();
285
  document.getElementById('st').innerText = d.message || "Working...";
286
  if(d.progress >= 100) { clearInterval(itv); load(); }
287
- }, 3000);
288
  }
289
 
290
  async function load() {
291
  const r = await fetch(`/output/${sid}/pages.json`); const pages = await r.json();
292
- document.getElementById('upload-zone')?.classList.add('hidden');
293
  document.getElementById('u-zone').classList.add('hidden');
294
  document.getElementById('editor-zone').classList.remove('hidden');
295
  const out = document.getElementById('output');
@@ -320,13 +317,13 @@ INDEX_HTML = '''
320
  return b;
321
  }
322
 
323
- function addB() { if(selP) selP.appendChild(createB("Dialogue", 60, 60)); else alert("Select a panel first!"); }
324
 
325
  async function exportPNG() {
326
  const pgs = document.querySelectorAll('.comic-page');
327
  for(let pg of pgs) {
328
  const url = await htmlToImage.toPng(pg, {pixelRatio: 3});
329
- const l = document.createElement('a'); l.download='ProComicPage.png'; l.href=url; l.click();
330
  }
331
  }
332
  </script>
 
42
  return obj
43
 
44
  # ======================================================
45
+ # 🧠 CORE GPU GENERATOR
46
  # ======================================================
47
  @spaces.GPU(duration=300)
48
  def generate_comic_gpu(video_path, user_dir, frames_dir, metadata_path, target_pages):
 
49
  from backend.keyframes.keyframes import black_bar_crop
50
  from backend.simple_color_enhancer import SimpleColorEnhancer
51
  from backend.subtitles.subs_real import get_real_subtitles
 
58
  duration = total_frames / fps
59
  cap.release()
60
 
 
61
  user_srt = os.path.join(user_dir, 'subs.srt')
62
  try:
63
  get_real_subtitles(video_path)
 
69
  try: all_subs = list(srt.parse(f.read()))
70
  except: all_subs = []
71
 
 
72
  panels_per_page = 5
73
  target_pages = int(target_pages)
74
  total_needed = target_pages * panels_per_page
 
83
  indices = np.linspace(0, len(all_subs) - 1, total_needed, dtype=int)
84
  moments = [{'text': all_subs[i].content, 'start': all_subs[i].start.total_seconds()} for i in indices]
85
 
 
86
  frame_metadata = {}
87
  cap = cv2.VideoCapture(video_path)
88
  frame_files = []
 
103
  try: black_bar_crop()
104
  except: pass
105
 
 
106
  se = SimpleColorEnhancer()
107
  pages_data = []
108
  for p_idx in range(target_pages):
 
131
  return sanitize_json(pages_data)
132
 
133
  # ======================================================
134
+ # 🔧 APP ENGINE
135
  # ======================================================
136
  app = Flask(__name__)
137
 
 
179
  return send_from_directory(os.path.join(BASE_USER_DIR, sid, 'output'), filename)
180
 
181
  # ======================================================
182
+ # 🌐 UI HTML (100% ZOOM INTEGRATED)
183
  # ======================================================
184
  INDEX_HTML = '''
185
  <!DOCTYPE html><html lang="en">
186
  <head>
187
+ <meta charset="UTF-8"><title>Pro Comic Generator (100% Zoom)</title>
188
  <script src="https://cdnjs.cloudflare.com/ajax/libs/html-to-image/1.11.11/html-to-image.min.js"></script>
189
  <link href="https://fonts.googleapis.com/css2?family=Comic+Neue:wght@700&family=Lato:wght@400;900&display=swap" rel="stylesheet">
190
  <style>
 
191
  :root {
192
  --w: 1000px;
193
  --h: 700px;
194
  --tierY: 350px;
195
+ --gut: 5.25px;
196
 
197
  --r1-topX: 641.2px;
198
  --r1-botX: 588.2px;
 
199
  --r2L-topX: 284.2px;
200
  --r2L-botX: 314.2px;
 
201
  --r2R-topX: 618.2px;
202
  --r2R-botX: 678.2px;
203
  }
204
 
205
  body { background: #0a0a0b; font-family: 'Lato', sans-serif; margin: 0; padding: 20px; color: white; }
206
+ .setup-box { max-width: 450px; margin: 80px auto; background: white; padding: 40px; border-radius: 12px; color: black; text-align: center; }
207
 
 
208
  .comic-page {
209
  background: white; width: var(--w); height: var(--h); margin: 40px auto;
210
+ border: 12px solid black; position: relative; overflow: hidden;
211
  display: block; box-shadow: 0 0 50px rgba(0,0,0,0.5);
212
  }
213
 
214
  .panel { position: absolute; background: #000; overflow: hidden; cursor: pointer; border: 1px solid black; }
215
+
216
+ /* ⚡ 100% ZOOM SETTINGS ⚡ */
217
+ .panel img {
218
+ width: 100%;
219
+ height: 100%;
220
+ object-fit: cover;
221
+ position: absolute;
222
+ top: 0;
223
+ left: 0;
224
+ pointer-events: none;
225
+ }
226
 
 
227
  /* ROW 1 */
228
  .p1 { top: 0; left: 0; width: 100%; height: var(--tierY); clip-path: polygon(0 0, calc(var(--r1-topX) - var(--gut)) 0, calc(var(--r1-botX) - var(--gut)) var(--tierY), 0 var(--tierY)); }
229
  .p2 { top: 0; left: 0; width: 100%; height: var(--tierY); clip-path: polygon(calc(var(--r1-topX) + var(--gut)) 0, var(--w) 0, var(--w) var(--tierY), calc(var(--r1-botX) + var(--gut)) var(--tierY)); }
 
233
  .p4 { top: calc(var(--tierY) + var(--gut)); left: 0; width: 100%; height: calc(var(--h) - var(--tierY)); clip-path: polygon(calc(var(--r2L-topX) + var(--gut)) 0, calc(var(--r2R-topX) - var(--gut)) 0, calc(var(--r2R-botX) - var(--gut)) 100%, calc(var(--r2L-botX) + var(--gut)) 100%); }
234
  .p5 { top: calc(var(--tierY) + var(--gut)); left: 0; width: 100%; height: calc(var(--h) - var(--tierY)); clip-path: polygon(calc(var(--r2R-topX) + var(--gut)) 0, var(--w) 0, var(--w) 100%, calc(var(--r2R-botX) + var(--gut)) 100%); }
235
 
 
236
  .bubble {
237
  position: absolute; background: white; border: 2.5px solid black; border-radius: 25px;
238
  padding: 10px 18px; font-family: 'Comic Neue'; font-weight: bold; font-size: 15px;
239
  color: black; min-width: 110px; text-align: center; cursor: move; z-index: 100;
240
+ box-shadow: 4px 4px 0 rgba(0,0,0,0.1);
241
  }
242
  .bubble::after { content: ""; position: absolute; bottom: -15px; left: 30px; border-left: 10px solid transparent; border-right: 10px solid transparent; border-top: 15px solid black; }
243
  .bubble::before { content: ""; position: absolute; bottom: -11px; left: 31px; border-left: 9px solid transparent; border-right: 9px solid transparent; border-top: 14px solid white; z-index: 2; }
 
247
  .btn-gen { background: #00d2ff; color: black; }
248
  .hidden { display: none; }
249
  .loader { border: 5px solid #333; border-top: 5px solid #00d2ff; border-radius: 50%; width: 35px; height: 35px; animation: spin 1s linear infinite; margin: 20px auto; }
 
250
  </style>
251
  </head>
252
  <body>
253
+ <div id="u-zone" class="setup-box">
254
+ <h1>🎬 Pro Comic Maker</h1>
255
+ <p>100% Zoom / Zero Crop Mode</p>
256
  <input type="file" id="vid" accept="video/mp4"><br><br>
257
  <label>Total Pages: </label><input type="number" id="pg" value="2" style="width:40px"><br><br>
258
  <button class="btn-gen" onclick="start()">🚀 GENERATE COMIC</button>
259
+ <div id="loading" class="hidden"><div class="loader"></div><p id="st">Working...</p></div>
260
  </div>
261
 
262
  <div id="editor-zone" class="hidden">
263
  <div id="output"></div>
264
  <div class="controls">
 
265
  <button onclick="addB()" style="background:#2ecc71; color:white;">💬 Add Bubble</button>
266
  <button onclick="exportPNG()" style="background:#3498db; color:white;">📥 Download PNGs</button>
267
  <button onclick="location.reload()" style="background:#e74c3c; color:white;">🏠 Reset</button>
 
282
  const r = await fetch(`/status?sid=${sid}`); const d = await r.json();
283
  document.getElementById('st').innerText = d.message || "Working...";
284
  if(d.progress >= 100) { clearInterval(itv); load(); }
285
+ }, 2000);
286
  }
287
 
288
  async function load() {
289
  const r = await fetch(`/output/${sid}/pages.json`); const pages = await r.json();
 
290
  document.getElementById('u-zone').classList.add('hidden');
291
  document.getElementById('editor-zone').classList.remove('hidden');
292
  const out = document.getElementById('output');
 
317
  return b;
318
  }
319
 
320
+ function addB() { if(selP) selP.appendChild(createB("Dialogue", 60, 60)); }
321
 
322
  async function exportPNG() {
323
  const pgs = document.querySelectorAll('.comic-page');
324
  for(let pg of pgs) {
325
  const url = await htmlToImage.toPng(pg, {pixelRatio: 3});
326
+ const l = document.createElement('a'); l.download='ComicPage.png'; l.href=url; l.click();
327
  }
328
  }
329
  </script>