tester343 commited on
Commit
60d2c02
·
verified ·
1 Parent(s): 84afe3b

Update app_enhanced.py

Browse files
Files changed (1) hide show
  1. app_enhanced.py +88 -36
app_enhanced.py CHANGED
@@ -100,7 +100,6 @@ def generate_comic_gpu(video_path, user_dir, frames_dir, metadata_path, status_p
100
  frame_metadata[fname] = {'dialogue': moment['text'], 'time': mid}
101
  frame_files_ordered.append(fname)
102
  count += 1
103
- # Update progress periodically
104
  if count % 2 == 0:
105
  prog = 30 + int((count / total_panels_needed) * 20)
106
  update_status_file(status_path, f"Extracted Frame {count}/{total_panels_needed}", prog)
@@ -375,70 +374,74 @@ INDEX_HTML = '''
375
  overflow: hidden;
376
  border: 2px solid #000;
377
  padding: 0;
378
- /* SCALING FOR EDITOR VIEW ONLY - REMOVED FOR EXPORT */
379
  transform-origin: top center;
380
- /* transform: scale(0.6); - DISABLED to show True Size */
381
- margin-bottom: 30px;
382
  }
383
 
384
  /* === 5-PANEL TEMPLATE (2 TOP, 3 BOTTOM) === */
385
- /* Background White for 0px Gutter */
386
  .comic-grid { width: 100%; height: 100%; position: relative; background: #ffffff; }
387
 
388
  /* Panel Background White */
389
- .panel { position: absolute; overflow: hidden; background: #ffffff; cursor: pointer; border: 0; }
390
  .panel.selected { z-index: 20; }
391
  .panel.selected img { outline: 3px solid #2196F3; outline-offset: -3px; }
392
 
393
  /*
394
- 0% GAP COORDINATES from Green Text:
395
  Row 1 Split: Top=63.2%, Bottom=59.5%
396
  Row 2 Left Split: Top=33.0%, Bottom=35.7%
397
  Row 2 Right Split: Top=64.8%, Bottom=68.2%
398
- Rows are 50% / 50%
399
  */
400
 
401
- /* --- ROW 1 (TOP) --- Height 50% */
402
 
403
- /* Panel 1: Right ends at 63.2% / 59.5% */
404
  .panel:nth-child(1) {
405
- top: 0; left: 0; height: 50%; width: 100%;
406
- clip-path: polygon(0% 0%, 63.2% 0%, 59.5% 100%, 0% 100%);
407
  }
408
- /* Panel 2: Left starts at 63.2% / 59.5% */
409
  .panel:nth-child(2) {
410
- top: 0; left: 0; height: 50%; width: 100%;
411
- clip-path: polygon(63.2% 0%, 100% 0%, 100% 100%, 59.5% 100%);
412
  }
413
 
414
- /* --- ROW 2 (BOTTOM) --- Top: 50%, Height: 50% */
415
 
416
- /* Panel 3: Right ends at 33.0% / 35.7% */
417
  .panel:nth-child(3) {
418
- top: 50%; left: 0; height: 50%; width: 100%;
419
- clip-path: polygon(0% 0%, 33.0% 0%, 35.7% 100%, 0% 100%);
420
  }
421
- /* Panel 4: Left starts 33.0%/35.7%. Right ends 64.8%/68.2% */
422
  .panel:nth-child(4) {
423
- top: 50%; left: 0; height: 50%; width: 100%;
424
- clip-path: polygon(33.0% 0%, 64.8% 0%, 68.2% 100%, 35.7% 100%);
425
  }
426
- /* Panel 5: Left starts 64.8%/68.2% */
427
  .panel:nth-child(5) {
428
- top: 50%; left: 0; height: 50%; width: 100%;
429
- clip-path: polygon(64.8% 0%, 100% 0%, 100% 100%, 68.2% 100%);
430
  }
431
 
432
  /* ====================== */
433
 
434
  /*
435
- IMAGE FIT: 'fill' ensures full image is visible (100% fit) and stretches to fill (0 white space).
 
 
 
436
  */
437
  .panel img {
438
  width: 100%; height: 100%;
439
- object-fit: fill; /* STRETCH TO FILL (No Zoom, No Gaps) */
440
  transition: transform 0.1s ease-out;
441
  transform-origin: center center;
 
442
  }
443
  /* Toggle classes */
444
  .panel img.fit-cover { object-fit: cover !important; }
@@ -969,12 +972,51 @@ INDEX_HTML = '''
969
  document.getElementById('bubble-text-color').addEventListener('change', (e) => { if(selectedBubble) { selectedBubble.style.setProperty('--bubble-text-color', e.target.value); saveDraft(true); } });
970
  document.getElementById('bubble-fill-color').addEventListener('change', (e) => { if(selectedBubble) { selectedBubble.style.setProperty('--bubble-fill-color', e.target.value); saveDraft(true); } });
971
 
972
- function handleZoom(el) { if(!selectedPanel) return; const img = selectedPanel.querySelector('img'); img.dataset.zoom = el.value; updateImageTransform(img); }
 
 
 
 
 
973
  document.getElementById('zoom-slider').addEventListener('change', () => saveDraft(true));
974
- function startPan(e, img) { if(parseFloat(img.dataset.zoom || 100) <= 100) return; e.preventDefault(); isPanning = true; selectedPanel = img.closest('.panel'); panStartX = e.clientX; panStartY = e.clientY; panStartTx = parseFloat(img.dataset.translateX || 0); panStartTy = parseFloat(img.dataset.translateY || 0); img.classList.add('panning'); }
975
- function panImage(e) { if(!isPanning || !selectedPanel) return; const img = selectedPanel.querySelector('img'); img.dataset.translateX = panStartTx + (e.clientX - panStartX); img.dataset.translateY = panStartTy + (e.clientY - panStartY); updateImageTransform(img); }
976
- function updateImageTransform(img) { const z = (img.dataset.zoom || 100) / 100; const x = img.dataset.translateX || 0; const y = img.dataset.translateY || 0; img.style.transform = `translateX(${x}px) translateY(${y}px) scale(${z})`; img.classList.toggle('pannable', z > 1); }
977
- function resetPanelTransform() { if(!selectedPanel) return alert("Select a panel"); const img = selectedPanel.querySelector('img'); img.dataset.zoom = 100; img.dataset.translateX = 0; img.dataset.translateY = 0; document.getElementById('zoom-slider').value = 100; updateImageTransform(img); saveDraft(true); }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
978
 
979
  function replacePanelImage() { if(!selectedPanel) return alert("Select a panel"); const inp = document.getElementById('image-uploader'); inp.onchange = async (e) => { const fd = new FormData(); fd.append('image', e.target.files[0]); const img = selectedPanel.querySelector('img'); const r = await fetch(`/replace_panel?sid=${sid}`, {method:'POST', body:fd}); const d = await r.json(); if(d.success) { img.src = `/frames/${d.new_filename}?sid=${sid}`; saveDraft(true); } inp.value = ''; }; inp.click(); }
980
  async function adjustFrame(dir) { if(isProcessing) return; if(!selectedPanel) return alert("Select a panel"); const img = selectedPanel.querySelector('img'); let fname = img.src.split('/').pop().split('?')[0]; setProcessing(true); img.style.opacity = '0.5'; try { const r = await fetch(`/regenerate_frame?sid=${sid}`, { method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({filename:fname, direction:dir}) }); const d = await r.json(); if(d.success) { img.src = `/frames/${fname}?sid=${sid}&t=${Date.now()}`; } else { alert('Error: ' + d.message); } } catch(e) { console.error(e); } img.style.opacity = '1'; setProcessing(false); saveDraft(true); }
@@ -1000,6 +1042,13 @@ INDEX_HTML = '''
1000
  if(selectedPanel) selectedPanel.classList.remove('selected');
1001
  alert(`Exporting ${pgs.length} page(s)...`);
1002
 
 
 
 
 
 
 
 
1003
  // --- 0% ERROR FIX ---
1004
  // 1. Lock specific pixel dimensions + 1px buffer to prevent word wrapping
1005
  const bubbles = document.querySelectorAll('.speech-bubble');
@@ -1017,8 +1066,8 @@ INDEX_HTML = '''
1017
  for(let i = 0; i < pgs.length; i++) {
1018
  try {
1019
  const u = await htmlToImage.toPng(pgs[i], {
1020
- pixelRatio: 2, // High quality
1021
- style: { transform: 'none' }
1022
  });
1023
  const a = document.createElement('a');
1024
  a.href = u;
@@ -1030,8 +1079,11 @@ INDEX_HTML = '''
1030
  }
1031
  }
1032
 
1033
- // Optional: Reload page or reset styles if user wants to continue editing immediately
1034
- // In this app, styles are locked until reload, which is safer for the export focus.
 
 
 
1035
  }
1036
 
1037
  function goBackToUpload() { if(confirm('Go home? Unsaved changes will be lost.')) { document.getElementById('editor-container').style.display = 'none'; document.getElementById('upload-container').style.display = 'flex'; document.getElementById('loading-view').style.display = 'none'; } }
 
100
  frame_metadata[fname] = {'dialogue': moment['text'], 'time': mid}
101
  frame_files_ordered.append(fname)
102
  count += 1
 
103
  if count % 2 == 0:
104
  prog = 30 + int((count / total_panels_needed) * 20)
105
  update_status_file(status_path, f"Extracted Frame {count}/{total_panels_needed}", prog)
 
374
  overflow: hidden;
375
  border: 2px solid #000;
376
  padding: 0;
377
+ /* SCALING FOR EDITOR VIEW */
378
  transform-origin: top center;
379
+ transform: scale(0.6);
380
+ margin-bottom: -280px; /* Compensate for scale empty space */
381
  }
382
 
383
  /* === 5-PANEL TEMPLATE (2 TOP, 3 BOTTOM) === */
384
+ /* Background White for 12px Gutter (1.2% width, 1.7% height of 712px) */
385
  .comic-grid { width: 100%; height: 100%; position: relative; background: #ffffff; }
386
 
387
  /* Panel Background White */
388
+ .panel { position: absolute; overflow: hidden; background: #ffffff; cursor: pointer; border: 0; display:flex; justify-content:center; align-items:center; }
389
  .panel.selected { z-index: 20; }
390
  .panel.selected img { outline: 3px solid #2196F3; outline-offset: -3px; }
391
 
392
  /*
393
+ COORDINATES from Green Text:
394
  Row 1 Split: Top=63.2%, Bottom=59.5%
395
  Row 2 Left Split: Top=33.0%, Bottom=35.7%
396
  Row 2 Right Split: Top=64.8%, Bottom=68.2%
397
+ Tier Height: 350px (approx 49.15% of 712px)
398
  */
399
 
400
+ /* --- ROW 1 (TOP) --- Height 49.15% */
401
 
402
+ /* Panel 1: Right ends at (63.2 - 0.6)% / (59.5 - 0.6)% */
403
  .panel:nth-child(1) {
404
+ top: 0; left: 0; height: 49.15%; width: 100%;
405
+ clip-path: polygon(0% 0%, 62.6% 0%, 58.9% 100%, 0% 100%);
406
  }
407
+ /* Panel 2: Left starts at (63.2 + 0.6)% / (59.5 + 0.6)% */
408
  .panel:nth-child(2) {
409
+ top: 0; left: 0; height: 49.15%; width: 100%;
410
+ clip-path: polygon(63.8% 0%, 100% 0%, 100% 100%, 60.1% 100%);
411
  }
412
 
413
+ /* --- ROW 2 (BOTTOM) --- Top: 50.85% (Gap ~1.7%), Height: 49.15% */
414
 
415
+ /* Panel 3: Right ends at (33.0 - 0.6)% / (35.7 - 0.6)% */
416
  .panel:nth-child(3) {
417
+ top: 50.85%; left: 0; height: 49.15%; width: 100%;
418
+ clip-path: polygon(0% 0%, 32.4% 0%, 35.1% 100%, 0% 100%);
419
  }
420
+ /* Panel 4: Left starts (33.0 + 0.6)% / (35.7 + 0.6)%. Right ends (64.8 - 0.6)% / (68.2 - 0.6)% */
421
  .panel:nth-child(4) {
422
+ top: 50.85%; left: 0; height: 49.15%; width: 100%;
423
+ clip-path: polygon(33.6% 0%, 64.2% 0%, 67.6% 100%, 36.3% 100%);
424
  }
425
+ /* Panel 5: Left starts (64.8 + 0.6)% / (68.2 + 0.6)% */
426
  .panel:nth-child(5) {
427
+ top: 50.85%; left: 0; height: 49.15%; width: 100%;
428
+ clip-path: polygon(65.4% 0%, 100% 0%, 100% 100%, 68.8% 100%);
429
  }
430
 
431
  /* ====================== */
432
 
433
  /*
434
+ IMAGE FIT: 'fill' to STRETCH fully into the panel shape
435
+ WITHOUT empty space and WITHOUT zooming.
436
+ (User accepted distortion over empty space/cutting)
437
+ Allowed to be transformed (panned/scaled) via JS if user wants.
438
  */
439
  .panel img {
440
  width: 100%; height: 100%;
441
+ object-fit: fill; /* STRETCH: No gaps, no cut */
442
  transition: transform 0.1s ease-out;
443
  transform-origin: center center;
444
+ display: block;
445
  }
446
  /* Toggle classes */
447
  .panel img.fit-cover { object-fit: cover !important; }
 
972
  document.getElementById('bubble-text-color').addEventListener('change', (e) => { if(selectedBubble) { selectedBubble.style.setProperty('--bubble-text-color', e.target.value); saveDraft(true); } });
973
  document.getElementById('bubble-fill-color').addEventListener('change', (e) => { if(selectedBubble) { selectedBubble.style.setProperty('--bubble-fill-color', e.target.value); saveDraft(true); } });
974
 
975
+ function handleZoom(el) {
976
+ if(!selectedPanel) return;
977
+ const img = selectedPanel.querySelector('img');
978
+ img.dataset.zoom = el.value;
979
+ updateImageTransform(img);
980
+ }
981
  document.getElementById('zoom-slider').addEventListener('change', () => saveDraft(true));
982
+
983
+ function startPan(e, img) {
984
+ // REMOVED ZOOM CHECK - ALLOW PAN ALWAYS
985
+ e.preventDefault();
986
+ isPanning = true;
987
+ selectedPanel = img.closest('.panel');
988
+ panStartX = e.clientX;
989
+ panStartY = e.clientY;
990
+ panStartTx = parseFloat(img.dataset.translateX || 0);
991
+ panStartTy = parseFloat(img.dataset.translateY || 0);
992
+ img.classList.add('panning');
993
+ }
994
+
995
+ function panImage(e) {
996
+ if(!isPanning || !selectedPanel) return;
997
+ const img = selectedPanel.querySelector('img');
998
+ img.dataset.translateX = panStartTx + (e.clientX - panStartX);
999
+ img.dataset.translateY = panStartTy + (e.clientY - panStartY);
1000
+ updateImageTransform(img);
1001
+ }
1002
+
1003
+ function updateImageTransform(img) {
1004
+ const z = (img.dataset.zoom || 100) / 100;
1005
+ const x = img.dataset.translateX || 0;
1006
+ const y = img.dataset.translateY || 0;
1007
+ img.style.transform = `translateX(${x}px) translateY(${y}px) scale(${z})`;
1008
+ }
1009
+
1010
+ function resetPanelTransform() {
1011
+ if(!selectedPanel) return alert("Select a panel");
1012
+ const img = selectedPanel.querySelector('img');
1013
+ img.dataset.zoom = 100;
1014
+ img.dataset.translateX = 0;
1015
+ img.dataset.translateY = 0;
1016
+ document.getElementById('zoom-slider').value = 100;
1017
+ updateImageTransform(img);
1018
+ saveDraft(true);
1019
+ }
1020
 
1021
  function replacePanelImage() { if(!selectedPanel) return alert("Select a panel"); const inp = document.getElementById('image-uploader'); inp.onchange = async (e) => { const fd = new FormData(); fd.append('image', e.target.files[0]); const img = selectedPanel.querySelector('img'); const r = await fetch(`/replace_panel?sid=${sid}`, {method:'POST', body:fd}); const d = await r.json(); if(d.success) { img.src = `/frames/${d.new_filename}?sid=${sid}`; saveDraft(true); } inp.value = ''; }; inp.click(); }
1022
  async function adjustFrame(dir) { if(isProcessing) return; if(!selectedPanel) return alert("Select a panel"); const img = selectedPanel.querySelector('img'); let fname = img.src.split('/').pop().split('?')[0]; setProcessing(true); img.style.opacity = '0.5'; try { const r = await fetch(`/regenerate_frame?sid=${sid}`, { method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({filename:fname, direction:dir}) }); const d = await r.json(); if(d.success) { img.src = `/frames/${fname}?sid=${sid}&t=${Date.now()}`; } else { alert('Error: ' + d.message); } } catch(e) { console.error(e); } img.style.opacity = '1'; setProcessing(false); saveDraft(true); }
 
1042
  if(selectedPanel) selectedPanel.classList.remove('selected');
1043
  alert(`Exporting ${pgs.length} page(s)...`);
1044
 
1045
+ // --- PREPARE FOR EXPORT: Remove editor scaling ---
1046
+ const comicPages = document.querySelectorAll('.comic-page');
1047
+ comicPages.forEach(page => {
1048
+ page.style.transform = 'none'; // Remove scaling from comic-page
1049
+ page.style.marginBottom = '0'; // Remove margin compensation
1050
+ });
1051
+
1052
  // --- 0% ERROR FIX ---
1053
  // 1. Lock specific pixel dimensions + 1px buffer to prevent word wrapping
1054
  const bubbles = document.querySelectorAll('.speech-bubble');
 
1066
  for(let i = 0; i < pgs.length; i++) {
1067
  try {
1068
  const u = await htmlToImage.toPng(pgs[i], {
1069
+ pixelRatio: 2, // High quality for print
1070
+ style: { transform: 'none' } // Ensure no transforms for html-to-image
1071
  });
1072
  const a = document.createElement('a');
1073
  a.href = u;
 
1079
  }
1080
  }
1081
 
1082
+ // --- RESTORE EDITOR SCALING AFTER EXPORT ---
1083
+ comicPages.forEach(page => {
1084
+ page.style.transform = 'scale(0.6)';
1085
+ page.style.marginBottom = '-280px';
1086
+ });
1087
  }
1088
 
1089
  function goBackToUpload() { if(confirm('Go home? Unsaved changes will be lost.')) { document.getElementById('editor-container').style.display = 'none'; document.getElementById('upload-container').style.display = 'flex'; document.getElementById('loading-view').style.display = 'none'; } }