tester343 commited on
Commit
b50a23d
Β·
verified Β·
1 Parent(s): 49ec272

Update app_enhanced.py

Browse files
Files changed (1) hide show
  1. app_enhanced.py +29 -31
app_enhanced.py CHANGED
@@ -29,7 +29,7 @@ BASE_USER_DIR = os.path.join(BASE_STORAGE_PATH, "userdata")
29
  os.makedirs(BASE_USER_DIR, exist_ok=True)
30
 
31
  # ======================================================
32
- # πŸ”§ JSON SANITIZER (FIX FOR int64 SERIALIZATION ERROR)
33
  # ======================================================
34
  def sanitize_json(obj):
35
  if isinstance(obj, dict):
@@ -49,7 +49,6 @@ def sanitize_json(obj):
49
  # ======================================================
50
  @spaces.GPU(duration=300)
51
  def generate_comic_gpu(video_path, user_dir, frames_dir, metadata_path, target_pages):
52
- # Heavy AI imports inside function to avoid startup timeout
53
  import cv2
54
  import srt
55
  import numpy as np
@@ -162,7 +161,7 @@ def uploader():
162
  def task():
163
  try:
164
  with open(os.path.join(o_dir, 'status.json'), 'w') as f:
165
- json.dump({'message': 'Generating Hairline Geometry...', 'progress': 30}, f)
166
  data = generate_comic_gpu(vid_p, u_dir, f_dir, os.path.join(f_dir, 'meta.json'), pages)
167
  with open(os.path.join(o_dir, 'pages.json'), 'w') as f: json.dump(data, f)
168
  with open(os.path.join(o_dir, 'status.json'), 'w') as f:
@@ -190,29 +189,29 @@ def get_output(sid, filename):
190
  return send_from_directory(os.path.join(BASE_USER_DIR, sid, 'output'), filename)
191
 
192
  # ======================================================
193
- # 🌐 UI HTML (HAIRLINE GAP + ENGLISH UI)
194
  # ======================================================
195
  INDEX_HTML = '''
196
  <!DOCTYPE html><html lang="en">
197
  <head>
198
- <meta charset="UTF-8"><title>Elite AI Comic Generator</title>
199
  <script src="https://cdnjs.cloudflare.com/ajax/libs/html-to-image/1.11.11/html-to-image.min.js"></script>
200
  <link href="https://fonts.googleapis.com/css2?family=Comic+Neue:wght@700&family=Lato:wght@400;900&display=swap" rel="stylesheet">
201
  <style>
202
  :root {
203
- --slant: 38px;
204
- --gutter: 0.7px; /* ULTRA THIN HAIRLINE GAP (3x smaller) */
205
  --page-border: 12px;
206
- --panel-ink: 1.5px; /* Razor thin panel border */
207
  }
208
 
209
- body { background: #0a0a0a; font-family: 'Lato', sans-serif; margin: 0; padding: 20px; color: white; }
210
- .setup-box { max-width: 450px; margin: 80px auto; background: white; padding: 45px; border-radius: 12px; color: black; text-align: center; box-shadow: 0 20px 50px rgba(0,0,0,0.5); }
211
 
212
- /* ⚑ THE HAIRLINE ASYMMETRICAL TEMPLATE ⚑ */
213
  .comic-page {
214
  background: white; width: 1000px; height: 720px; margin: 40px auto;
215
- border: var(--page-border) solid black; padding: 4px; box-sizing: border-box;
216
  display: grid; gap: var(--gutter);
217
  grid-template-columns: repeat(6, 1fr);
218
  grid-template-rows: 1.35fr 1fr;
@@ -220,9 +219,10 @@ INDEX_HTML = '''
220
  }
221
 
