init
This commit is contained in:
+10
@@ -0,0 +1,10 @@
|
||||
__pycache__
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.pyd
|
||||
*.pyw
|
||||
*.pyz
|
||||
*.pywz
|
||||
*.pyzw
|
||||
*.pyzwz
|
||||
*.pyzwzw
|
||||
@@ -0,0 +1,42 @@
|
||||
# ComfyUI WB Audio Duration Node
|
||||
|
||||
一个简单的ComfyUI自定义节点,用于获取音频文件的时长。
|
||||
|
||||
## 功能
|
||||
|
||||
- 支持1D, 2D, 3D音频张量格式
|
||||
- 支持 `waveform` 和 `audio` 键格式
|
||||
- 提供详细的调试信息
|
||||
- 格式化时长显示(秒、分、小时)
|
||||
|
||||
## 安装
|
||||
|
||||
1. 将此文件夹复制到ComfyUI的 `custom_nodes` 目录
|
||||
2. 重启ComfyUI
|
||||
3. 在节点列表中找到 "WB Audio Duration"
|
||||
|
||||
## 使用方法
|
||||
|
||||
1. 添加 "Load Audio" 节点加载音频文件
|
||||
2. 添加 "WB Audio Duration" 节点
|
||||
3. 连接音频输出到时长节点
|
||||
4. 运行工作流查看结果
|
||||
|
||||
## 输出
|
||||
|
||||
- `duration_seconds`: 时长(秒,浮点数)
|
||||
- `duration_text`: 格式化的时长文本
|
||||
|
||||
## 支持格式
|
||||
|
||||
- 1D张量: `[samples]`
|
||||
- 2D张量: `[channels, samples]` 或 `[batch, samples]`
|
||||
- 3D张量: `[batch, channels, samples]`
|
||||
|
||||
## 调试信息
|
||||
|
||||
节点会在控制台输出详细的调试信息,包括:
|
||||
- 音频输入类型
|
||||
- 音频字典键
|
||||
- 音频数据形状
|
||||
- 计算过程
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
# ComfyUI Audio Duration Node Package
|
||||
|
||||
# 直接定义节点类,避免导入问题
|
||||
import os
|
||||
import sys
|
||||
import folder_paths
|
||||
|
||||
# 尝试导入torch,如果失败则使用备用方法
|
||||
try:
|
||||
import torch
|
||||
TORCH_AVAILABLE = True
|
||||
except ImportError:
|
||||
TORCH_AVAILABLE = False
|
||||
print("警告: torch未安装,将使用备用方法处理音频数据")
|
||||
|
||||
class WB_AudioDurationNode:
|
||||
def __init__(self):
|
||||
self.output_dir = folder_paths.get_output_directory()
|
||||
self.type = "output"
|
||||
self.output_node = True
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"audio": ("AUDIO",),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("FLOAT", "STRING",)
|
||||
RETURN_NAMES = ("duration_seconds", "duration_text",)
|
||||
FUNCTION = "get_duration"
|
||||
OUTPUT_NODE = True
|
||||
CATEGORY = "audio"
|
||||
|
||||
def get_duration(self, audio):
|
||||
"""
|
||||
简化的音频时长获取函数
|
||||
支持1D, 2D, 3D张量格式
|
||||
"""
|
||||
try:
|
||||
print(f"=== 音频分析开始 ===")
|
||||
print(f"音频输入类型: {type(audio)}")
|
||||
|
||||
# 处理字典格式
|
||||
if isinstance(audio, dict):
|
||||
print(f"音频字典键: {list(audio.keys())}")
|
||||
|
||||
# 检查是否有必要的键 - 支持两种格式
|
||||
if ('waveform' in audio and 'sample_rate' in audio) or ('audio' in audio and 'sample_rate' in audio):
|
||||
# 优先使用waveform,如果没有则使用audio
|
||||
if 'waveform' in audio:
|
||||
audio_data = audio['waveform']
|
||||
print("使用waveform键")
|
||||
else:
|
||||
audio_data = audio['audio']
|
||||
print("使用audio键")
|
||||
|
||||
sample_rate = audio['sample_rate']
|
||||
|
||||
print(f"音频数据类型: {type(audio_data)}")
|
||||
print(f"采样率: {sample_rate}")
|
||||
|
||||
# 尝试获取音频长度
|
||||
if hasattr(audio_data, 'shape'):
|
||||
print(f"音频数据形状: {audio_data.shape}")
|
||||
|
||||
# 计算样本数 - 支持1D, 2D, 3D张量
|
||||
if len(audio_data.shape) == 1:
|
||||
# 1D: [samples]
|
||||
num_samples = audio_data.shape[0]
|
||||
print("处理1D张量")
|
||||
elif len(audio_data.shape) == 2:
|
||||
# 2D: [channels, samples] 或 [batch, samples]
|
||||
num_samples = audio_data.shape[1]
|
||||
print("处理2D张量")
|
||||
elif len(audio_data.shape) == 3:
|
||||
# 3D: [batch, channels, samples]
|
||||
num_samples = audio_data.shape[2]
|
||||
print("处理3D张量")
|
||||
else:
|
||||
return (0.0, f"不支持的形状: {audio_data.shape}")
|
||||
|
||||
# 计算时长
|
||||
duration_seconds = num_samples / sample_rate
|
||||
duration_text = self.format_duration(duration_seconds)
|
||||
|
||||
print(f"样本数: {num_samples}")
|
||||
print(f"计算出的时长: {duration_seconds}秒")
|
||||
print(f"格式化时长: {duration_text}")
|
||||
|
||||
return (duration_seconds, duration_text)
|
||||
|
||||
elif hasattr(audio_data, '__len__'):
|
||||
# 如果数据有长度属性
|
||||
num_samples = len(audio_data)
|
||||
duration_seconds = num_samples / sample_rate
|
||||
duration_text = self.format_duration(duration_seconds)
|
||||
|
||||
print(f"使用长度属性,样本数: {num_samples}")
|
||||
print(f"计算出的时长: {duration_seconds}秒")
|
||||
|
||||
return (duration_seconds, duration_text)
|
||||
|
||||
else:
|
||||
return (0.0, f"无法获取音频数据长度,类型: {type(audio_data)}")
|
||||
|
||||
else:
|
||||
return (0.0, f"音频字典缺少必要键,当前键: {list(audio.keys())}")
|
||||
|
||||
# 处理字符串(文件路径)
|
||||
elif isinstance(audio, str):
|
||||
print(f"音频文件路径: {audio}")
|
||||
if os.path.exists(audio):
|
||||
# 简单估算
|
||||
file_size = os.path.getsize(audio)
|
||||
estimated_duration = (file_size * 8) / (128 * 1000) # 假设128kbps
|
||||
duration_text = self.format_duration(estimated_duration)
|
||||
return (estimated_duration, f"估算时长: {duration_text}")
|
||||
else:
|
||||
return (0.0, f"文件不存在: {audio}")
|
||||
|
||||
else:
|
||||
return (0.0, f"不支持的音频输入类型: {type(audio)}")
|
||||
|
||||
except Exception as e:
|
||||
import traceback
|
||||
print(f"=== 错误详情 ===")
|
||||
print(f"错误类型: {type(e)}")
|
||||
print(f"错误信息: {str(e)}")
|
||||
print(f"错误堆栈: {traceback.format_exc()}")
|
||||
return (0.0, f"处理错误: {str(e)}")
|
||||
|
||||
def format_duration(self, seconds):
|
||||
"""
|
||||
格式化时长显示
|
||||
"""
|
||||
if seconds < 1:
|
||||
return f"{seconds:.3f}秒"
|
||||
elif seconds < 60:
|
||||
return f"{seconds:.2f}秒"
|
||||
elif seconds < 3600:
|
||||
minutes = int(seconds // 60)
|
||||
remaining_seconds = seconds % 60
|
||||
return f"{minutes}分{remaining_seconds:.2f}秒"
|
||||
else:
|
||||
hours = int(seconds // 3600)
|
||||
remaining_minutes = int((seconds % 3600) // 60)
|
||||
remaining_seconds = seconds % 60
|
||||
return f"{hours}小时{remaining_minutes}分{remaining_seconds:.2f}秒"
|
||||
|
||||
# 节点注册
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"WB_AudioDuration": WB_AudioDurationNode
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"WB_AudioDuration": "WB Audio Duration"
|
||||
}
|
||||
|
||||
print("ComfyUI WB Audio Duration Node 已加载")
|
||||
@@ -0,0 +1,185 @@
|
||||
{
|
||||
"id": "dfb2747d-8793-4209-b89b-c92284762d7d",
|
||||
"revision": 0,
|
||||
"last_node_id": 6,
|
||||
"last_link_id": 4,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 2,
|
||||
"type": "LoadAudio",
|
||||
"pos": [
|
||||
1446.5673828125,
|
||||
-2019.3924560546875
|
||||
],
|
||||
"size": [
|
||||
270,
|
||||
136
|
||||
],
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "AUDIO",
|
||||
"type": "AUDIO",
|
||||
"links": [
|
||||
1
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.44",
|
||||
"Node name for S&R": "LoadAudio"
|
||||
},
|
||||
"widgets_values": [
|
||||
"song.mp3",
|
||||
null,
|
||||
null
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"type": "WB_AudioDuration",
|
||||
"pos": [
|
||||
1782.639892578125,
|
||||
-1973.1875
|
||||
],
|
||||
"size": [
|
||||
169.095703125,
|
||||
46
|
||||
],
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "audio",
|
||||
"type": "AUDIO",
|
||||
"link": 1
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "duration_seconds",
|
||||
"type": "FLOAT",
|
||||
"links": [
|
||||
3
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "duration_text",
|
||||
"type": "STRING",
|
||||
"links": [
|
||||
4
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "WB_AudioDuration"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"type": "PreviewAny",
|
||||
"pos": [
|
||||
2064.296875,
|
||||
-2021.0582275390625
|
||||
],
|
||||
"size": [
|
||||
140,
|
||||
76
|
||||
],
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "source",
|
||||
"type": "*",
|
||||
"link": 3
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.44",
|
||||
"Node name for S&R": "PreviewAny"
|
||||
},
|
||||
"widgets_values": []
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"type": "PreviewAny",
|
||||
"pos": [
|
||||
2060.477294921875,
|
||||
-1892.009521484375
|
||||
],
|
||||
"size": [
|
||||
140,
|
||||
76
|
||||
],
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "source",
|
||||
"type": "*",
|
||||
"link": 4
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.44",
|
||||
"Node name for S&R": "PreviewAny"
|
||||
},
|
||||
"widgets_values": []
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
1,
|
||||
2,
|
||||
0,
|
||||
3,
|
||||
0,
|
||||
"AUDIO"
|
||||
],
|
||||
[
|
||||
3,
|
||||
3,
|
||||
0,
|
||||
5,
|
||||
0,
|
||||
"*"
|
||||
],
|
||||
[
|
||||
4,
|
||||
3,
|
||||
1,
|
||||
6,
|
||||
0,
|
||||
"*"
|
||||
]
|
||||
],
|
||||
"groups": [],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"scale": 1.7088742511245891,
|
||||
"offset": [
|
||||
-1331.3379874532752,
|
||||
2114.1241784595736
|
||||
]
|
||||
},
|
||||
"frontendVersion": "1.23.4",
|
||||
"VHS_latentpreview": false,
|
||||
"VHS_latentpreviewrate": 0,
|
||||
"VHS_MetadataImage": true,
|
||||
"VHS_KeepIntermediate": true
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
Reference in New Issue
Block a user