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

Update app_enhanced.py

Browse files
Files changed (1) hide show
  1. app_enhanced.py +36 -39
app_enhanced.py CHANGED
@@ -1,8 +1,3 @@
1
-
2
-
3
-
4
-
5
-
6
  import spaces # <--- CRITICAL: MUST BE THE FIRST IMPORT
7
  import os
8
  import time
@@ -34,7 +29,7 @@ BASE_USER_DIR = os.path.join(BASE_STORAGE_PATH, "userdata")
34
  os.makedirs(BASE_USER_DIR, exist_ok=True)
35
 
36
  # ======================================================
37
- # πŸ”§ JSON SANITIZER (FIX FOR int64 ERROR)
38
  # ======================================================
39
  def sanitize_json(obj):
40
  if isinstance(obj, dict):
@@ -147,7 +142,7 @@ def generate_comic_gpu(video_path, user_dir, frames_dir, metadata_path, target_p
147
  return sanitize_json(pages_data)
148
 
149
  # ======================================================
150
- # πŸ”§ APP SETUP
151
  # ======================================================
152
  app = Flask(__name__)
153
 
@@ -167,7 +162,7 @@ def uploader():
167
  def task():
168
  try:
169
  with open(os.path.join(o_dir, 'status.json'), 'w') as f:
170
- json.dump({'message': 'Drafting Perfect Geometry...', 'progress': 30}, f)
171
  data = generate_comic_gpu(vid_p, u_dir, f_dir, os.path.join(f_dir, 'meta.json'), pages)
172
  with open(os.path.join(o_dir, 'pages.json'), 'w') as f: json.dump(data, f)
173
  with open(os.path.join(o_dir, 'status.json'), 'w') as f:
@@ -195,39 +190,39 @@ def get_output(sid, filename):
195
  return send_from_directory(os.path.join(BASE_USER_DIR, sid, 'output'), filename)
196
 
197
  # ======================================================
198
- # 🌐 UI HTML (MINIMAL GAP + ENGLISH UI)
199
  # ======================================================
200
  INDEX_HTML = '''
201
  <!DOCTYPE html><html lang="en">
202
  <head>
203
- <meta charset="UTF-8"><title>Pro AI Comic Generator</title>
204
  <script src="https://cdnjs.cloudflare.com/ajax/libs/html-to-image/1.11.11/html-to-image.min.js"></script>
205
  <link href="https://fonts.googleapis.com/css2?family=Comic+Neue:wght@700&family=Lato:wght@400;900&display=swap" rel="stylesheet">
206
  <style>
207
  :root {
208
- --slant: 40px;
209
- --gutter: 2px; /* RAZOR THIN GAP AS REQUESTED */
210
- --border-panel: 3px;
211
- --border-page: 10px;
212
  }
213
 
214
- body { background: #111; font-family: 'Lato', sans-serif; margin: 0; padding: 20px; color: white; }
215
- .setup-box { max-width: 450px; margin: 80px auto; background: white; padding: 40px; border-radius: 12px; color: black; text-align: center; }
216
 
217
- /* ⚑ THE PIXEL-PERFECT ASYMMETRICAL TEMPLATE ⚑ */
218
  .comic-page {
219
  background: white; width: 1000px; height: 720px; margin: 40px auto;
220
- border: var(--border-page) solid black; padding: 5px; box-sizing: border-box;
221
  display: grid; gap: var(--gutter);
222
  grid-template-columns: repeat(6, 1fr);
223
  grid-template-rows: 1.35fr 1fr;
224
  position: relative; overflow: hidden;
225
  }
226
 
227
- .panel { position: relative; background: #000; overflow: hidden; cursor: pointer; border: var(--border-panel) solid black; }
228
- .panel img { width: 118%; height: 118%; object-fit: cover; position: absolute; top: -9%; left: -9%; object-position: center 15%; pointer-events: none; }
229
 
230
- /* ROW 1: Slant LEFT \ (Panel 1 Wide, Panel 2 Narrow) */
231
  .panel:nth-child(1) {
232
  grid-column: span 4;
233
  clip-path: polygon(0 0, 100% 0, calc(100% - var(--slant)) 100%, 0 100%);
@@ -237,7 +232,7 @@ INDEX_HTML = '''
237
  clip-path: polygon(var(--slant) 0, 100% 0, 100% 100%, 0 100%);
238
  }
239
 
