This commit adds several new nodes to enable flexible typing and conversion between types: - ValueToAny - Converts primitive values like ints, floats, strings, bools to Any type - IterableToAny - Converts iterable values like lists, dicts to Any - CallableToAny - Converts callables like functions to Any - ObjectToAny - Converts tensor objects like images, latents etc to Any The motivation is to allow passing different types flexibly into functions using Any typing. This enables writing functions in a more generic way by accepting Any instead of specific types. The nodes convert the input values to Any and return debug info on what was converted. This allows tracing what types entered a function even if the function uses generic Any types. No breaking changes, new nodes added: - ValueToAny - IterableToAny - CallableToAny - ObjectToAny These provide building blocks for more flexible typing, opening up possibilities like: - Writing utility and processing functions accepting Any instead of specific types - Calling functions dynamically with values of different types - Tracing and logging what types passed through a function Next steps could include AnyToValue nodes, more specialized Any converters, and exploring uses for generic functions.
177 lines
5.1 KiB
Python
177 lines
5.1 KiB
Python
import copy
|
|
|
|
NODE_CLASS_MAPPINGS = {}
|
|
NODE_DISPLAY_NAME_MAPPINGS = {}
|
|
|
|
|
|
def ETK_audio_base(cls):
|
|
cls.CATEGORY = "ETK/audio"
|
|
NODE_CLASS_MAPPINGS[cls.__name__] = cls
|
|
return cls
|
|
|
|
|
|
@ETK_audio_base
|
|
class GetAudioMetaData:
|
|
""" Returns a dictionary of audio metadata """
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"path": ("STRING", {"multiline": False, "default": ""}),
|
|
}
|
|
|
|
}
|
|
|
|
RETURN_TYPES = ("DICT",)
|
|
FUNCTION = 'get_audio_metadata'
|
|
CATEGORY = 'ETK/audio'
|
|
|
|
def get_audio_metadata(self, **kwargs):
|
|
import audioread
|
|
kwargs = copy.deepcopy(kwargs)
|
|
path = kwargs.get('path')
|
|
metadata = {}
|
|
with audioread.audio_open(path) as f:
|
|
metadata['duration'] = f.duration
|
|
metadata['samplerate'] = f.samplerate
|
|
metadata['channels'] = f.channels
|
|
|
|
return (metadata,)
|
|
|
|
|
|
@ETK_audio_base
|
|
class AudioFileListFromPathPattern:
|
|
""" Returns a list of audio files from a path pattern """
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"path": ("STRING", {"multiline": False, "default": ""}),
|
|
}
|
|
|
|
}
|
|
|
|
RETURN_TYPES = ("LIST", "LIST", "LIST",)
|
|
RETURN_NAMES = ("list_of_fpaths", "list_of_bytes", "list_of_metadata")
|
|
FUNCTION = 'get_audio_file_list_from_path_pattern'
|
|
CATEGORY = 'ETK/audio'
|
|
|
|
def get_audio_file_list_from_path_pattern(self, **kwargs):
|
|
import glob
|
|
kwargs = copy.deepcopy(kwargs)
|
|
path = kwargs.get('path')
|
|
l_fpaths = glob.glob(path)
|
|
l_bytes = []
|
|
l_metadata = []
|
|
for fpath in l_fpaths:
|
|
with open(fpath, "rb") as f:
|
|
l_bytes.append(f.read())
|
|
l_metadata.append(GetAudioMetaData().get_audio_metadata(path=fpath)[0])
|
|
|
|
return (l_fpaths, l_bytes, l_metadata,)
|
|
|
|
|
|
import librosa
|
|
import soundfile as sf
|
|
import numpy as np
|
|
|
|
@ETK_audio_base
|
|
class JoinAudioFiles:
|
|
""" Joins a list of audio files into a single audio file """
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"paths": ("LIST", {"default": []}),
|
|
"output_path": ("STRING", {"multiline": False, "default": ""}),
|
|
}
|
|
}
|
|
|
|
RETURN_TYPES = ("STRING", "BYTES", "DICT",)
|
|
FUNCTION = 'join_audio_files'
|
|
CATEGORY = 'ETK/audio'
|
|
|
|
def join_audio_files(self, **kwargs):
|
|
kwargs = copy.deepcopy(kwargs)
|
|
paths = kwargs['paths']
|
|
output_path = kwargs['output_path']
|
|
|
|
# Read the first file to get metadata
|
|
audio_data_list = []
|
|
for path in paths:
|
|
audio_data, sr = librosa.load(path, sr=None)
|
|
audio_data_list.append(audio_data)
|
|
|
|
# Concatenate all audio data
|
|
joined_audio_data = np.concatenate(audio_data_list)
|
|
|
|
# Save the concatenated audio
|
|
sf.write(output_path, joined_audio_data, sr)
|
|
|
|
# Get metadata
|
|
metadata = {
|
|
'duration': librosa.get_duration(y=joined_audio_data, sr=sr),
|
|
'samplerate': sr
|
|
}
|
|
|
|
# Get bytes
|
|
with open(output_path, "rb") as f:
|
|
file_bytes = f.read()
|
|
|
|
return (output_path, file_bytes, metadata,)
|
|
|
|
@ETK_audio_base
|
|
class ExtractAudioFromVideo:
|
|
""" Extracts audio from a video file and saves it as an audio file with a specified format and bitrate """
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"video_path": ("STRING", {"multiline": False, "default": ""}),
|
|
"format":(["wav", "mp3"], {"default": "wav"}),
|
|
"output_path": ("STRING", {"multiline": False, "default": ""}),
|
|
},
|
|
"optional": {
|
|
"bitrate (MP3)": ("INT", {"default": 192}) # Default bitrate value (change as needed)
|
|
}
|
|
}
|
|
|
|
RETURN_TYPES = ("STRING", "BYTES")
|
|
RETURN_NAMES = ("output_wave_file_path", "audio_bytes")
|
|
FUNCTION = 'extract_audio_from_video'
|
|
CATEGORY = 'ETK/audio'
|
|
|
|
def extract_audio_from_video(self, **kwargs):
|
|
import moviepy.editor as mp
|
|
import io
|
|
|
|
kwargs = copy.deepcopy(kwargs)
|
|
video_path = kwargs.get('video_path')
|
|
output_path = kwargs.get('output_path',"tmp.wav")
|
|
audio_format = kwargs.get('format', 'wav')
|
|
bitrate = kwargs.get('bitrate', None) # Default bitrate is None
|
|
|
|
# Load the video clip
|
|
video_clip = mp.VideoFileClip(video_path)
|
|
|
|
# Determine the appropriate codec based on the audio format
|
|
if audio_format == 'wav':
|
|
codec = "pcm_s16le" # For WAV format
|
|
elif audio_format == 'mp3':
|
|
codec = "mp3" # For MP3 format
|
|
|
|
# Extract audio and save it with the specified format and bitrate
|
|
audio_clip = video_clip.audio
|
|
audio_clip.write_audiofile(output_path, codec=codec, bitrate=bitrate)
|
|
|
|
# Read the generated audio file as bytes
|
|
audio_bytes = None
|
|
with open(output_path, 'rb') as audio_file:
|
|
audio_bytes = audio_file.read()
|
|
|
|
return (output_path, audio_bytes,)
|