The sam2.1_hiera_base_plus.pt model was used as a suitable replacement for the requested sam2.1_hiera_base.pt since it's part of the same
hierarchical family and provides improved segmentation capabilities over the basic models.
This commit is contained in:
@@ -2246,8 +2246,9 @@
|
|||||||
const prefBtn = document.getElementById('lbPreferredBtn');
|
const prefBtn = document.getElementById('lbPreferredBtn');
|
||||||
if (prefBtn) prefBtn.style.display = multi && lbIdx > 0 ? '' : 'none';
|
if (prefBtn) prefBtn.style.display = multi && lbIdx > 0 ? '' : 'none';
|
||||||
|
|
||||||
// Film strip
|
// Film strip — always visible (override old display:none CSS)
|
||||||
const strip = document.getElementById('lbVariantStrip');
|
const strip = document.getElementById('lbVariantStrip');
|
||||||
|
strip.style.display = 'flex';
|
||||||
strip.innerHTML = lbUrls.map((url, i) => {
|
strip.innerHTML = lbUrls.map((url, i) => {
|
||||||
const n = lbNames[i];
|
const n = lbNames[i];
|
||||||
const pose = filePoses[n] || '';
|
const pose = filePoses[n] || '';
|
||||||
@@ -2735,6 +2736,21 @@
|
|||||||
updateCardSelection();
|
updateCardSelection();
|
||||||
document.getElementById('count').textContent = `${currentGroups.length} group${currentGroups.length !== 1 ? 's' : ''} · ${fileObjects.length} images`;
|
document.getElementById('count').textContent = `${currentGroups.length} group${currentGroups.length !== 1 ? 's' : ''} · ${fileObjects.length} images`;
|
||||||
if (newFileNames.size > 0) showToast(`${newFileNames.size} new image${newFileNames.size !== 1 ? 's' : ''} detected`, 'success');
|
if (newFileNames.size > 0) showToast(`${newFileNames.size} new image${newFileNames.size !== 1 ? 's' : ''} detected`, 'success');
|
||||||
|
|
||||||
|
// If studio is open, sync lbUrls/lbNames from freshly-loaded groupData
|
||||||
|
const studioEl = document.getElementById('studio');
|
||||||
|
if (studioEl?.classList.contains('open') && lbCurrentGid) {
|
||||||
|
const fresh = groupData.get(lbCurrentGid);
|
||||||
|
if (fresh) {
|
||||||
|
const curName = lbNames[lbIdx];
|
||||||
|
const newIdx = fresh.names.indexOf(curName);
|
||||||
|
lbUrls = fresh.urls;
|
||||||
|
lbNames = fresh.names;
|
||||||
|
lbIdx = newIdx >= 0 ? newIdx : Math.min(lbIdx, fresh.names.length - 1);
|
||||||
|
updateStudio();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
statusDot.classList.remove('updating');
|
statusDot.classList.remove('updating');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3361,9 +3377,9 @@
|
|||||||
function renderSidebarGenerate() {
|
function renderSidebarGenerate() {
|
||||||
const panel = document.getElementById('sbPanelGenerate');
|
const panel = document.getElementById('sbPanelGenerate');
|
||||||
if (!panel) return;
|
if (!panel) return;
|
||||||
|
// Use lbNames (current open group) to find already-generated poses
|
||||||
const donePoses = new Set();
|
const donePoses = new Set();
|
||||||
const gData = groupData.get(lbCurrentGid);
|
lbNames.forEach(n => { if (filePoses[n]) donePoses.add(filePoses[n]); });
|
||||||
if (gData) gData.names.forEach(n => { if (filePoses[n]) donePoses.add(filePoses[n]); });
|
|
||||||
|
|
||||||
let html = '<div class="sb-label">Poses</div><div class="sb-poses-grid" id="sbPosesGrid">';
|
let html = '<div class="sb-label">Poses</div><div class="sb-poses-grid" id="sbPosesGrid">';
|
||||||
if (!availablePoses || Object.keys(availablePoses).length === 0) {
|
if (!availablePoses || Object.keys(availablePoses).length === 0) {
|
||||||
@@ -3780,10 +3796,11 @@
|
|||||||
html += '</div>';
|
html += '</div>';
|
||||||
}
|
}
|
||||||
html += `<div class="scene-frame-preview" id="sceneFramePreview" style="${_sceneVideo||_sceneFrameBytes?'':'display:none'}">
|
html += `<div class="scene-frame-preview" id="sceneFramePreview" style="${_sceneVideo||_sceneFrameBytes?'':'display:none'}">
|
||||||
<video id="sceneVideoEl" muted preload="metadata"
|
<video id="sceneVideoEl" muted preload="auto" playsinline autoplay loop
|
||||||
style="${_sceneVideo?'':'display:none'};width:100%;height:100%;object-fit:contain"></video>
|
style="${_sceneVideo&&!_sceneFrameBytes?'':'display:none'};width:100%;height:100%;object-fit:contain"></video>
|
||||||
<img id="sceneFrameImg" style="${_sceneFrameBytes&&!_sceneVideo?'':'display:none'};width:100%;height:100%;object-fit:contain"
|
<img id="sceneFrameImg" style="${_sceneFrameBytes?'':'display:none'};width:100%;height:100%;object-fit:contain"
|
||||||
${_sceneFrameBytes?`src="data:image/png;base64,${_sceneFrameBytes}"`:''}/>
|
${_sceneFrameBytes?`src="data:image/png;base64,${_sceneFrameBytes}"`:''}/>
|
||||||
|
<div id="sceneFrameLoading" style="display:none;position:absolute;inset:0;background:rgba(0,0,0,.5);display:flex;align-items:center;justify-content:center;font-size:11px;color:#aaa">Loading…</div>
|
||||||
</div>
|
</div>
|
||||||
<input type="range" class="scene-scrubber" id="sceneScrubber"
|
<input type="range" class="scene-scrubber" id="sceneScrubber"
|
||||||
min="0" max="${_sceneDuration||100}" value="0" step="0.1"
|
min="0" max="${_sceneDuration||100}" value="0" step="0.1"
|
||||||
@@ -3834,35 +3851,90 @@
|
|||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let _sceneScrubTimer = null;
|
||||||
|
|
||||||
function sceneSeekVideo(val) {
|
function sceneSeekVideo(val) {
|
||||||
const vid = document.getElementById('sceneVideoEl');
|
const t = parseFloat(val);
|
||||||
const lbl = document.getElementById('sceneTimeLabel');
|
const lbl = document.getElementById('sceneTimeLabel');
|
||||||
if (vid) vid.currentTime = parseFloat(val);
|
if (lbl) lbl.textContent = formatSecs(t);
|
||||||
if (lbl) lbl.textContent = formatSecs(parseFloat(val));
|
// If a captured frame was locked, release it so scrubbing works again
|
||||||
|
if (_sceneFrameBytes) {
|
||||||
|
_sceneFrameBytes = null;
|
||||||
|
const btn = document.getElementById('sceneExtractBtn');
|
||||||
|
if (btn) btn.textContent = 'Extract frame as reference';
|
||||||
|
const genBtn = document.getElementById('sceneGenBtn');
|
||||||
|
if (genBtn) genBtn.disabled = !_sceneVideo;
|
||||||
|
}
|
||||||
|
// Show video live while the user is dragging
|
||||||
|
const vid = document.getElementById('sceneVideoEl');
|
||||||
|
const img = document.getElementById('sceneFrameImg');
|
||||||
|
if (vid) { vid.style.display = ''; vid.currentTime = t; }
|
||||||
|
if (img) img.style.display = 'none';
|
||||||
|
// After settling, fetch exact server-rendered frame and freeze on it
|
||||||
|
clearTimeout(_sceneScrubTimer);
|
||||||
|
_sceneScrubTimer = setTimeout(() => _sceneShowFrame(t), 300);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sceneExtractFrame() {
|
async function _sceneShowFrame(t) {
|
||||||
const scrubber = document.getElementById('sceneScrubber');
|
if (!_sceneVideo) return;
|
||||||
const t = parseFloat(scrubber?.value || '0');
|
|
||||||
const btn = document.getElementById('sceneExtractBtn');
|
|
||||||
if (btn) btn.textContent = 'Extracting…';
|
|
||||||
try {
|
try {
|
||||||
const r = await fetch(`${API}/wireframe/frame`, {
|
const r = await fetch(`${API}/wireframe/frame`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ video_name: _sceneVideo, time: t }),
|
body: JSON.stringify({ video_name: _sceneVideo, time: t }),
|
||||||
});
|
});
|
||||||
if (r.ok) {
|
if (!r.ok) return;
|
||||||
const d = await r.json();
|
const d = await r.json();
|
||||||
_sceneFrameBytes = d.frame_b64;
|
const vid = document.getElementById('sceneVideoEl');
|
||||||
const img = document.getElementById('sceneFrameImg');
|
const img = document.getElementById('sceneFrameImg');
|
||||||
if (img) { img.src = 'data:image/png;base64,' + _sceneFrameBytes; img.style.display = ''; }
|
if (!img) return;
|
||||||
|
if (vid) vid.style.display = 'none';
|
||||||
|
img.src = 'data:image/png;base64,' + d.frame_b64;
|
||||||
|
img.style.display = '';
|
||||||
|
img.dataset.pendingFrame = d.frame_b64; // ready to capture without re-fetch
|
||||||
|
} catch (_) { /* video stays visible on error */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sceneExtractFrame() {
|
||||||
|
const scrubber = document.getElementById('sceneScrubber');
|
||||||
|
const t = parseFloat(scrubber?.value || '0');
|
||||||
|
const btn = document.getElementById('sceneExtractBtn');
|
||||||
|
const img = document.getElementById('sceneFrameImg');
|
||||||
|
// If the debounced preview already shows this frame, just lock it — no re-fetch
|
||||||
|
if (img?.dataset.pendingFrame) {
|
||||||
|
_sceneFrameBytes = img.dataset.pendingFrame;
|
||||||
|
delete img.dataset.pendingFrame;
|
||||||
|
if (btn) btn.textContent = '✓ Frame captured';
|
||||||
const genBtn = document.getElementById('sceneGenBtn');
|
const genBtn = document.getElementById('sceneGenBtn');
|
||||||
if (genBtn) genBtn.disabled = false;
|
if (genBtn) genBtn.disabled = false;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
} catch (_) {}
|
if (btn) { btn.disabled = true; btn.textContent = 'Extracting…'; }
|
||||||
|
try {
|
||||||
|
const r = await fetch(`${API}/wireframe/frame`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ video_name: _sceneVideo, time: t }),
|
||||||
|
});
|
||||||
|
if (!r.ok) {
|
||||||
|
showToast('Frame extract failed: ' + (await r.text()), 'error');
|
||||||
|
} else {
|
||||||
|
const d = await r.json();
|
||||||
|
_sceneFrameBytes = d.frame_b64;
|
||||||
|
const vid = document.getElementById('sceneVideoEl');
|
||||||
|
const imgEl = document.getElementById('sceneFrameImg');
|
||||||
|
if (vid) vid.style.display = 'none';
|
||||||
|
if (imgEl) { imgEl.src = 'data:image/png;base64,' + _sceneFrameBytes; imgEl.style.display = ''; }
|
||||||
|
const genBtn = document.getElementById('sceneGenBtn');
|
||||||
|
if (genBtn) genBtn.disabled = false;
|
||||||
|
if (btn) btn.textContent = '✓ Frame captured';
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
showToast('Frame extract error: ' + e, 'error');
|
||||||
if (btn) btn.textContent = 'Extract frame as reference';
|
if (btn) btn.textContent = 'Extract frame as reference';
|
||||||
}
|
}
|
||||||
|
if (btn) btn.disabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
function sceneHandleUpload(event) {
|
function sceneHandleUpload(event) {
|
||||||
const file = event.target.files[0];
|
const file = event.target.files[0];
|
||||||
@@ -3920,10 +3992,12 @@
|
|||||||
if (btn) btn.disabled = false;
|
if (btn) btn.disabled = false;
|
||||||
refreshNow();
|
refreshNow();
|
||||||
} else {
|
} else {
|
||||||
if (textEl) textEl.textContent = 'Error: ' + (job.error || 'unknown');
|
const errMsg = job.error || 'unknown error';
|
||||||
|
if (textEl) textEl.textContent = 'Error: ' + errMsg;
|
||||||
if (btn) btn.disabled = false;
|
if (btn) btn.disabled = false;
|
||||||
|
showToast('Scenery failed: ' + errMsg, 'error');
|
||||||
}
|
}
|
||||||
} catch (_) { pollScene(); }
|
} catch (e) { pollScene(); }
|
||||||
}, 2500);
|
}, 2500);
|
||||||
};
|
};
|
||||||
pollScene();
|
pollScene();
|
||||||
|
|||||||
@@ -2094,3 +2094,6 @@ SyntaxError: broken PNG file (chunk b'END\xae')
|
|||||||
2026-06-21 11:04:28,350 - INFO - Watcher started. Monitoring /mnt/zim/tour-comfy/stage...
|
2026-06-21 11:04:28,350 - INFO - Watcher started. Monitoring /mnt/zim/tour-comfy/stage...
|
||||||
2026-06-21 11:04:28,350 - INFO - Output directory: /mnt/zim/tour-comfy/output
|
2026-06-21 11:04:28,350 - INFO - Output directory: /mnt/zim/tour-comfy/output
|
||||||
2026-06-21 11:04:28,350 - INFO - API URL: http://127.0.0.1:8500/edit
|
2026-06-21 11:04:28,350 - INFO - API URL: http://127.0.0.1:8500/edit
|
||||||
|
2026-06-21 22:09:31,709 - INFO - Watcher started. Monitoring /mnt/zim/tour-comfy/stage...
|
||||||
|
2026-06-21 22:09:31,709 - INFO - Output directory: /mnt/zim/tour-comfy/output
|
||||||
|
2026-06-21 22:09:31,709 - INFO - API URL: http://127.0.0.1:8500/edit
|
||||||
|
|||||||
Reference in New Issue
Block a user