240
- /* ROW 2: Slant RIGHT / (Three equal action panels) */
241
  .panel:nth-child(3) {
242
  grid-column: span 2;
243
  clip-path: polygon(0 0, calc(100% - var(--slant)) 0, 100% 100%, 0 100%);
@@ -251,19 +246,19 @@ INDEX_HTML = '''
251
  clip-path: polygon(var(--slant) 0, 100% 0, 100% 100%, var(--slant) 100%);
252
  }
253
 
254
- .panel.selected { outline: 8px solid #3498db; z-index: 5; filter: brightness(1.1); }
255
 
256
- /* BUBBLE STYLING - PRECISE WHITE CAPSULE */
257
  .bubble {
258
- position: absolute; background: white; border: 2.5px solid black; border-radius: 25px;
259
  padding: 10px 20px; font-family: 'Comic Neue'; font-weight: bold; font-size: 15px;
260
  color: black; min-width: 110px; text-align: center; cursor: move; z-index: 10;
261
- box-shadow: 4px 4px 0 rgba(0,0,0,0.1);
262
  }
263
  .bubble::after {
264
  content: ""; position: absolute; bottom: -18px; left: 30px;
265
  width: 0; height: 0; border-left: 10px solid transparent;
266
- border-right: 10px solid transparent; border-top: 20px solid black;
267
  }
268
  .bubble::before {
269
  content: ""; position: absolute; bottom: -13px; left: 31px;
@@ -273,30 +268,31 @@ INDEX_HTML = '''
273
  }
274
 
275
  .controls { position: fixed; bottom: 20px; right: 20px; background: #000; padding: 25px; border-radius: 12px; width: 240px; border: 2px solid #333; }
276
- button { width: 100%; padding: 12px; margin-top: 10px; cursor: pointer; font-weight: bold; border-radius: 6px; border: none; }
 
277
  .hidden { display: none; }
278
- .loader { border: 5px solid #333; border-top: 5px solid #e67e22; border-radius: 50%; width: 40px; height: 40px; animation: spin 1s linear infinite; margin: 20px auto; }
279
  @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
280
  </style>
281
  </head>
282
  <body>
283
  <div id="upload-zone" class="setup-box">
284
- <h1>🎬 AI Comic Maker</h1>
285
- <p>Ultra-Minimal Gap Geometry</p>
286
  <input type="file" id="vid" accept="video/mp4"><br><br>
287
- <label>Total Pages: </label><input type="number" id="pg" value="2" style="width:50px">
288
  <br><br>
289
- <button onclick="start()" style="background:#e67e22; color:white;">Generate Comic</button>
290
- <div id="loading" class="hidden"><div class="loader"></div><p id="st">Acquiring GPU...</p></div>
291
  </div>
292
 
293
  <div id="editor-zone" class="hidden">
294
  <div id="output"></div>
295
  <div class="controls">
296
- <h4 style="margin:0; color:white;">Interactive Editor</h4>
297
  <button onclick="addB()" style="background:#2ecc71; color:white;">πŸ’¬ Add Bubble</button>
298
  <button onclick="exportPNG()" style="background:#3498db; color:white;">πŸ“₯ Download PNGs</button>
299
- <button onclick="location.reload()" style="background:#555; color:white;">🏠 Reset</button>
300
  </div>
301
  </div>
302
 
@@ -314,6 +310,7 @@ INDEX_HTML = '''
314
  const r = await fetch(`/status?sid=${sid}`); const d = await r.json();
315
  document.getElementById('st').innerText = d.message || "Processing...";
316
  if(d.progress >= 100) { clearInterval(itv); load(); }
 
317
  }, 2000);
318
  }
319
 
@@ -345,17 +342,17 @@ INDEX_HTML = '''
345
  document.onmousemove = (ev) => { b.style.left=(ev.clientX-ox)+'px'; b.style.top=(ev.clientY-oy)+'px'; };
346
  document.onmouseup = () => { document.onmousemove = null; };
347
  };
348
- b.ondblclick = () => { let n = prompt("Edit text:", b.innerText); if(n) b.innerText = n; };
349
  return b;
350
  }
351
 
352
- function addB() { if(selP) selP.appendChild(createB("New Text", 60, 60)); else alert("Select a panel first!"); }
353
 
354
  async function exportPNG() {
355
  const pgs = document.querySelectorAll('.comic-page');
356
  for(let pg of pgs) {
357
  const url = await htmlToImage.toPng(pg, {pixelRatio: 2});
358
- const l = document.createElement('a'); l.download='ComicPage.png'; l.href=url; l.click();
359
  }
360
  }
361
  </script>
 
 
 
 
 
 
1
  import spaces # <--- CRITICAL: MUST BE THE FIRST IMPORT
2
  import os
3
  import time
 
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):
 
142
  return sanitize_json(pages_data)
143
 
144
  # ======================================================
145
+ # πŸ”§ APP ENGINE
146
  # ======================================================
147
  app = Flask(__name__)
148
 
 
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
  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;
219
  position: relative; overflow: hidden;
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
  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
  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;
 
268
  }
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>
296
  </div>
297
  </div>
298
 
 
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
  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
 
349
+ function addB() { if(selP) selP.appendChild(createB("Dialogue", 60, 60)); else alert("Select a panel first!"); }
350
 
351
  async function exportPNG() {
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>