Updated Load Audio UI Node

- added duration widget
- added ability to drag and move the entire selection bar
This commit is contained in:
WhatDreamsCost
2026-05-01 16:26:45 -05:00
parent 71d3d82522
commit ff555ed2ac
3 changed files with 87 additions and 17 deletions

View File

@@ -245,9 +245,6 @@ app.registerExtension({
const widget = this.addDOMWidget("audio_ui", "audio_ui", container);
// --- DEFAULT SIZE FOR NEW NODES ---
// We set the width to 475 initially.
// If a workflow is loading, LiteGraph will overwrite this size
// automatically after this function finishes.
this.size = [475, this.computeSize()[1]];
widget.computeSize = function(width) {
@@ -259,6 +256,49 @@ app.registerExtension({
const audioWidget = node.widgets && node.widgets.find(w => w.name === "audio");
const startWidget = node.widgets && node.widgets.find(w => w.name === "start_time");
const endWidget = node.widgets && node.widgets.find(w => w.name === "end_time");
const durationWidget = node.widgets && node.widgets.find(w => w.name === "duration");
let duration = 0;
let dragging = null;
let dragOffset = 0;
let dragSelectionWidth = 0;
let isUpdatingDuration = false; // Flag to prevent infinite loops
// Hook into the Python-generated native duration widget
if (durationWidget) {
const origCallback = durationWidget.callback;
durationWidget.callback = function(v) {
// If we're internally triggering it, ignore it
if (!duration || isUpdatingDuration) {
if (origCallback) origCallback.apply(this, arguments);
return;
}
isUpdatingDuration = true;
let d = parseFloat(v) || 0;
if (d < 0) d = 0;
if (d > duration) d = duration;
let s = startWidget ? parseFloat(startWidget.value) || 0 : 0;
let newStart = s;
let newEnd = s + d;
// If adding duration pushes past the end of the audio, shift the start time back instead
if (newEnd > duration) {
newEnd = duration;
newStart = duration - d;
}
if (startWidget) startWidget.value = parseFloat(newStart.toFixed(2));
if (endWidget) endWidget.value = parseFloat(newEnd.toFixed(2));
updateUI(true);
app.graph.setDirtyCanvas(true, false);
if (origCallback) origCallback.apply(this, arguments);
isUpdatingDuration = false;
};
}
if (audioWidget) {
const updateAudio = () => {
@@ -306,9 +346,6 @@ app.registerExtension({
}
};
let duration = 0;
let dragging = null;
const formatTime = (secs) => {
if (secs < 60) return secs.toFixed(1) + "s";
const m = Math.floor(secs / 60);
@@ -360,7 +397,16 @@ app.registerExtension({
endHandle.style.left = `${ePct}%`;
fill.style.left = `${sPct}%`;
fill.style.width = `${ePct - sPct}%`;
trimLength.textContent = `Trimmed: ${formatTime(e - s)}`;
// Sync native duration widget seamlessly to match UI handles
const currentDur = parseFloat((e - s).toFixed(2));
trimLength.textContent = `Trimmed: ${currentDur}s`;
if (durationWidget && durationWidget.value !== currentDur) {
isUpdatingDuration = true;
durationWidget.value = currentDur;
isUpdatingDuration = false;
}
if (syncPlayer && audioEl.readyState >= 1) { audioEl.currentTime = s; }
};
@@ -370,13 +416,13 @@ app.registerExtension({
// Handle trim reset for new audio selection
if (node._should_reset_trim) {
if (startWidget) startWidget.value = 0;
if (endWidget) endWidget.value = parseFloat(duration.toFixed(3));
if (endWidget) endWidget.value = parseFloat(duration.toFixed(2));
node._should_reset_trim = false;
} else {
// Default clamping logic for initial load or out-of-bounds saved values
let e = endWidget ? parseFloat(endWidget.value) || 0 : 0;
if (endWidget && (e === 0 || e > duration)) {
endWidget.value = parseFloat(duration.toFixed(3));
endWidget.value = parseFloat(duration.toFixed(2));
}
}
@@ -414,12 +460,20 @@ app.registerExtension({
const val = (x / rect.width) * duration;
let s = startWidget ? parseFloat(startWidget.value) || 0 : 0;
let e_val = endWidget ? parseFloat(endWidget.value) || duration : duration;
if (Math.abs(val - s) < Math.abs(val - e_val)) {
// Define a "handle tolerance" zone (approx 10px on each side) to prioritize resizing over dragging
const handleTolerance = (10 / rect.width) * duration;
if (val > s + handleTolerance && val < e_val - handleTolerance) {
dragging = 'center';
dragOffset = val - s;
dragSelectionWidth = e_val - s;
} else if (Math.abs(val - s) < Math.abs(val - e_val)) {
dragging = 'start';
if(startWidget) startWidget.value = parseFloat(Math.min(val, e_val).toFixed(3));
if(startWidget) startWidget.value = parseFloat(Math.min(val, e_val).toFixed(2));
} else {
dragging = 'end';
if(endWidget) endWidget.value = parseFloat(Math.max(val, s).toFixed(3));
if(endWidget) endWidget.value = parseFloat(Math.max(val, s).toFixed(2));
}
updateUI(true); app.graph.setDirtyCanvas(true, false);
sliderBox.setPointerCapture(e.pointerId);
@@ -432,10 +486,25 @@ app.registerExtension({
const val = (x / rect.width) * duration;
if (dragging === 'start') {
let e_val = endWidget ? parseFloat(endWidget.value) || duration : duration;
if(startWidget) startWidget.value = parseFloat(Math.min(val, e_val).toFixed(3));
if(startWidget) startWidget.value = parseFloat(Math.min(val, e_val).toFixed(2));
} else if (dragging === 'end') {
const s = startWidget ? parseFloat(startWidget.value) || 0 : 0;
if(endWidget) endWidget.value = parseFloat(Math.max(val, s).toFixed(3));
if(endWidget) endWidget.value = parseFloat(Math.max(val, s).toFixed(2));
} else if (dragging === 'center') {
let newStart = val - dragOffset;
let newEnd = newStart + dragSelectionWidth;
// Clamp to bounds
if (newStart < 0) {
newStart = 0;
newEnd = dragSelectionWidth;
} else if (newEnd > duration) {
newEnd = duration;
newStart = duration - dragSelectionWidth;
}
if(startWidget) startWidget.value = parseFloat(newStart.toFixed(2));
if(endWidget) endWidget.value = parseFloat(newEnd.toFixed(2));
}
updateUI(true); app.graph.setDirtyCanvas(true, false);
};