222
  .panel { position: relative; background: #000; overflow: hidden; cursor: pointer; border: var(--panel-ink) solid black; }
223
- .panel img { width: 116%; height: 116%; object-fit: cover; position: absolute; top: -8%; left: -8%; object-position: center 15%; pointer-events: none; }
 
224
 
225
- /* ROW 1: Precise Parallel Slant LEFT \ */
226
  .panel:nth-child(1) {
227
  grid-column: span 4;
228
  clip-path: polygon(0 0, 100% 0, calc(100% - var(--slant)) 100%, 0 100%);
@@ -232,7 +232,7 @@ INDEX_HTML = '''
232
  clip-path: polygon(var(--slant) 0, 100% 0, 100% 100%, 0 100%);
233
  }
234
 
235
- /* ROW 2: Precise Parallel Slant RIGHT / */
236
  .panel:nth-child(3) {
237
  grid-column: span 2;
238
  clip-path: polygon(0 0, calc(100% - var(--slant)) 0, 100% 100%, 0 100%);
@@ -246,19 +246,19 @@ INDEX_HTML = '''
246
  clip-path: polygon(var(--slant) 0, 100% 0, 100% 100%, var(--slant) 100%);
247
  }
248
 
249
- .panel.selected { outline: 6px solid #00f2fe; z-index: 5; filter: brightness(1.1); }
250
 
251
- /* BUBBLE STYLING - ELITE CAPSULE */
252
  .bubble {
253
- position: absolute; background: white; border: 2.2px solid black; border-radius: 25px;
254
  padding: 10px 20px; font-family: 'Comic Neue'; font-weight: bold; font-size: 15px;
255
  color: black; min-width: 110px; text-align: center; cursor: move; z-index: 10;
256
- box-shadow: 3px 3px 0 rgba(0,0,0,0.1);
257
  }
258
  .bubble::after {
259
  content: ""; position: absolute; bottom: -18px; left: 30px;
260
  width: 0; height: 0; border-left: 10px solid transparent;
261
- border-right: 10px solid transparent; border-top: 18px solid black;
262
  }
263
  .bubble::before {
264
  content: ""; position: absolute; bottom: -13px; left: 31px;
@@ -269,27 +269,26 @@ INDEX_HTML = '''
269
 
270
  .controls { position: fixed; bottom: 20px; right: 20px; background: #000; padding: 25px; border-radius: 12px; width: 240px; border: 2px solid #333; }
271
  button { width: 100%; padding: 12px; margin-top: 10px; cursor: pointer; font-weight: bold; border-radius: 6px; border: none; transition: 0.2s; }
272
- button:hover { opacity: 0.8; transform: scale(1.02); }
273
  .hidden { display: none; }
274
- .loader { border: 5px solid #333; border-top: 5px solid #00f2fe; border-radius: 50%; width: 40px; height: 40px; animation: spin 1s linear infinite; margin: 20px auto; }
275
  @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
276
  </style>
277
  </head>
278
  <body>
279
  <div id="upload-zone" class="setup-box">
280
- <h1>🎬 Elite Comic Maker</h1>
281
- <p>Hairline Gutters & Parallel Geometry</p>
282
  <input type="file" id="vid" accept="video/mp4"><br><br>
283
- <label>Number of Pages: </label><input type="number" id="pg" value="2" style="width:50px">
284
  <br><br>
285
- <button onclick="start()" style="background:#00f2fe; color:black; font-size:16px;">πŸš€ GENERATE COMIC</button>
286
- <div id="loading" class="hidden"><div class="loader"></div><p id="st">Processing Frames...</p></div>
287
  </div>
288
 
289
  <div id="editor-zone" class="hidden">
290
  <div id="output"></div>
291
  <div class="controls">
292
- <h4 style="margin:0; color:#00f2fe;">EDITOR</h4>
293
  <button onclick="addB()" style="background:#2ecc71; color:white;">πŸ’¬ Add Bubble</button>
294
  <button onclick="exportPNG()" style="background:#3498db; color:white;">πŸ“₯ Download PNGs</button>
295
  <button onclick="location.reload()" style="background:#e74c3c; color:white;">🏠 Reset</button>
@@ -310,7 +309,6 @@ INDEX_HTML = '''
310
  const r = await fetch(`/status?sid=${sid}`); const d = await r.json();
311
  document.getElementById('st').innerText = d.message || "Processing...";
312
  if(d.progress >= 100) { clearInterval(itv); load(); }
313
- if(d.progress < 0) { clearInterval(itv); alert("GPU Error. Restarting."); location.reload(); }
314
  }, 2000);
315
  }
316
 
