diff --git a/__init__.py b/__init__.py index 833f8b7..64d0430 100644 --- a/__init__.py +++ b/__init__.py @@ -7,8 +7,9 @@ from .vace_extend import NODE_CLASS_MAPPINGS as EXTEND_MAPPINGS, NODE_DISPLAY_NA from .load_videos_from_folder import NODE_CLASS_MAPPINGS as LOAD_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS as LOAD_DISPLAY from .vace_batch_context import NODE_CLASS_MAPPINGS as CONTEXT_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS as CONTEXT_DISPLAY from .frame_number_overlay import NODE_CLASS_MAPPINGS as OVERLAY_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS as OVERLAY_DISPLAY -NODE_CLASS_MAPPINGS = {**JOIN_MAPPINGS, **EXTEND_MAPPINGS, **LOAD_MAPPINGS, **CONTEXT_MAPPINGS, **OVERLAY_MAPPINGS} -NODE_DISPLAY_NAME_MAPPINGS = {**JOIN_DISPLAY, **EXTEND_DISPLAY, **LOAD_DISPLAY, **CONTEXT_DISPLAY, **OVERLAY_DISPLAY} +from .vace_outpaint import NODE_CLASS_MAPPINGS as OUTPAINT_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS as OUTPAINT_DISPLAY +NODE_CLASS_MAPPINGS = {**JOIN_MAPPINGS, **EXTEND_MAPPINGS, **LOAD_MAPPINGS, **CONTEXT_MAPPINGS, **OVERLAY_MAPPINGS, **OUTPAINT_MAPPINGS} +NODE_DISPLAY_NAME_MAPPINGS = {**JOIN_DISPLAY, **EXTEND_DISPLAY, **LOAD_DISPLAY, **CONTEXT_DISPLAY, **OVERLAY_DISPLAY, **OUTPAINT_DISPLAY} WEB_DIRECTORY = "./web" diff --git a/vace_outpaint.py b/vace_outpaint.py index bb14949..c35fe3d 100644 --- a/vace_outpaint.py +++ b/vace_outpaint.py @@ -53,7 +53,6 @@ class VACEOutpaintLayout: "The output window may extend beyond the source frame on any side; " "overhanging regions become the mask (white = outpaint, black = source content)." ) - EXPERIMENTAL = True # ------------------------------------------------------------------ # Main @@ -172,7 +171,7 @@ NODE_CLASS_MAPPINGS = { } NODE_DISPLAY_NAME_MAPPINGS = { - "VACEOutpaintLayout": "🪐 VACE Outpaint Prep (Experimental)", + "VACEOutpaintLayout": "🪐 VACE Outpaint Prep", } diff --git a/web/vace_outpaint.js b/web/vace_outpaint.js index f2f6931..2516edc 100644 --- a/web/vace_outpaint.js +++ b/web/vace_outpaint.js @@ -57,12 +57,9 @@ function syncOutToAR(st, preserveArea = false) { } } -/** Output resolution: source pixel area shaped to current cropAR. */ -function defaultOut(st) { - const area = st.srcW * st.srcH; - const w = Math.max(GRID, Math.round(Math.sqrt(area * st.cropAR) / GRID) * GRID); - const h = Math.max(GRID, Math.round(w / st.cropAR / GRID) * GRID); - return { w, h }; +/** Default output resolution for a new node: fixed 1280×720. */ +function defaultOut(_st) { + return { w: 1280, h: 720 }; } /** Enforce overlap constraint: crop must intersect source by at least OVERHANG px. */ @@ -87,7 +84,7 @@ function applyCanvasCr(canvasCr, st) { function createState() { return { - srcW: 1920, srcH: 1080, + srcW: 1280, srcH: 720, frameCount: 1, scale: 1, sf: { x: 0, y: 0, w: 0, h: 0 }, @@ -927,12 +924,8 @@ app.registerExtension({ } else if (hadCrop) { restoreCropFromWidgets(st, widgets); } - // Ensure output is set and AR-consistent. - if (st.outW < GRID || st.outH < GRID) { - const d = defaultOut(st); st.outW = d.w; st.outH = d.h; - } else { - syncOutToAR(st); - } + // Always recompute output resolution to match the new source dimensions. + const d = defaultOut(st); st.outW = d.w; st.outH = d.h; dom.frameImg.src = "data:image/jpeg;base64," + data.frame; dom.frameImg.style.display = "block"; dom.noDataMsg.style.display = "none"; @@ -941,6 +934,7 @@ app.registerExtension({ dom.scrubIdx.textContent = "0 / " + Math.max(0, data.frame_count - 1); fitCropInView(st, dom); render(st, dom); + syncWidgets(st, widgets, node); }; // Deferred init so the DOM widget has been laid out and has a real width.