Added fix for numResults

This commit is contained in:
Sirshendu Ganguly
2026-05-11 14:07:40 +05:30
parent 934b5cb7b5
commit ef2f596325
2 changed files with 87 additions and 32 deletions
+86 -32
View File
@@ -5,6 +5,7 @@ import numpy as np
import librosa
import tempfile
import os
from urllib.parse import urlparse
import comfy.model_management
from .utils import runwareUtils as rwUtils
@@ -22,10 +23,6 @@ class RunwareAudioInference:
"model": ("RUNWAREAUDIOMODEL", {
"tooltip": "AI model to use for audio generation"
}),
"usePositivePrompt": ("BOOLEAN", {
"default": True,
"tooltip": "Enable/disable positivePrompt parameter in API request"
}),
"positivePrompt": ("STRING", {
"multiline": True,
"default": "classical piano piece, gentle and melodic",
@@ -74,10 +71,6 @@ class RunwareAudioInference:
"step": 1,
"tooltip": "Number of audio channels (1=mono, 2=stereo)"
}),
"outputType": (["URL", "dataURI", "base64Data"], {
"default": "URL",
"tooltip": "Output type for the generated audio"
}),
"outputFormat": (["MP3", "MP4", "WAV", "FLAC", "OGG"], {
"default": "MP3",
"tooltip": "Format of the output audio"
@@ -219,13 +212,16 @@ class RunwareAudioInference:
status = audioData["status"]
if status == "success":
audioUrl = self._extractAudioUrl(pollResult)
hasAudio = audioUrl is not None
audioUrls = self._extractAudioUrls(pollResult)
hasAudio = len(audioUrls) > 0
hasVideo = bool(audioData.get("videoURL") or audioData.get("videoBase64Data", False))
if hasAudio:
audioObj = self._downloadAndProcessAudio(audioUrl, params["sampleRate"])
print(f"[DEBUG] Audio URL found, returning audio. Audio URL: {audioUrl}")
audioObjects = []
for audioUrl in audioUrls:
audioObjects.append(self._downloadAndProcessAudio(audioUrl, params["sampleRate"], params["outputFormat"]))
audioObj = self._mergeAudioObjects(audioObjects, params["sampleRate"])
print(f"[DEBUG] Audio URL(s) found, returning batched audio. Count: {len(audioUrls)}")
# Return empty video object when only audio is present (prevents errors in downstream nodes)
emptyVideoObj = rwUtils.VideoObject("", width=0, height=0)
return (audioObj, emptyVideoObj)
@@ -260,7 +256,6 @@ class RunwareAudioInference:
"""Extract and validate parameters from kwargs"""
return {
"positivePrompt": kwargs.get("positivePrompt", ""),
"usePositivePrompt": kwargs.get("usePositivePrompt", True),
"model": kwargs.get("model", ""),
"duration": kwargs.get("duration", 30),
"useDuration": kwargs.get("useDuration", True),
@@ -270,7 +265,6 @@ class RunwareAudioInference:
"useBitrate": kwargs.get("useBitrate", False),
"channels": kwargs.get("channels", 2),
"useChannels": kwargs.get("useChannels", False),
"outputType": kwargs.get("outputType", "URL"),
"outputFormat": kwargs.get("outputFormat", "MP3"),
"negativePrompt": kwargs.get("negativePrompt", ""),
"numberResults": kwargs.get("numberResults", 1),
@@ -310,7 +304,7 @@ class RunwareAudioInference:
"taskType": "audioInference",
"taskUUID": taskUuid,
"model": params["model"],
"outputType": params["outputType"],
"outputType": "URL",
"outputFormat": params["outputFormat"],
"deliveryMethod": "async",
"includeCost": True,
@@ -338,8 +332,8 @@ class RunwareAudioInference:
params["useDuration"] = False
print(f"[DEBUG] Disabled duration because sections are provided")
# Add prompts conditionally
if params["usePositivePrompt"]:
# Add positivePrompt only when non-empty
if len(params["positivePrompt"]) > 0:
genConfig[0]["positivePrompt"] = params["positivePrompt"]
print(f"[DEBUG] Added positivePrompt: '{params['positivePrompt']}'")
else:
@@ -447,26 +441,68 @@ class RunwareAudioInference:
if not hasAudio and not hasVideo:
raise Exception("No audio or video data received from API")
def _extractAudioUrl(self, genResult):
"""Extract audio URL from API response. Returns None if no audio URL is present (e.g., when only video is returned)."""
audioData = genResult["data"][0]
audioUrl = audioData.get("audioURL", "")
if not audioUrl:
audioUrl = audioData.get("audioDataURI", "")
if not audioUrl:
audioUrl = audioData.get("audioBase64Data", "")
def _extractAudioUrls(self, genResult):
"""Extract all available audio URLs from API response."""
audioUrls = []
for item in genResult.get("data", []):
audioUrl = item.get("audioURL", "") or item.get("audioDataURI", "") or item.get("audioBase64Data", "")
if audioUrl:
audioUrls.append(audioUrl)
return audioUrls
return audioUrl if audioUrl else None
def _mergeAudioObjects(self, audioObjects, targetSampleRate):
"""Merge multiple audio objects into a single batched audio object."""
if not audioObjects:
return {
"waveform": torch.zeros((1, 1, 1)),
"sample_rate": targetSampleRate,
"format": "wav"
}
def _downloadAndProcessAudio(self, audioUrl, targetSampleRate):
waveforms = []
maxChannels = 0
maxSamples = 0
for audioObj in audioObjects:
waveform = audioObj["waveform"]
if waveform.dim() == 2:
waveform = waveform.unsqueeze(0)
waveforms.append(waveform)
maxChannels = max(maxChannels, waveform.shape[1])
maxSamples = max(maxSamples, waveform.shape[2])
paddedWaveforms = []
for waveform in waveforms:
batchSize, channels, samples = waveform.shape
if channels < maxChannels:
channelPadding = torch.zeros(
(batchSize, maxChannels - channels, samples),
dtype=waveform.dtype,
device=waveform.device
)
waveform = torch.cat([waveform, channelPadding], dim=1)
if samples < maxSamples:
waveform = torch.nn.functional.pad(waveform, (0, maxSamples - samples))
paddedWaveforms.append(waveform)
outputFormat = audioObjects[0].get("format", "wav")
return {
"waveform": torch.cat(paddedWaveforms, dim=0),
"sample_rate": targetSampleRate,
"format": outputFormat
}
def _downloadAndProcessAudio(self, audioUrl, targetSampleRate, outputFormat):
"""Download audio file and process it for ComfyUI"""
try:
response = requests.get(audioUrl, timeout=30)
response.raise_for_status()
with tempfile.NamedTemporaryFile(suffix='.mp3', delete=False) as tempFile:
tempSuffix = self._inferAudioSuffix(audioUrl, outputFormat)
with tempfile.NamedTemporaryFile(suffix=tempSuffix, delete=False) as tempFile:
tempFile.write(response.content)
tempFilePath = tempFile.name
@@ -477,7 +513,8 @@ class RunwareAudioInference:
return {
"waveform": waveformTensor,
"sample_rate": targetSampleRate
"sample_rate": targetSampleRate,
"format": tempSuffix.lstrip(".")
}
finally:
if os.path.exists(tempFilePath):
@@ -485,6 +522,23 @@ class RunwareAudioInference:
except Exception as e:
raise Exception(f"Failed to download audio: {e}")
def _inferAudioSuffix(self, audioUrl, outputFormat):
"""Infer temporary audio file extension from URL or requested format."""
parsedPath = urlparse(audioUrl).path if audioUrl else ""
ext = os.path.splitext(parsedPath)[1].lower()
allowed = {".mp3", ".mp4", ".wav", ".flac", ".ogg"}
if ext in allowed:
return ext
formatMap = {
"MP3": ".mp3",
"MP4": ".mp4",
"WAV": ".wav",
"FLAC": ".flac",
"OGG": ".ogg",
}
return formatMap.get(str(outputFormat).upper(), ".wav")
def _loadAudioFile(self, filePath):
"""Load audio file using librosa"""
waveformNp, originalSampleRate = librosa.load(filePath, sr=None, mono=False)
File diff suppressed because one or more lines are too long