@@ -342,7 +340,7 @@ INDEX_HTML = '''
342
  document.onmousemove = (ev) => { b.style.left=(ev.clientX-ox)+'px'; b.style.top=(ev.clientY-oy)+'px'; };
343
  document.onmouseup = () => { document.onmousemove = null; };
344
  };
345
- b.ondblclick = () => { let n = prompt("Edit Dialogue:", b.innerText); if(n) b.innerText = n; };
346
  return b;
347
  }
348
 
@@ -352,7 +350,7 @@ INDEX_HTML = '''
352
  const pgs = document.querySelectorAll('.comic-page');
353
  for(let pg of pgs) {
354
  const url = await htmlToImage.toPng(pg, {pixelRatio: 2});
355
- const l = document.createElement('a'); l.download='Elite_Comic_Page.png'; l.href=url; l.click();
356
  }
357
  }
358
  </script>
 
29
  os.makedirs(BASE_USER_DIR, exist_ok=True)
30
 
31
  # ======================================================
32
+ # πŸ”§ JSON SANITIZER (FIX FOR int64 ERROR)
33
  # ======================================================
34
  def sanitize_json(obj):
35
  if isinstance(obj, dict):
 
49
  # ======================================================
50
  @spaces.GPU(duration=300)
51
  def generate_comic_gpu(video_path, user_dir, frames_dir, metadata_path, target_pages):
 
52
  import cv2
53
  import srt
54
  import numpy as np
 
161
  def task():
162
  try:
163
  with open(os.path.join(o_dir, 'status.json'), 'w') as f:
164
+ json.dump({'message': 'Generating 0.1px Hairlines...', 'progress': 30}, f)
165
  data = generate_comic_gpu(vid_p, u_dir, f_dir, os.path.join(f_dir, 'meta.json'), pages)
166
  with open(os.path.join(o_dir, 'pages.json'), 'w') as f: json.dump(data, f)
167
  with open(os.path.join(o_dir, 'status.json'), 'w') as f:
 
189
  return send_from_directory(os.path.join(BASE_USER_DIR, sid, 'output'), filename)
190
 
191
  # ======================================================
192
+ # 🌐 UI HTML (0.1px HAIRLINE + ENGLISH UI)
193
  # ======================================================
