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');
|
||||
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');
|
||||
strip.style.display = 'flex';
|
||||
strip.innerHTML = lbUrls.map((url, i) => {
|
||||
const n = lbNames[i];
|
||||
const pose = filePoses[n] || '';
|
||||
@@ -2735,6 +2736,21 @@
|
||||
updateCardSelection();
|
||||
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 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');
|
||||
}
|
||||
|
||||
@@ -3361,9 +3377,9 @@
|
||||
function renderSidebarGenerate() {
|
||||
const panel = document.getElementById('sbPanelGenerate');
|
||||
if (!panel) return;
|
||||
// Use lbNames (current open group) to find already-generated poses
|
||||
const donePoses = new Set();
|
||||
const gData = groupData.get(lbCurrentGid);
|
||||
if (gData) gData.names.forEach(n => { if (filePoses[n]) donePoses.add(filePoses[n]); });
|
||||
lbNames.forEach(n => { if (filePoses[n]) donePoses.add(filePoses[n]); });
|
||||
|
||||
let html = '<div class="sb-label">Poses</div><div class="sb-poses-grid" id="sbPosesGrid">';
|
||||
if (!availablePoses || Object.keys(availablePoses).length === 0) {
|
||||
@@ -3780,10 +3796,11 @@
|
||||
html += '</div>';
|
||||
}
|
||||
html += `<div class="scene-frame-preview" id="sceneFramePreview" style="${_sceneVideo||_sceneFrameBytes?'':'display:none'}">
|
||||
<video id="sceneVideoEl" muted preload="metadata"
|
||||
style="${_sceneVideo?'':'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"
|
||||
<video id="sceneVideoEl" muted preload="auto" playsinline autoplay loop
|
||||
style="${_sceneVideo&&!_sceneFrameBytes?'':'display:none'};width:100%;height:100%;object-fit:contain"></video>
|
||||
<img id="sceneFrameImg" style="${_sceneFrameBytes?'':'display:none'};width:100%;height:100%;object-fit:contain"
|
||||
${_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>
|
||||
<input type="range" class="scene-scrubber" id="sceneScrubber"
|
||||
min="0" max="${_sceneDuration||100}" value="0" step="0.1"
|
||||
@@ -3834,34 +3851,89 @@
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
let _sceneScrubTimer = null;
|
||||
|
||||
function sceneSeekVideo(val) {
|
||||
const vid = document.getElementById('sceneVideoEl');
|
||||
const t = parseFloat(val);
|
||||
const lbl = document.getElementById('sceneTimeLabel');
|
||||
if (vid) vid.currentTime = parseFloat(val);
|
||||
if (lbl) lbl.textContent = formatSecs(parseFloat(val));
|
||||
if (lbl) lbl.textContent = formatSecs(t);
|
||||
// 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() {
|
||||
const scrubber = document.getElementById('sceneScrubber');
|
||||
const t = parseFloat(scrubber?.value || '0');
|
||||
const btn = document.getElementById('sceneExtractBtn');
|
||||
if (btn) btn.textContent = 'Extracting…';
|
||||
async function _sceneShowFrame(t) {
|
||||
if (!_sceneVideo) return;
|
||||
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) {
|
||||
if (!r.ok) return;
|
||||
const d = await r.json();
|
||||
const vid = document.getElementById('sceneVideoEl');
|
||||
const img = document.getElementById('sceneFrameImg');
|
||||
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');
|
||||
if (genBtn) genBtn.disabled = false;
|
||||
return;
|
||||
}
|
||||
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 img = document.getElementById('sceneFrameImg');
|
||||
if (img) { img.src = 'data:image/png;base64,' + _sceneFrameBytes; img.style.display = ''; }
|
||||
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 (_) {}
|
||||
if (btn) btn.textContent = 'Extract frame as reference';
|
||||
} catch (e) {
|
||||
showToast('Frame extract error: ' + e, 'error');
|
||||
if (btn) btn.textContent = 'Extract frame as reference';
|
||||
}
|
||||
if (btn) btn.disabled = false;
|
||||
}
|
||||
|
||||
function sceneHandleUpload(event) {
|
||||
@@ -3920,10 +3992,12 @@
|
||||
if (btn) btn.disabled = false;
|
||||
refreshNow();
|
||||
} 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;
|
||||
showToast('Scenery failed: ' + errMsg, 'error');
|
||||
}
|
||||
} catch (_) { pollScene(); }
|
||||
} catch (e) { pollScene(); }
|
||||
}, 2500);
|
||||
};
|
||||
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 - 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 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