Updated Load Audio UI Node
- added duration widget - added ability to drag and move the entire selection bar
This commit is contained in:
@@ -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);
|
||||
};
|
||||
|
||||
@@ -61,9 +61,10 @@ class LoadAudioUI:
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"audio": (files, {"audio_upload": True}),
|
||||
"audio": (files, {"audio_upload": True}), # Moved to the top so it appears first
|
||||
"start_time": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 100000.0, "step": 0.01}),
|
||||
"end_time": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 100000.0, "step": 0.01}),
|
||||
"duration": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 100000.0, "step": 0.01}),
|
||||
},
|
||||
"optional": {
|
||||
"audioUI": ("AUDIO_UI",)
|
||||
@@ -82,7 +83,7 @@ class LoadAudioUI:
|
||||
# where our fallback silence logic can handle the missing file gracefully.
|
||||
return True
|
||||
|
||||
def load_audio(self, audio, start_time, end_time, **kwargs):
|
||||
def load_audio(self, audio, start_time, end_time, duration, **kwargs):
|
||||
# Determine the annotated file path if a file is actually selected
|
||||
# We wrap this in a try/except because get_annotated_filepath can fail if
|
||||
# the input string is malformed or doesn't follow expected paths.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "WhatDreamsCost-ComfyUI"
|
||||
description = "A variety of custom ComfyUI nodes and workflows for creatives."
|
||||
version = "1.2.4"
|
||||
version = "1.2.5"
|
||||
license = {file = "LICENSE"}
|
||||
# classifiers = [
|
||||
# # For OS-independent nodes (works on all operating systems)
|
||||
|
||||
Reference in New Issue
Block a user