194
  INDEX_HTML = '''
195
  <!DOCTYPE html><html lang="en">
196
  <head>
197
+ <meta charset="UTF-8"><title>Elite Razor-Thin Comic Generator</title>
198
  <script src="https://cdnjs.cloudflare.com/ajax/libs/html-to-image/1.11.11/html-to-image.min.js"></script>
199
  <link href="https://fonts.googleapis.com/css2?family=Comic+Neue:wght@700&family=Lato:wght@400;900&display=swap" rel="stylesheet">
200
  <style>
201
  :root {
202
+ --slant: 40px;
203
+ --gutter: 0.1px; /* RAZOR THIN HAIRLINE */
204
  --page-border: 12px;
205
+ --panel-ink: 1px;
206
  }
207
 
208
+ body { background: #000; font-family: 'Lato', sans-serif; margin: 0; padding: 20px; color: white; }
209
+ .setup-box { max-width: 450px; margin: 80px auto; background: white; padding: 40px; border-radius: 12px; color: black; text-align: center; }
210
 
211
+ /* ⚑ THE 0.1px PIXEL-PERFECT TEMPLATE ⚑ */
212
  .comic-page {
213
  background: white; width: 1000px; height: 720px; margin: 40px auto;
214
+ border: var(--page-border) solid black; padding: 2px; box-sizing: border-box;
215
  display: grid; gap: var(--gutter);
216
  grid-template-columns: repeat(6, 1fr);
217
  grid-template-rows: 1.35fr 1fr;
 
219
  }
220
 
221
  .panel { position: relative; background: #000; overflow: hidden; cursor: pointer; border: var(--panel-ink) solid black; }
222
+ /* Oversize images slightly and center them top-weighted for faces */
223
+ .panel img { width: 118%; height: 118%; object-fit: cover; position: absolute; top: -9%; left: -9%; object-position: center 15%; pointer-events: none; }
224
 
225
+ /* ROW 1: Slant LEFT \ (Panel 1 Wide, Panel 2 Narrow) */
226
  .panel:nth-child(1) {
227
  grid-column: span 4;
228
  clip-path: polygon(0 0, 100% 0, calc(100% - var(--slant)) 100%, 0 100%);
 
232
  clip-path: polygon(var(--slant) 0, 100% 0, 100% 100%, 0 100%);
233
  }
234
 
235
+ /* ROW 2: Slant RIGHT / (Three equal action panels) */
236
  .panel:nth-child(3) {
237
  grid-column: span 2;
238
  clip-path: polygon(0 0, calc(100% - var(--slant)) 0, 100% 100%, 0 100%);
 
246
  clip-path: polygon(var(--slant) 0, 100% 0, 100% 100%, var(--slant) 100%);
247
  }
248
 
249
+ .panel.selected { outline: 6px solid #00d2ff; z-index: 5; filter: brightness(1.1); }
250
 
251
+ /* BUBBLE STYLING - PRECISE WHITE CAPSULE */
252
  .bubble {
253
+ position: absolute; background: white; border: 2.5px solid black; border-radius: 25px;
254
  padding: 10px 20px; font-family: 'Comic Neue'; font-weight: bold; font-size: 15px;
255
  color: black; min-width: 110px; text-align: center; cursor: move; z-index: 10;
256
+ box-shadow: 4px 4px 0 rgba(0,0,0,0.1);
257
  }
258
  .bubble::after {
259
  content: ""; position: absolute; bottom: -18px; left: 30px;
260
  width: 0; height: 0; border-left: 10px solid transparent;
261
+ border-right: 10px solid transparent; border-top: 20px solid black;
262
  }
263
  .bubble::before {
264
  content: ""; position: absolute; bottom: -13px; left: 31px;
 
269
 
270
  .controls { position: fixed; bottom: 20px; right: 20px; background: #000; padding: 25px; border-radius: 12px; width: 240px; border: 2px solid #333; }
271
  button { width: 100%; padding: 12px; margin-top: 10px; cursor: pointer; font-weight: bold; border-radius: 6px; border: none; transition: 0.2s; }
 
272
  .hidden { display: none; }
273
+ .loader { border: 5px solid #333; border-top: 5px solid #00d2ff; border-radius: 50%; width: 40px; height: 40px; animation: spin 1s linear infinite; margin: 20px auto; }
274
  @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
275
  </style>
276
  </head>
277
  <body>
278
  <div id="upload-zone" class="setup-box">
279
+ <h1>🎬 Hairline Comic Maker</h1>
280
+ <p>0.1px Gap Precision & Parallel Geometry</p>
281
  <input type="file" id="vid" accept="video/mp4"><br><br>
282
+ <label>Total Pages: </label><input type="number" id="pg" value="2" style="width:50px">
283
  <br><br>
284
+ <button onclick="start()" style="background:#00d2ff; color:black; font-size:16px;">πŸš€ GENERATE COMIC</button>
285
+ <div id="loading" class="hidden"><div class="loader"></div><p id="st">Acquiring GPU...</p></div>
286
  </div>
287
 
288
  <div id="editor-zone" class="hidden">
289
  <div id="output"></div>
290
  <div class="controls">
291
+ <h4 style="margin:0; color:#00d2ff;">EDITOR</h4>
292
  <button onclick="addB()" style="background:#2ecc71; color:white;">πŸ’¬ Add Bubble</button>
293
  <button onclick="exportPNG()" style="background:#3498db; color:white;">πŸ“₯ Download PNGs</button>
294
  <button onclick="location.reload()" style="background:#e74c3c; color:white;">🏠 Reset</button>
 
309
  const r = await fetch(`/status?sid=${sid}`); const d = await r.json();
310
  document.getElementById('st').innerText = d.message || "Processing...";
311
  if(d.progress >= 100) { clearInterval(itv); load(); }
 
312
  }, 2000);
313
  }
314
 
 
340
  document.onmousemove = (ev) => { b.style.left=(ev.clientX-ox)+'px'; b.style.top=(ev.clientY-oy)+'px'; };
341
  document.onmouseup = () => { document.onmousemove = null; };
342
  };
343
+ b.ondblclick = () => { let n = prompt("Edit Text:", b.innerText); if(n) b.innerText = n; };
344
  return b;
345
  }
346
 
 
350
  const pgs = document.querySelectorAll('.comic-page');
351
  for(let pg of pgs) {
352
  const url = await htmlToImage.toPng(pg, {pixelRatio: 2});
353
+ const l = document.createElement('a'); l.download='Elite_Comic.png'; l.href=url; l.click();
354
  }
355
  }
356
  </script>