Update app_enhanced.py
Browse files- app_enhanced.py +21 -27
app_enhanced.py
CHANGED
|
@@ -118,7 +118,7 @@ def generate_comic_gpu(video_path, user_dir, frames_dir, metadata_path, target_p
|
|
| 118 |
raw_moments = [{'text': s.content, 'start': s.start.total_seconds(), 'end': s.end.total_seconds()} for s in valid_subs]
|
| 119 |
|
| 120 |
if target_pages <= 0: target_pages = 1
|
| 121 |
-
#
|
| 122 |
panels_per_page = 4
|
| 123 |
total_panels_needed = target_pages * panels_per_page
|
| 124 |
|
|
@@ -332,26 +332,32 @@ INDEX_HTML = '''
|
|
| 332 |
background: white;
|
| 333 |
width: 1000px;
|
| 334 |
height: 700px;
|
| 335 |
-
box-shadow: 0 0 50px rgba(0,0,0,0.2);
|
| 336 |
position: relative;
|
| 337 |
z-index: 1;
|
| 338 |
overflow: hidden;
|
| 339 |
-
|
|
|
|
|
|
|
| 340 |
}
|
| 341 |
|
| 342 |
/* HANDLES FOR DRAGGING */
|
| 343 |
.handle {
|
| 344 |
position: absolute;
|
| 345 |
-
|
|
|
|
| 346 |
background: #ff4757;
|
| 347 |
-
border:
|
| 348 |
border-radius: 50%;
|
| 349 |
cursor: ew-resize;
|
| 350 |
z-index: 999;
|
| 351 |
transform: translate(-50%, -50%);
|
| 352 |
box-shadow: 0 2px 5px rgba(0,0,0,0.3);
|
| 353 |
}
|
| 354 |
-
.handle.horiz {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 355 |
|
| 356 |
/* PANELS */
|
| 357 |
.panel {
|
|
@@ -514,11 +520,10 @@ INDEX_HTML = '''
|
|
| 514 |
let startX, startY, initX, initY, panStartX, panStartY, panStartTx, panStartTy;
|
| 515 |
let activeLayoutHandle = null, activePageId = null;
|
| 516 |
|
| 517 |
-
// 4-PANEL DEFAULT LAYOUT
|
| 518 |
const DEFAULT_LAYOUT = {
|
| 519 |
width: 1000, height: 700, gutter: 10, tierY: 350,
|
| 520 |
-
r1: { topX: 500, botX: 500 },
|
| 521 |
-
r2: { topX: 500, botX: 500 }
|
| 522 |
};
|
| 523 |
|
| 524 |
let historyStack = [];
|
|
@@ -627,7 +632,6 @@ INDEX_HTML = '''
|
|
| 627 |
div.id = pageId;
|
| 628 |
div.dataset.layout = JSON.stringify(layout);
|
| 629 |
|
| 630 |
-
// Generate 4 Panels
|
| 631 |
for(let i=0; i<4; i++) {
|
| 632 |
const pDiv = document.createElement('div');
|
| 633 |
pDiv.className = 'panel'; pDiv.id = `${pageId}-p${i}`;
|
|
@@ -643,11 +647,10 @@ INDEX_HTML = '''
|
|
| 643 |
div.appendChild(pDiv);
|
| 644 |
}
|
| 645 |
|
| 646 |
-
// Generate Handles (Vertical Dividers and Tier Divider)
|
| 647 |
const handles = [
|
| 648 |
-
{id: 'h1-top', t:0, l:0}, {id: 'h1-bot', t:0, l:0},
|
| 649 |
-
{id: 'h2-top', t:0, l:0}, {id: 'h2-bot', t:0, l:0},
|
| 650 |
-
{id: 'h-tier', t:0, l:0, cls: 'horiz'}
|
| 651 |
];
|
| 652 |
handles.forEach(h => {
|
| 653 |
const hDiv = document.createElement('div');
|
|
@@ -657,7 +660,6 @@ INDEX_HTML = '''
|
|
| 657 |
div.appendChild(hDiv);
|
| 658 |
});
|
| 659 |
|
| 660 |
-
// Bubbles
|
| 661 |
if(page.pageBubbles) {
|
| 662 |
page.pageBubbles.forEach(bData => div.appendChild(createBubbleHTML(bData)));
|
| 663 |
}
|
|
@@ -672,7 +674,6 @@ INDEX_HTML = '''
|
|
| 672 |
pageWrapper.appendChild(div);
|
| 673 |
con.appendChild(pageWrapper);
|
| 674 |
|
| 675 |
-
// Draw Initial Layout
|
| 676 |
drawLayout(pageId);
|
| 677 |
});
|
| 678 |
|
|
@@ -680,7 +681,6 @@ INDEX_HTML = '''
|
|
| 680 |
document.getElementById('font-select').disabled = true;
|
| 681 |
}
|
| 682 |
|
| 683 |
-
// --- LAYOUT ENGINE (4-PANEL) ---
|
| 684 |
function drawLayout(pageId) {
|
| 685 |
const pageEl = document.getElementById(pageId);
|
| 686 |
if(!pageEl) return;
|
|
@@ -690,27 +690,22 @@ INDEX_HTML = '''
|
|
| 690 |
const w = state.width;
|
| 691 |
const h = state.height;
|
| 692 |
|
| 693 |
-
// P0: Top Left
|
| 694 |
const p0 = document.getElementById(`${pageId}-p0`);
|
| 695 |
p0.style.top='0px'; p0.style.left='0px'; p0.style.width='100%'; p0.style.height = ty + 'px';
|
| 696 |
p0.style.clipPath = `polygon(0 0, ${state.r1.topX - g}px 0, ${state.r1.botX - g}px ${ty}px, 0 ${ty}px)`;
|
| 697 |
|
| 698 |
-
// P1: Top Right
|
| 699 |
const p1 = document.getElementById(`${pageId}-p1`);
|
| 700 |
p1.style.top='0px'; p1.style.left='0px'; p1.style.width='100%'; p1.style.height = ty + 'px';
|
| 701 |
p1.style.clipPath = `polygon(${state.r1.topX + g}px 0, ${w}px 0, ${w}px ${ty}px, ${state.r1.botX + g}px ${ty}px)`;
|
| 702 |
|
| 703 |
-
// P2: Bottom Left
|
| 704 |
const p2 = document.getElementById(`${pageId}-p2`);
|
| 705 |
p2.style.top = ty + 'px'; p2.style.left='0px'; p2.style.width='100%'; p2.style.height = (h - ty) + 'px';
|
| 706 |
p2.style.clipPath = `polygon(0 ${g}px, ${state.r2.topX - g}px ${g}px, ${state.r2.botX - g}px ${h-ty}px, 0 ${h-ty}px)`;
|
| 707 |
|
| 708 |
-
// P3: Bottom Right
|
| 709 |
const p3 = document.getElementById(`${pageId}-p3`);
|
| 710 |
p3.style.top = ty + 'px'; p3.style.left='0px'; p3.style.width='100%'; p3.style.height = (h - ty) + 'px';
|
| 711 |
p3.style.clipPath = `polygon(${state.r2.topX + g}px ${g}px, ${w}px ${g}px, ${w}px ${h-ty}px, ${state.r2.botX + g}px ${h-ty}px)`;
|
| 712 |
|
| 713 |
-
// Update Handles Position
|
| 714 |
const setH = (id, l, t) => { const el = document.getElementById(`${pageId}-${id}`); if(el) { el.style.left=l+'px'; el.style.top=t+'px'; } };
|
| 715 |
|
| 716 |
setH('h1-top', state.r1.topX, 0);
|
|
@@ -727,7 +722,6 @@ INDEX_HTML = '''
|
|
| 727 |
isDragging = true;
|
| 728 |
}
|
| 729 |
|
| 730 |
-
// --- GLOBAL MOUSE EVENTS ---
|
| 731 |
document.addEventListener('mousemove', (e) => {
|
| 732 |
if(isDragging && activeLayoutHandle && activePageId) {
|
| 733 |
const pageEl = document.getElementById(activePageId);
|
|
@@ -769,7 +763,6 @@ INDEX_HTML = '''
|
|
| 769 |
});
|
| 770 |
}
|
| 771 |
|
| 772 |
-
// --- IMAGE FIT & TRANSFORM ---
|
| 773 |
function fitImageToPanel(img, panel, savedState) {
|
| 774 |
if(savedState && savedState.baseScale) {
|
| 775 |
img.dataset.zoom = savedState.zoom || 100;
|
|
@@ -780,13 +773,15 @@ INDEX_HTML = '''
|
|
| 780 |
return;
|
| 781 |
}
|
| 782 |
|
| 783 |
-
//
|
| 784 |
const pW = 1000;
|
| 785 |
const pH = panel.offsetHeight;
|
| 786 |
const iW = img.naturalWidth || img.width;
|
| 787 |
const iH = img.naturalHeight || img.height;
|
| 788 |
|
| 789 |
-
|
|
|
|
|
|
|
| 790 |
const tx = (pW - iW * scale) / 2;
|
| 791 |
const ty = (pH - iH * scale) / 2;
|
| 792 |
|
|
@@ -812,7 +807,6 @@ INDEX_HTML = '''
|
|
| 812 |
function handleZoom(el) { if(!selectedPanel) return; const img = selectedPanel.querySelector('img'); img.dataset.zoom = el.value; updateImageTransform(img); }
|
| 813 |
function resetPanelTransform() { if(!selectedPanel) return alert("Select a panel"); const img = selectedPanel.querySelector('img'); fitImageToPanel(img, selectedPanel, null); saveDraft(true); }
|
| 814 |
|
| 815 |
-
// --- STANDARD FUNCTIONS ---
|
| 816 |
function selectPanel(el) {
|
| 817 |
if(selectedPanel) selectedPanel.classList.remove('selected');
|
| 818 |
if(selectedBubble) { selectedBubble.classList.remove('selected'); selectedBubble = null; }
|
|
|
|
| 118 |
raw_moments = [{'text': s.content, 'start': s.start.total_seconds(), 'end': s.end.total_seconds()} for s in valid_subs]
|
| 119 |
|
| 120 |
if target_pages <= 0: target_pages = 1
|
| 121 |
+
# 4 PANELS
|
| 122 |
panels_per_page = 4
|
| 123 |
total_panels_needed = target_pages * panels_per_page
|
| 124 |
|
|
|
|
| 332 |
background: white;
|
| 333 |
width: 1000px;
|
| 334 |
height: 700px;
|
|
|
|
| 335 |
position: relative;
|
| 336 |
z-index: 1;
|
| 337 |
overflow: hidden;
|
| 338 |
+
/* UPDATED: THICK WHITE BORDER */
|
| 339 |
+
border: 10px solid white;
|
| 340 |
+
box-shadow: 0 0 30px rgba(0,0,0,0.5);
|
| 341 |
}
|
| 342 |
|
| 343 |
/* HANDLES FOR DRAGGING */
|
| 344 |
.handle {
|
| 345 |
position: absolute;
|
| 346 |
+
/* UPDATED: LARGER HANDLES */
|
| 347 |
+
width: 24px; height: 24px;
|
| 348 |
background: #ff4757;
|
| 349 |
+
border: 3px solid white;
|
| 350 |
border-radius: 50%;
|
| 351 |
cursor: ew-resize;
|
| 352 |
z-index: 999;
|
| 353 |
transform: translate(-50%, -50%);
|
| 354 |
box-shadow: 0 2px 5px rgba(0,0,0,0.3);
|
| 355 |
}
|
| 356 |
+
.handle.horiz {
|
| 357 |
+
cursor: ns-resize;
|
| 358 |
+
width: 60px; height: 18px;
|
| 359 |
+
border-radius: 9px;
|
| 360 |
+
}
|
| 361 |
|
| 362 |
/* PANELS */
|
| 363 |
.panel {
|
|
|
|
| 520 |
let startX, startY, initX, initY, panStartX, panStartY, panStartTx, panStartTy;
|
| 521 |
let activeLayoutHandle = null, activePageId = null;
|
| 522 |
|
|
|
|
| 523 |
const DEFAULT_LAYOUT = {
|
| 524 |
width: 1000, height: 700, gutter: 10, tierY: 350,
|
| 525 |
+
r1: { topX: 500, botX: 500 },
|
| 526 |
+
r2: { topX: 500, botX: 500 }
|
| 527 |
};
|
| 528 |
|
| 529 |
let historyStack = [];
|
|
|
|
| 632 |
div.id = pageId;
|
| 633 |
div.dataset.layout = JSON.stringify(layout);
|
| 634 |
|
|
|
|
| 635 |
for(let i=0; i<4; i++) {
|
| 636 |
const pDiv = document.createElement('div');
|
| 637 |
pDiv.className = 'panel'; pDiv.id = `${pageId}-p${i}`;
|
|
|
|
| 647 |
div.appendChild(pDiv);
|
| 648 |
}
|
| 649 |
|
|
|
|
| 650 |
const handles = [
|
| 651 |
+
{id: 'h1-top', t:0, l:0}, {id: 'h1-bot', t:0, l:0},
|
| 652 |
+
{id: 'h2-top', t:0, l:0}, {id: 'h2-bot', t:0, l:0},
|
| 653 |
+
{id: 'h-tier', t:0, l:0, cls: 'horiz'}
|
| 654 |
];
|
| 655 |
handles.forEach(h => {
|
| 656 |
const hDiv = document.createElement('div');
|
|
|
|
| 660 |
div.appendChild(hDiv);
|
| 661 |
});
|
| 662 |
|
|
|
|
| 663 |
if(page.pageBubbles) {
|
| 664 |
page.pageBubbles.forEach(bData => div.appendChild(createBubbleHTML(bData)));
|
| 665 |
}
|
|
|
|
| 674 |
pageWrapper.appendChild(div);
|
| 675 |
con.appendChild(pageWrapper);
|
| 676 |
|
|
|
|
| 677 |
drawLayout(pageId);
|
| 678 |
});
|
| 679 |
|
|
|
|
| 681 |
document.getElementById('font-select').disabled = true;
|
| 682 |
}
|
| 683 |
|
|
|
|
| 684 |
function drawLayout(pageId) {
|
| 685 |
const pageEl = document.getElementById(pageId);
|
| 686 |
if(!pageEl) return;
|
|
|
|
| 690 |
const w = state.width;
|
| 691 |
const h = state.height;
|
| 692 |
|
|
|
|
| 693 |
const p0 = document.getElementById(`${pageId}-p0`);
|
| 694 |
p0.style.top='0px'; p0.style.left='0px'; p0.style.width='100%'; p0.style.height = ty + 'px';
|
| 695 |
p0.style.clipPath = `polygon(0 0, ${state.r1.topX - g}px 0, ${state.r1.botX - g}px ${ty}px, 0 ${ty}px)`;
|
| 696 |
|
|
|
|
| 697 |
const p1 = document.getElementById(`${pageId}-p1`);
|
| 698 |
p1.style.top='0px'; p1.style.left='0px'; p1.style.width='100%'; p1.style.height = ty + 'px';
|
| 699 |
p1.style.clipPath = `polygon(${state.r1.topX + g}px 0, ${w}px 0, ${w}px ${ty}px, ${state.r1.botX + g}px ${ty}px)`;
|
| 700 |
|
|
|
|
| 701 |
const p2 = document.getElementById(`${pageId}-p2`);
|
| 702 |
p2.style.top = ty + 'px'; p2.style.left='0px'; p2.style.width='100%'; p2.style.height = (h - ty) + 'px';
|
| 703 |
p2.style.clipPath = `polygon(0 ${g}px, ${state.r2.topX - g}px ${g}px, ${state.r2.botX - g}px ${h-ty}px, 0 ${h-ty}px)`;
|
| 704 |
|
|
|
|
| 705 |
const p3 = document.getElementById(`${pageId}-p3`);
|
| 706 |
p3.style.top = ty + 'px'; p3.style.left='0px'; p3.style.width='100%'; p3.style.height = (h - ty) + 'px';
|
| 707 |
p3.style.clipPath = `polygon(${state.r2.topX + g}px ${g}px, ${w}px ${g}px, ${w}px ${h-ty}px, ${state.r2.botX + g}px ${h-ty}px)`;
|
| 708 |
|
|
|
|
| 709 |
const setH = (id, l, t) => { const el = document.getElementById(`${pageId}-${id}`); if(el) { el.style.left=l+'px'; el.style.top=t+'px'; } };
|
| 710 |
|
| 711 |
setH('h1-top', state.r1.topX, 0);
|
|
|
|
| 722 |
isDragging = true;
|
| 723 |
}
|
| 724 |
|
|
|
|
| 725 |
document.addEventListener('mousemove', (e) => {
|
| 726 |
if(isDragging && activeLayoutHandle && activePageId) {
|
| 727 |
const pageEl = document.getElementById(activePageId);
|
|
|
|
| 763 |
});
|
| 764 |
}
|
| 765 |
|
|
|
|
| 766 |
function fitImageToPanel(img, panel, savedState) {
|
| 767 |
if(savedState && savedState.baseScale) {
|
| 768 |
img.dataset.zoom = savedState.zoom || 100;
|
|
|
|
| 773 |
return;
|
| 774 |
}
|
| 775 |
|
| 776 |
+
// CONTAIN LOGIC: Ensure WHOLE image fits
|
| 777 |
const pW = 1000;
|
| 778 |
const pH = panel.offsetHeight;
|
| 779 |
const iW = img.naturalWidth || img.width;
|
| 780 |
const iH = img.naturalHeight || img.height;
|
| 781 |
|
| 782 |
+
// Math.min makes sure it fits fully (contain)
|
| 783 |
+
const scale = Math.min(pW / iW, pH / iH);
|
| 784 |
+
|
| 785 |
const tx = (pW - iW * scale) / 2;
|
| 786 |
const ty = (pH - iH * scale) / 2;
|
| 787 |
|
|
|
|
| 807 |
function handleZoom(el) { if(!selectedPanel) return; const img = selectedPanel.querySelector('img'); img.dataset.zoom = el.value; updateImageTransform(img); }
|
| 808 |
function resetPanelTransform() { if(!selectedPanel) return alert("Select a panel"); const img = selectedPanel.querySelector('img'); fitImageToPanel(img, selectedPanel, null); saveDraft(true); }
|
| 809 |
|
|
|
|
| 810 |
function selectPanel(el) {
|
| 811 |
if(selectedPanel) selectedPanel.classList.remove('selected');
|
| 812 |
if(selectedBubble) { selectedBubble.classList.remove('selected'); selectedBubble = null; }
|