added preview widgets
This commit is contained in:
@@ -241,11 +241,12 @@ class LoadAudio:
|
||||
|
||||
def load_audio(self, audio, sr):
|
||||
audio_path = folder_paths.get_annotated_filepath(audio)
|
||||
print(f"{audio_path=} {audio=}")
|
||||
widgetId = get_hash(audio_path)
|
||||
audio_name = os.path.basename(audio).split(".")[0]
|
||||
sr = None if sr=="None" else int(sr)
|
||||
audio = load_input_audio(audio_path,sr=sr)
|
||||
return (audio_name,lambda:audio_to_bytes(*audio))
|
||||
return {"ui": {"preview": [{"filename": audio_name, "type": "input", "widgetId": widgetId}]}, "result": (audio_name, lambda:audio_to_bytes(*audio))}
|
||||
# return (audio_name,lambda:audio_to_bytes(*audio))
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, audio):
|
||||
|
||||
+15
-2
@@ -1,10 +1,13 @@
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from ..lib.audio import SUPPORTED_AUDIO, audio_to_bytes, bytes_to_audio, load_input_audio, save_input_audio
|
||||
from ..lib.utils import get_hash
|
||||
from .utils import increment_filename_no_overwrite
|
||||
import folder_paths
|
||||
|
||||
temp_path = folder_paths.get_temp_directory()
|
||||
|
||||
class PreviewAudio:
|
||||
def __init__(self):
|
||||
pass
|
||||
@@ -18,6 +21,7 @@ class PreviewAudio:
|
||||
"save_format": (SUPPORTED_AUDIO,{"default": "flac"},),
|
||||
"save_channels": ([1,2],{"default": 1}),
|
||||
"overwrite_existing": ("BOOLEAN", {"default": True}),
|
||||
"autoplay": ("BOOLEAN", {"default": True}),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -30,7 +34,7 @@ class PreviewAudio:
|
||||
|
||||
FUNCTION = "save_audio"
|
||||
|
||||
def save_audio(self, audio, filename, save_format, save_channels, overwrite_existing):
|
||||
def save_audio(self, audio, filename, save_format, save_channels, overwrite_existing, autoplay):
|
||||
|
||||
filename = filename.strip()
|
||||
assert filename, "Filename cannot be empty"
|
||||
@@ -44,10 +48,19 @@ class PreviewAudio:
|
||||
if os.path.isfile(output_path) and not overwrite_existing:
|
||||
output_path = increment_filename_no_overwrite(output_path)
|
||||
|
||||
|
||||
input_audio = bytes_to_audio(audio())
|
||||
print(save_input_audio(output_path,input_audio,to_int16=True,to_stereo=save_channels==2))
|
||||
|
||||
return output_path, lambda:audio_to_bytes(*input_audio)
|
||||
tempdir = os.path.join(temp_path,"preview")
|
||||
os.makedirs(tempdir, exist_ok=True)
|
||||
widgetId = get_hash(output_path,save_channels)
|
||||
audio_name = f"{widgetId}.{save_format}"
|
||||
preview_file = os.path.join(tempdir,audio_name)
|
||||
if not os.path.isfile(preview_file): shutil.copyfile(output_path,preview_file)
|
||||
return {"ui": {"preview": [{
|
||||
"filename": audio_name, "type": "temp", "subfolder": "preview", "widgetId": widgetId, "autoplay": autoplay
|
||||
}]}, "result": (output_path, lambda:audio_to_bytes(*input_audio))}
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, audio):
|
||||
|
||||
+9
-2
@@ -1,4 +1,5 @@
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from ..lib.audio import audio_to_bytes, bytes_to_audio, load_input_audio, save_input_audio
|
||||
|
||||
@@ -57,7 +58,8 @@ class RVCNode:
|
||||
|
||||
def convert(self, audio, model, hubert_model, pitch_extraction_params, format, use_cache=True, **kwargs):
|
||||
|
||||
cache_name = os.path.join(BASE_CACHE_DIR,"rvc",f"{get_hash(audio(), model, *pitch_extraction_params.items())}.{format}")
|
||||
widgetId = get_hash(audio(), model, *pitch_extraction_params.items())
|
||||
cache_name = os.path.join(BASE_CACHE_DIR,"rvc",f"{widgetId}.{format}")
|
||||
|
||||
if use_cache and os.path.isfile(cache_name): output_audio = load_input_audio(cache_name)
|
||||
else:
|
||||
@@ -65,7 +67,12 @@ class RVCNode:
|
||||
output_audio = vc_single(hubert_model=hubert_model(),input_audio=input_audio,**model(),**pitch_extraction_params,**kwargs)
|
||||
print(save_input_audio(cache_name, output_audio))
|
||||
|
||||
return (lambda:audio_to_bytes(*output_audio),)
|
||||
tempdir = os.path.join(temp_path,"preview")
|
||||
os.makedirs(tempdir, exist_ok=True)
|
||||
audio_name = os.path.basename(cache_name)
|
||||
preview_file = os.path.join(tempdir,audio_name)
|
||||
if not os.path.isfile(preview_file): shutil.copyfile(cache_name,preview_file)
|
||||
return {"ui": {"preview": [{"filename": audio_name, "type": "temp", "subfolder": "preview", "widgetId": widgetId}]}, "result": (lambda:audio_to_bytes(*output_audio),)}
|
||||
|
||||
|
||||
@classmethod
|
||||
|
||||
+13
-5
@@ -10,7 +10,7 @@ from ..lib.utils import gc_collect, get_hash
|
||||
from .settings.downloader import RVC_DOWNLOAD_LINK, download_file
|
||||
from ..lib import BASE_MODELS_DIR
|
||||
import numpy as np
|
||||
from ..lib.audio import MAX_INT16, audio_to_bytes, bytes_to_audio, merge_audio, remix_audio
|
||||
from ..lib.audio import MAX_INT16, audio_to_bytes, bytes_to_audio, load_input_audio, merge_audio, remix_audio, save_input_audio
|
||||
|
||||
CATEGORY = "🌺RVC-Studio/utils"
|
||||
temp_path = folder_paths.get_temp_directory()
|
||||
@@ -357,10 +357,18 @@ class MergeAudioNode:
|
||||
|
||||
def merge(self, audio1, audio2, sr, merge_type, normalize):
|
||||
if sr=="None": sr=None
|
||||
input_audio1 = bytes_to_audio(audio1())
|
||||
input_audio2 = bytes_to_audio(audio2())
|
||||
merged_audio = merge_audio(input_audio1,input_audio2,sr=sr,to_int16=True,norm=normalize,merge_type=merge_type)
|
||||
return (lambda: audio_to_bytes(*merged_audio),)
|
||||
|
||||
widgetId = get_hash(audio1(),audio2(),sr,merge_type,normalize)
|
||||
audio_path = os.path.join(temp_path,"preview",f"{widgetId}.flac")
|
||||
|
||||
if os.path.isfile(audio_path): merged_audio = load_input_audio(audio_path, sr, norm=normalize)
|
||||
else:
|
||||
input_audio1 = bytes_to_audio(audio1())
|
||||
input_audio2 = bytes_to_audio(audio2())
|
||||
merged_audio = merge_audio(input_audio1,input_audio2,sr=sr,to_int16=True,norm=normalize,merge_type=merge_type)
|
||||
print(save_input_audio(audio_path,merged_audio))
|
||||
audio_name = os.path.basename(audio_path)
|
||||
return {"ui": {"preview": [{"filename": audio_name, "type": "temp", "subfolder": "preview", "widgetId": widgetId}]}, "result": (lambda: audio_to_bytes(*merged_audio),)}
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *args, **kwargs):
|
||||
|
||||
+1
-1
@@ -84,4 +84,4 @@ class UVR5Node:
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *args, **kwargs):
|
||||
print(f"{args=} {kwargs=}")
|
||||
return get_hash(*args, *kwargs.items())
|
||||
return get_hash(args,kwargs)
|
||||
@@ -0,0 +1,894 @@
|
||||
{
|
||||
"last_node_id": 39,
|
||||
"last_link_id": 64,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 7,
|
||||
"type": "UVR5Node",
|
||||
"pos": [
|
||||
269,
|
||||
-736
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 170
|
||||
},
|
||||
"flags": {},
|
||||
"order": 6,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "audio",
|
||||
"type": "VHS_AUDIO",
|
||||
"link": 20
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "primary_stem",
|
||||
"type": "VHS_AUDIO",
|
||||
"links": [
|
||||
49
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "secondary_stem",
|
||||
"type": "VHS_AUDIO",
|
||||
"links": [
|
||||
51
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 1
|
||||
},
|
||||
{
|
||||
"name": "audio_passthrough",
|
||||
"type": "VHS_AUDIO",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "UVR5Node"
|
||||
},
|
||||
"widgets_values": [
|
||||
"karafan/MDX23C-8KFFT-InstVoc_HQ.ckpt",
|
||||
10,
|
||||
"flac",
|
||||
true
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"type": "UVR5Node",
|
||||
"pos": [
|
||||
277,
|
||||
-505
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 170
|
||||
},
|
||||
"flags": {},
|
||||
"order": 7,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "audio",
|
||||
"type": "VHS_AUDIO",
|
||||
"link": 24
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "primary_stem",
|
||||
"type": "VHS_AUDIO",
|
||||
"links": [
|
||||
50
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "secondary_stem",
|
||||
"type": "VHS_AUDIO",
|
||||
"links": [
|
||||
52
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 1
|
||||
},
|
||||
{
|
||||
"name": "audio_passthrough",
|
||||
"type": "VHS_AUDIO",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "UVR5Node"
|
||||
},
|
||||
"widgets_values": [
|
||||
"UVR/HP5-vocals+instrumentals.pth",
|
||||
10,
|
||||
"flac",
|
||||
true
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 17,
|
||||
"type": "UVR5Node",
|
||||
"pos": [
|
||||
272.40658221699283,
|
||||
-161.99581170557863
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 170
|
||||
},
|
||||
"flags": {},
|
||||
"order": 10,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "audio",
|
||||
"type": "VHS_AUDIO",
|
||||
"link": 53
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "primary_stem",
|
||||
"type": "VHS_AUDIO",
|
||||
"links": [
|
||||
56
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "secondary_stem",
|
||||
"type": "VHS_AUDIO",
|
||||
"links": [],
|
||||
"shape": 3,
|
||||
"slot_index": 1
|
||||
},
|
||||
{
|
||||
"name": "audio_passthrough",
|
||||
"type": "VHS_AUDIO",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "UVR5Node"
|
||||
},
|
||||
"widgets_values": [
|
||||
"UVR/5_HP-Karaoke-UVR.pth",
|
||||
10,
|
||||
"flac",
|
||||
true
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"type": "LoadHubertModel",
|
||||
"pos": [
|
||||
306,
|
||||
51
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 58
|
||||
},
|
||||
"flags": {
|
||||
"collapsed": true
|
||||
},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "hubert_model",
|
||||
"type": "HUBERT_MODEL",
|
||||
"links": [
|
||||
58
|
||||
],
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoadHubertModel"
|
||||
},
|
||||
"widgets_values": [
|
||||
"hubert_base.pt"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"type": "LoadPitchExtractionParams",
|
||||
"pos": [
|
||||
288,
|
||||
105
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 178
|
||||
},
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "pitch_extraction_params",
|
||||
"type": "PITCH_EXTRACTION",
|
||||
"links": [
|
||||
59
|
||||
],
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoadPitchExtractionParams"
|
||||
},
|
||||
"widgets_values": [
|
||||
"rmvpe+",
|
||||
false,
|
||||
0.75,
|
||||
0,
|
||||
0.25,
|
||||
0.25
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"type": "LoadRVCModelNode",
|
||||
"pos": [
|
||||
649,
|
||||
-136
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 78
|
||||
},
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "model",
|
||||
"type": "RVC_MODEL",
|
||||
"links": [
|
||||
57
|
||||
],
|
||||
"shape": 3
|
||||
},
|
||||
{
|
||||
"name": "model_name",
|
||||
"type": "STRING",
|
||||
"links": [
|
||||
40
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 1
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoadRVCModelNode"
|
||||
},
|
||||
"widgets_values": [
|
||||
"RVC/loss27_Sayano_e151.pth"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 35,
|
||||
"type": "MergeAudioNode",
|
||||
"pos": [
|
||||
666,
|
||||
-728
|
||||
],
|
||||
"size": [
|
||||
315,
|
||||
126
|
||||
],
|
||||
"flags": {},
|
||||
"order": 8,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "audio1",
|
||||
"type": "VHS_AUDIO",
|
||||
"link": 49
|
||||
},
|
||||
{
|
||||
"name": "audio2",
|
||||
"type": "VHS_AUDIO",
|
||||
"link": 50
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "vhs_audio",
|
||||
"type": "VHS_AUDIO",
|
||||
"links": [
|
||||
53
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "MergeAudioNode"
|
||||
},
|
||||
"widgets_values": [
|
||||
"None",
|
||||
"min",
|
||||
true,
|
||||
{
|
||||
"hidden": false,
|
||||
"paused": false,
|
||||
"params": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 36,
|
||||
"type": "MergeAudioNode",
|
||||
"pos": [
|
||||
676,
|
||||
-528
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 126
|
||||
},
|
||||
"flags": {},
|
||||
"order": 9,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "audio1",
|
||||
"type": "VHS_AUDIO",
|
||||
"link": 51
|
||||
},
|
||||
{
|
||||
"name": "audio2",
|
||||
"type": "VHS_AUDIO",
|
||||
"link": 52
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "vhs_audio",
|
||||
"type": "VHS_AUDIO",
|
||||
"links": [
|
||||
54
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "MergeAudioNode"
|
||||
},
|
||||
"widgets_values": [
|
||||
"None",
|
||||
"mean",
|
||||
true
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 37,
|
||||
"type": "RVCNode",
|
||||
"pos": [
|
||||
643,
|
||||
5
|
||||
],
|
||||
"size": {
|
||||
"0": 309.34271240234375,
|
||||
"1": 166
|
||||
},
|
||||
"flags": {},
|
||||
"order": 11,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "audio",
|
||||
"type": "VHS_AUDIO",
|
||||
"link": 56
|
||||
},
|
||||
{
|
||||
"name": "model",
|
||||
"type": "RVC_MODEL",
|
||||
"link": 57
|
||||
},
|
||||
{
|
||||
"name": "hubert_model",
|
||||
"type": "HUBERT_MODEL",
|
||||
"link": 58
|
||||
},
|
||||
{
|
||||
"name": "pitch_extraction_params",
|
||||
"type": "PITCH_EXTRACTION",
|
||||
"link": 59
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "VHS_AUDIO",
|
||||
"type": "VHS_AUDIO",
|
||||
"links": [
|
||||
60
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "RVCNode"
|
||||
},
|
||||
"widgets_values": [
|
||||
0,
|
||||
"flac",
|
||||
true
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 33,
|
||||
"type": "MergeAudioNode",
|
||||
"pos": [
|
||||
1154,
|
||||
-367
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 126
|
||||
},
|
||||
"flags": {},
|
||||
"order": 12,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "audio1",
|
||||
"type": "VHS_AUDIO",
|
||||
"link": 54
|
||||
},
|
||||
{
|
||||
"name": "audio2",
|
||||
"type": "VHS_AUDIO",
|
||||
"link": 60
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "vhs_audio",
|
||||
"type": "VHS_AUDIO",
|
||||
"links": [
|
||||
63
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "MergeAudioNode"
|
||||
},
|
||||
"widgets_values": [
|
||||
"None",
|
||||
"mean",
|
||||
true
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 31,
|
||||
"type": "JoinStrings",
|
||||
"pos": [
|
||||
726,
|
||||
241
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 106
|
||||
},
|
||||
"flags": {
|
||||
"collapsed": true
|
||||
},
|
||||
"order": 4,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "string1",
|
||||
"type": "STRING",
|
||||
"link": 62,
|
||||
"widget": {
|
||||
"name": "string1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "string2",
|
||||
"type": "STRING",
|
||||
"link": 40,
|
||||
"widget": {
|
||||
"name": "string2"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "STRING",
|
||||
"type": "STRING",
|
||||
"links": [
|
||||
64
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "JoinStrings"
|
||||
},
|
||||
"widgets_values": [
|
||||
"",
|
||||
"",
|
||||
"\\"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 39,
|
||||
"type": "PreviewAudio",
|
||||
"pos": [
|
||||
1514,
|
||||
-358
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 150
|
||||
},
|
||||
"flags": {},
|
||||
"order": 13,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "audio",
|
||||
"type": "VHS_AUDIO",
|
||||
"link": 63
|
||||
},
|
||||
{
|
||||
"name": "filename",
|
||||
"type": "STRING",
|
||||
"link": 64,
|
||||
"widget": {
|
||||
"name": "filename"
|
||||
},
|
||||
"slot_index": 1
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "output_path",
|
||||
"type": "STRING",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
},
|
||||
{
|
||||
"name": "vhs_audio",
|
||||
"type": "VHS_AUDIO",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "PreviewAudio"
|
||||
},
|
||||
"widgets_values": [
|
||||
"test",
|
||||
"flac",
|
||||
2,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 38,
|
||||
"type": "LoadAudio",
|
||||
"pos": [
|
||||
-171,
|
||||
-739
|
||||
],
|
||||
"size": [
|
||||
309.89001187135455,
|
||||
126
|
||||
],
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "audio_name",
|
||||
"type": "STRING",
|
||||
"links": [
|
||||
62
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "vhs_audio",
|
||||
"type": "VHS_AUDIO",
|
||||
"links": [
|
||||
61
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 1
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoadAudio"
|
||||
},
|
||||
"widgets_values": [
|
||||
"already-enough.mp3",
|
||||
"None",
|
||||
"image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"type": "UVR5Node",
|
||||
"pos": [
|
||||
-172,
|
||||
-567
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 170
|
||||
},
|
||||
"flags": {},
|
||||
"order": 5,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "audio",
|
||||
"type": "VHS_AUDIO",
|
||||
"link": 61
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "primary_stem",
|
||||
"type": "VHS_AUDIO",
|
||||
"links": null,
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "secondary_stem",
|
||||
"type": "VHS_AUDIO",
|
||||
"links": [
|
||||
20,
|
||||
24
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 1
|
||||
},
|
||||
{
|
||||
"name": "audio_passthrough",
|
||||
"type": "VHS_AUDIO",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "UVR5Node"
|
||||
},
|
||||
"widgets_values": [
|
||||
"UVR/UVR-DeEcho-DeReverb.pth",
|
||||
10,
|
||||
"flac",
|
||||
true
|
||||
]
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
20,
|
||||
11,
|
||||
1,
|
||||
7,
|
||||
0,
|
||||
"VHS_AUDIO"
|
||||
],
|
||||
[
|
||||
24,
|
||||
11,
|
||||
1,
|
||||
14,
|
||||
0,
|
||||
"VHS_AUDIO"
|
||||
],
|
||||
[
|
||||
40,
|
||||
4,
|
||||
1,
|
||||
31,
|
||||
1,
|
||||
"STRING"
|
||||
],
|
||||
[
|
||||
49,
|
||||
7,
|
||||
0,
|
||||
35,
|
||||
0,
|
||||
"VHS_AUDIO"
|
||||
],
|
||||
[
|
||||
50,
|
||||
14,
|
||||
0,
|
||||
35,
|
||||
1,
|
||||
"VHS_AUDIO"
|
||||
],
|
||||
[
|
||||
51,
|
||||
7,
|
||||
1,
|
||||
36,
|
||||
0,
|
||||
"VHS_AUDIO"
|
||||
],
|
||||
[
|
||||
52,
|
||||
14,
|
||||
1,
|
||||
36,
|
||||
1,
|
||||
"VHS_AUDIO"
|
||||
],
|
||||
[
|
||||
53,
|
||||
35,
|
||||
0,
|
||||
17,
|
||||
0,
|
||||
"VHS_AUDIO"
|
||||
],
|
||||
[
|
||||
54,
|
||||
36,
|
||||
0,
|
||||
33,
|
||||
0,
|
||||
"VHS_AUDIO"
|
||||
],
|
||||
[
|
||||
56,
|
||||
17,
|
||||
0,
|
||||
37,
|
||||
0,
|
||||
"VHS_AUDIO"
|
||||
],
|
||||
[
|
||||
57,
|
||||
4,
|
||||
0,
|
||||
37,
|
||||
1,
|
||||
"RVC_MODEL"
|
||||
],
|
||||
[
|
||||
58,
|
||||
5,
|
||||
0,
|
||||
37,
|
||||
2,
|
||||
"HUBERT_MODEL"
|
||||
],
|
||||
[
|
||||
59,
|
||||
6,
|
||||
0,
|
||||
37,
|
||||
3,
|
||||
"PITCH_EXTRACTION"
|
||||
],
|
||||
[
|
||||
60,
|
||||
37,
|
||||
0,
|
||||
33,
|
||||
1,
|
||||
"VHS_AUDIO"
|
||||
],
|
||||
[
|
||||
61,
|
||||
38,
|
||||
1,
|
||||
11,
|
||||
0,
|
||||
"VHS_AUDIO"
|
||||
],
|
||||
[
|
||||
62,
|
||||
38,
|
||||
0,
|
||||
31,
|
||||
0,
|
||||
"STRING"
|
||||
],
|
||||
[
|
||||
63,
|
||||
33,
|
||||
0,
|
||||
39,
|
||||
0,
|
||||
"VHS_AUDIO"
|
||||
],
|
||||
[
|
||||
64,
|
||||
31,
|
||||
0,
|
||||
39,
|
||||
1,
|
||||
"STRING"
|
||||
]
|
||||
],
|
||||
"groups": [
|
||||
{
|
||||
"title": "Preprocess",
|
||||
"bounding": [
|
||||
-181,
|
||||
-813,
|
||||
336,
|
||||
507
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24
|
||||
},
|
||||
{
|
||||
"title": "Ensemble Vocal",
|
||||
"bounding": [
|
||||
259,
|
||||
-810,
|
||||
742,
|
||||
485
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24
|
||||
},
|
||||
{
|
||||
"title": "Postprocess",
|
||||
"bounding": [
|
||||
262,
|
||||
-236,
|
||||
712,
|
||||
529
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24
|
||||
},
|
||||
{
|
||||
"title": "Output Audio",
|
||||
"bounding": [
|
||||
1144,
|
||||
-441,
|
||||
698,
|
||||
255
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24
|
||||
}
|
||||
],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"scale": 0.5644739300537777,
|
||||
"offset": [
|
||||
-568.7175814310663,
|
||||
890.3856832248159
|
||||
]
|
||||
}
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
import { app } from "../../../scripts/app.js";
|
||||
import { api } from '../../../scripts/api.js'
|
||||
|
||||
function fitHeight(node) {
|
||||
node.setSize([node.size[0], node.computeSize([node.size[0], node.size[1]])[1]])
|
||||
node?.graph?.setDirtyCanvas(true);
|
||||
}
|
||||
function chainCallback(object, property, callback) {
|
||||
if (object == undefined) {
|
||||
//This should not happen.
|
||||
console.error("Tried to add callback to non-existant object")
|
||||
return;
|
||||
}
|
||||
if (property in object) {
|
||||
const callback_orig = object[property]
|
||||
object[property] = function () {
|
||||
const r = callback_orig.apply(this, arguments);
|
||||
callback.apply(this, arguments);
|
||||
return r
|
||||
};
|
||||
} else {
|
||||
object[property] = callback;
|
||||
}
|
||||
}
|
||||
|
||||
function addPreviewOptions(nodeType) {
|
||||
chainCallback(nodeType.prototype, "getExtraMenuOptions", function(_, options) {
|
||||
// The intended way of appending options is returning a list of extra options,
|
||||
// but this isn't used in widgetInputs.js and would require
|
||||
// less generalization of chainCallback
|
||||
let optNew = []
|
||||
try {
|
||||
const previewWidget = this.widgets.find((w) => w.name === "audiopreview");
|
||||
|
||||
let url = null
|
||||
if (previewWidget.audioEl?.hidden == false && previewWidget.audioEl.src) {
|
||||
//Use full quality audio
|
||||
//url = api.apiURL('/view?' + new URLSearchParams(previewWidget.value.params));
|
||||
url = previewWidget.audioEl.src
|
||||
}
|
||||
if (url) {
|
||||
optNew.push(
|
||||
{
|
||||
content: "Open preview",
|
||||
callback: () => {
|
||||
window.open(url, "_blank")
|
||||
},
|
||||
},
|
||||
{
|
||||
content: "Save preview",
|
||||
callback: () => {
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.setAttribute("download", new URLSearchParams(previewWidget.value.params).get("filename"));
|
||||
document.body.append(a);
|
||||
a.click();
|
||||
requestAnimationFrame(() => a.remove());
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
if(options.length > 0 && options[0] != null && optNew.length > 0) {
|
||||
optNew.push(null);
|
||||
}
|
||||
options.unshift(...optNew);
|
||||
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
function previewAudio(node,file,type){
|
||||
var element = document.createElement("div");
|
||||
const previewNode = node;
|
||||
var previewWidget = node.addDOMWidget("audiopreview", "preview", element, {
|
||||
serialize: false,
|
||||
hideOnZoom: false,
|
||||
getValue() {
|
||||
return element.value;
|
||||
},
|
||||
setValue(v) {
|
||||
element.value = v;
|
||||
},
|
||||
});
|
||||
previewWidget.computeSize = function(width) {
|
||||
if (this.aspectRatio && !this.parentEl.hidden) {
|
||||
let height = (previewNode.size[0]-20)/ this.aspectRatio + 10;
|
||||
if (!(height > 0)) {
|
||||
height = 0;
|
||||
}
|
||||
this.computedHeight = height + 10;
|
||||
return [width, height];
|
||||
}
|
||||
return [width, -4];//no loaded src, widget should not display
|
||||
}
|
||||
// element.style['pointer-events'] = "none"
|
||||
previewWidget.value = {hidden: false, paused: false, params: {}}
|
||||
previewWidget.parentEl = document.createElement("div");
|
||||
previewWidget.parentEl.className = "audio_preview";
|
||||
previewWidget.parentEl.style['width'] = "100%"
|
||||
element.appendChild(previewWidget.parentEl);
|
||||
previewWidget.audioEl = document.createElement("audio");
|
||||
previewWidget.audioEl.controls = true;
|
||||
previewWidget.audioEl.loop = false;
|
||||
previewWidget.audioEl.muted = false;
|
||||
previewWidget.audioEl.style['width'] = "100%"
|
||||
previewWidget.audioEl.addEventListener("loadedmetadata", () => {
|
||||
previewWidget.aspectRatio = previewWidget.audioEl.audioWidth / previewWidget.audioEl.audioHeight;
|
||||
fitHeight(this);
|
||||
});
|
||||
previewWidget.audioEl.addEventListener("error", () => {
|
||||
//TODO: consider a way to properly notify the user why a preview isn't shown.
|
||||
previewWidget.parentEl.hidden = true;
|
||||
fitHeight(this);
|
||||
});
|
||||
|
||||
let params = {
|
||||
"filename": file,
|
||||
"type": type,
|
||||
}
|
||||
|
||||
previewWidget.parentEl.hidden = previewWidget.value.hidden;
|
||||
// previewWidget.audioEl.autoplay = !previewWidget.value.paused && !previewWidget.value.hidden;
|
||||
let target_width = 256
|
||||
if (element.style?.width) {
|
||||
//overscale to allow scrolling. Endpoint won't return higher than native
|
||||
target_width = element.style.width.slice(0,-2)*2;
|
||||
}
|
||||
if (!params.force_size || params.force_size.includes("?") || params.force_size == "Disabled") {
|
||||
params.force_size = target_width+"x?"
|
||||
} else {
|
||||
let size = params.force_size.split("x")
|
||||
let ar = parseInt(size[0])/parseInt(size[1])
|
||||
params.force_size = target_width+"x"+(target_width/ar)
|
||||
}
|
||||
|
||||
previewWidget.audioEl.src = api.apiURL('/view?' + new URLSearchParams(params));
|
||||
|
||||
previewWidget.audioEl.hidden = false;
|
||||
previewWidget.parentEl.appendChild(previewWidget.audioEl)
|
||||
}
|
||||
|
||||
app.registerExtension({
|
||||
name: "RVC-Studio.PreviewAudio",
|
||||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||
if (nodeData?.name == "PreviewAudio" && nodeData.category?.includes("RVC")) {
|
||||
console.log({nodeData})
|
||||
nodeType.prototype.onExecuted = function (data) {
|
||||
previewAudio(this, data.audio[0], data.audio[1]);
|
||||
console.log({data})
|
||||
}
|
||||
addPreviewOptions(nodeType)
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,366 @@
|
||||
import { app } from "../../../scripts/app.js";
|
||||
import { api } from '../../../scripts/api.js'
|
||||
import { ComfyWidgets } from "../../../scripts/widgets.js"
|
||||
|
||||
function fitHeight(node) {
|
||||
node.setSize([node.size[0], node.computeSize([node.size[0], node.size[1]])[1]])
|
||||
node?.graph?.setDirtyCanvas(true);
|
||||
}
|
||||
|
||||
function addPreviewWidget(nodeType, nodeData, widgetName="audio", when="onNodeCreated") {
|
||||
|
||||
switch (when){
|
||||
case "onNodeCreated":
|
||||
// TODO: not working
|
||||
// chainCallback(nodeType.prototype, "onNodeCreated", function () {
|
||||
// const audioWidget = this.widgets.find((w) => w.name === widgetName);
|
||||
// if (!audioWidget || !audioWidget?.value) return null
|
||||
|
||||
// const widgetId = `opt_widget-${Math.random()}`
|
||||
// let options = {
|
||||
// widgetId, type: "input", filename: audioWidget.value
|
||||
// }
|
||||
// nodeData.input.hidden = {
|
||||
// ...nodeData.input.hidden,
|
||||
// [widgetId]: ["AUDIOPREVIEW",options]
|
||||
// };
|
||||
// previewAudio(this,options)
|
||||
// audioWidget.callback = function () {
|
||||
// options = {
|
||||
// widgetId, type: "input", filename: audioWidget.value
|
||||
// }
|
||||
// previewAudio(this,options)
|
||||
// if (cb) {
|
||||
// return cb.apply(this, arguments);
|
||||
// }
|
||||
// };
|
||||
// })
|
||||
break;
|
||||
case "onExecuted":
|
||||
chainCallback(nodeType.prototype, "onExecuted", function (data) {
|
||||
const widgetId = `opt_widget-${Math.random()}`
|
||||
nodeData.input.hidden = {
|
||||
...nodeData.input.hidden,
|
||||
[widgetId]: ["AUDIOPREVIEW",{widgetName,...data.preview[0]}]
|
||||
};
|
||||
previewAudio(this,data.preview[0])
|
||||
})
|
||||
break;
|
||||
|
||||
default: console.log({nodeType, nodeData, widgetName, when, node: this})
|
||||
}
|
||||
}
|
||||
|
||||
function previewAudio(node,options={filename: null, type: "input", widgetName: "audio", widgetId: null}){
|
||||
// while (node.widgets.length > 2){
|
||||
// node.widgets.pop();
|
||||
// }
|
||||
try {
|
||||
console.log({node, options})
|
||||
var el = document.getElementById(options.widgetId);
|
||||
el?.remove();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
var element = document.createElement("div");
|
||||
element.id = options.widgetId
|
||||
const previewNode = node;
|
||||
var previewWidget = node.addDOMWidget("audiopreview", "preview", element, {
|
||||
serialize: false,
|
||||
hideOnZoom: false,
|
||||
getValue() {
|
||||
return element.value;
|
||||
},
|
||||
setValue(v) {
|
||||
element.value = v;
|
||||
},
|
||||
});
|
||||
previewWidget.computeSize = function(width) {
|
||||
if (node.aspectRatio && !node.parentEl.hidden) {
|
||||
let height = (previewNode.size[0]-20)/ node.aspectRatio + 10;
|
||||
if (!(height > 0)) {
|
||||
height = 0;
|
||||
}
|
||||
node.computedHeight = height + 10;
|
||||
return [width, height];
|
||||
}
|
||||
return [width, -4];//no loaded src, widget should not display
|
||||
}
|
||||
// element.style['pointer-events'] = "none"
|
||||
previewWidget.value = {hidden: false, paused: false, params: {}}
|
||||
previewWidget.parentEl = document.createElement("div");
|
||||
previewWidget.parentEl.className = "audio_preview";
|
||||
previewWidget.parentEl.style['width'] = "100%"
|
||||
element.appendChild(previewWidget.parentEl);
|
||||
previewWidget.audioEl = document.createElement("audio");
|
||||
previewWidget.audioEl.controls = true;
|
||||
previewWidget.audioEl.loop = false;
|
||||
previewWidget.audioEl.muted = false;
|
||||
previewWidget.audioEl.style['width'] = "100%"
|
||||
previewWidget.audioEl.addEventListener("loadedmetadata", () => {
|
||||
previewWidget.aspectRatio = previewWidget.audioEl.audioWidth / previewWidget.audioEl.audioHeight;
|
||||
fitHeight(node);
|
||||
});
|
||||
previewWidget.audioEl.addEventListener("error", () => {
|
||||
//TODO: consider a way to properly notify the user why a preview isn't shown.
|
||||
previewWidget.parentEl.hidden = true;
|
||||
fitHeight(node);
|
||||
});
|
||||
|
||||
let params = options;
|
||||
|
||||
previewWidget.parentEl.hidden = previewWidget.value.hidden;
|
||||
previewWidget.audioEl.autoplay = !previewWidget.value.paused && !previewWidget.value.hidden && Boolean(options.autoplay);
|
||||
let target_width = 256
|
||||
if (element.style?.width) {
|
||||
//overscale to allow scrolling. Endpoint won't return higher than native
|
||||
target_width = element.style.width.slice(0,-2)*2;
|
||||
}
|
||||
if (!params.force_size || params.force_size.includes("?") || params.force_size == "Disabled") {
|
||||
params.force_size = target_width+"x?"
|
||||
} else {
|
||||
let size = params.force_size.split("x")
|
||||
let ar = parseInt(size[0])/parseInt(size[1])
|
||||
params.force_size = target_width+"x"+(target_width/ar)
|
||||
}
|
||||
|
||||
previewWidget.audioEl.src = api.apiURL('/view?' + new URLSearchParams(params));
|
||||
|
||||
previewWidget.audioEl.hidden = false;
|
||||
previewWidget.parentEl.appendChild(previewWidget.audioEl)
|
||||
return previewWidget
|
||||
}
|
||||
|
||||
function audioPreview(node, inputName, inputData, app) {
|
||||
|
||||
const options = inputData.pop()
|
||||
const audioWidget = node.widgets.find((w) => w.name === options.widgetName);
|
||||
if (!audioWidget) return null
|
||||
|
||||
/*
|
||||
A method that returns the required style for the html
|
||||
*/
|
||||
var default_value = audioWidget.value;
|
||||
Object.defineProperty(audioWidget, "value", {
|
||||
set : function(value) {
|
||||
this._real_value = value;
|
||||
},
|
||||
|
||||
get : function() {
|
||||
let value = "";
|
||||
if (this._real_value) {
|
||||
value = this._real_value;
|
||||
} else {
|
||||
return default_value;
|
||||
}
|
||||
|
||||
if (value.filename) {
|
||||
let real_value = value;
|
||||
value = "";
|
||||
if (real_value.subfolder) {
|
||||
value = real_value.subfolder + "/";
|
||||
}
|
||||
|
||||
value += real_value.filename;
|
||||
|
||||
if(real_value.type && real_value.type !== "input")
|
||||
value += ` [${real_value.type}]`;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
});
|
||||
|
||||
let element = previewAudio(node, {filename: audioWidget.value, ...options});
|
||||
const cb = node.callback;
|
||||
audioWidget.callback = function () {
|
||||
element = previewAudio(node, {filename: audioWidget.value, ...options});
|
||||
if (cb) {
|
||||
return cb.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
// document.body.append(element);
|
||||
// console.log({element})
|
||||
// let uploadWidget = this.addWidget(element);
|
||||
// uploadWidget.options.serialize = false;
|
||||
|
||||
return { widget: element };
|
||||
}
|
||||
|
||||
function chainCallback(object, property, callback) {
|
||||
if (object == undefined) {
|
||||
//This should not happen.
|
||||
console.error("Tried to add callback to non-existant object")
|
||||
return;
|
||||
}
|
||||
if (property in object) {
|
||||
const callback_orig = object[property]
|
||||
object[property] = function () {
|
||||
const r = callback_orig.apply(this, arguments);
|
||||
callback.apply(this, arguments);
|
||||
return r
|
||||
};
|
||||
} else {
|
||||
object[property] = callback;
|
||||
}
|
||||
}
|
||||
|
||||
async function uploadFile(file) {
|
||||
//TODO: Add uploaded file to cache with Cache.put()?
|
||||
try {
|
||||
// Wrap file in formdata so it includes filename
|
||||
const body = new FormData();
|
||||
const i = file.webkitRelativePath.lastIndexOf('/');
|
||||
const subfolder = file.webkitRelativePath.slice(0,i+1)
|
||||
const new_file = new File([file], file.name, {
|
||||
type: file.type,
|
||||
lastModified: file.lastModified,
|
||||
});
|
||||
body.append("image", new_file);
|
||||
if (i > 0) {
|
||||
body.append("subfolder", subfolder);
|
||||
}
|
||||
const resp = await api.fetchApi("/upload/image", {
|
||||
method: "POST",
|
||||
body,
|
||||
});
|
||||
|
||||
if (resp.status === 200) {
|
||||
return resp.status
|
||||
} else {
|
||||
alert(resp.status + " - " + resp.statusText);
|
||||
}
|
||||
} catch (error) {
|
||||
alert(error);
|
||||
}
|
||||
}
|
||||
|
||||
function addUploadWidget(nodeType, nodeData, widgetName, type="video") {
|
||||
|
||||
chainCallback(nodeType.prototype, "onNodeCreated", function() {
|
||||
const pathWidget = this.widgets.find((w) => w.name === widgetName);
|
||||
const fileInput = document.createElement("input");
|
||||
chainCallback(nodeType.prototype, "onRemoved", () => {
|
||||
fileInput?.remove();
|
||||
});
|
||||
if (type == "folder") {
|
||||
Object.assign(fileInput, {
|
||||
type: "file",
|
||||
style: "display: none",
|
||||
webkitdirectory: true,
|
||||
onchange: async () => {
|
||||
const directory = fileInput.files[0].webkitRelativePath;
|
||||
const i = directory.lastIndexOf('/');
|
||||
if (i <= 0) {
|
||||
throw "No directory found";
|
||||
}
|
||||
const path = directory.slice(0,directory.lastIndexOf('/'))
|
||||
if (pathWidget.options.values.includes(path)) {
|
||||
alert("A folder of the same name already exists");
|
||||
return;
|
||||
}
|
||||
let successes = 0;
|
||||
for(const file of fileInput.files) {
|
||||
if (await uploadFile(file) == 200) {
|
||||
successes++;
|
||||
} else {
|
||||
//Upload failed, but some prior uploads may have succeeded
|
||||
//Stop future uploads to prevent cascading failures
|
||||
//and only add to list if an upload has succeeded
|
||||
if (successes > 0) {
|
||||
break
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
pathWidget.options.values.push(path);
|
||||
pathWidget.value = path;
|
||||
if (pathWidget.callback) {
|
||||
pathWidget.callback(path)
|
||||
}
|
||||
},
|
||||
});
|
||||
} else if (type == "video") {
|
||||
Object.assign(fileInput, {
|
||||
type: "file",
|
||||
accept: "video/webm,video/mp4,video/mkv,image/gif",
|
||||
style: "display: none",
|
||||
onchange: async () => {
|
||||
if (fileInput.files.length) {
|
||||
if (await uploadFile(fileInput.files[0]) != 200) {
|
||||
//upload failed and file can not be added to options
|
||||
return;
|
||||
}
|
||||
const filename = fileInput.files[0].name;
|
||||
pathWidget.options.values.push(filename);
|
||||
pathWidget.value = filename;
|
||||
if (pathWidget.callback) {
|
||||
pathWidget.callback(filename)
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
} else if (type == "audio") {
|
||||
Object.assign(fileInput, {
|
||||
type: "file",
|
||||
accept: "audio/mpeg,audio/wav,audio/x-wav,audio/ogg",
|
||||
style: "display: none",
|
||||
onchange: async () => {
|
||||
if (fileInput.files.length) {
|
||||
if (await uploadFile(fileInput.files[0]) != 200) {
|
||||
//upload failed and file can not be added to options
|
||||
return;
|
||||
}
|
||||
const filename = fileInput.files[0].name;
|
||||
pathWidget.options.values.push(filename);
|
||||
pathWidget.value = filename;
|
||||
if (pathWidget.callback) {
|
||||
pathWidget.callback(filename)
|
||||
}
|
||||
previewAudio(this, {filename, type: "input", widgetId: filename})
|
||||
}
|
||||
},
|
||||
});
|
||||
}else {
|
||||
throw "Unknown upload type"
|
||||
}
|
||||
document.body.append(fileInput);
|
||||
let uploadWidget = this.addWidget("button", "choose " + type + " to upload", "image", () => {
|
||||
//clear the active click event
|
||||
app.canvas.node_widget = null
|
||||
|
||||
fileInput.click();
|
||||
});
|
||||
uploadWidget.options.serialize = false;
|
||||
});
|
||||
}
|
||||
|
||||
// ComfyWidgets.AUDIOPREVIEW = audioPreview;
|
||||
|
||||
app.registerExtension({
|
||||
name: "RVC-Studio.UI",
|
||||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||
if (nodeData.category?.includes("RVC")){
|
||||
switch (nodeData?.name){
|
||||
case "LoadAudio":
|
||||
addUploadWidget(nodeType, nodeData, "audio", "audio")
|
||||
addPreviewWidget(nodeType, nodeData, "audio", "onNodeCreated" )
|
||||
break
|
||||
case "PreviewAudio":
|
||||
addPreviewWidget(nodeType, nodeData, "audio", "onExecuted" )
|
||||
break
|
||||
case "MergeAudioNode":
|
||||
addPreviewWidget(nodeType, nodeData, "audio", "onExecuted" )
|
||||
break;
|
||||
case "RVCNode":
|
||||
addPreviewWidget(nodeType, nodeData, "audio", "onExecuted" )
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,203 +0,0 @@
|
||||
import { app } from "../../../scripts/app.js";
|
||||
import { api } from '../../../scripts/api.js'
|
||||
import { ComfyWidgets } from "../../../scripts/widgets.js"
|
||||
|
||||
function fitHeight(node) {
|
||||
node.setSize([node.size[0], node.computeSize([node.size[0], node.size[1]])[1]])
|
||||
node?.graph?.setDirtyCanvas(true);
|
||||
}
|
||||
|
||||
function previewAudio(node,file){
|
||||
while (node.widgets.length > 2){
|
||||
node.widgets.pop();
|
||||
}
|
||||
try {
|
||||
var el = document.getElementById("uploadAudio");
|
||||
el?.remove();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
var element = document.createElement("div");
|
||||
element.id = "uploadAudio";
|
||||
const previewNode = node;
|
||||
var previewWidget = node.addDOMWidget("audiopreview", "preview", element, {
|
||||
serialize: false,
|
||||
hideOnZoom: false,
|
||||
getValue() {
|
||||
return element.value;
|
||||
},
|
||||
setValue(v) {
|
||||
element.value = v;
|
||||
},
|
||||
});
|
||||
previewWidget.computeSize = function(width) {
|
||||
if (this.aspectRatio && !this.parentEl.hidden) {
|
||||
let height = (previewNode.size[0]-20)/ this.aspectRatio + 10;
|
||||
if (!(height > 0)) {
|
||||
height = 0;
|
||||
}
|
||||
this.computedHeight = height + 10;
|
||||
return [width, height];
|
||||
}
|
||||
return [width, -4];//no loaded src, widget should not display
|
||||
}
|
||||
// element.style['pointer-events'] = "none"
|
||||
previewWidget.value = {hidden: false, paused: false, params: {}}
|
||||
previewWidget.parentEl = document.createElement("div");
|
||||
previewWidget.parentEl.className = "audio_preview";
|
||||
previewWidget.parentEl.style['width'] = "100%"
|
||||
element.appendChild(previewWidget.parentEl);
|
||||
previewWidget.audioEl = document.createElement("audio");
|
||||
previewWidget.audioEl.controls = true;
|
||||
previewWidget.audioEl.loop = false;
|
||||
previewWidget.audioEl.muted = false;
|
||||
previewWidget.audioEl.style['width'] = "100%"
|
||||
previewWidget.audioEl.addEventListener("loadedmetadata", () => {
|
||||
previewWidget.aspectRatio = previewWidget.audioEl.audioWidth / previewWidget.audioEl.audioHeight;
|
||||
fitHeight(node);
|
||||
});
|
||||
previewWidget.audioEl.addEventListener("error", () => {
|
||||
//TODO: consider a way to properly notify the user why a preview isn't shown.
|
||||
previewWidget.parentEl.hidden = true;
|
||||
fitHeight(node);
|
||||
});
|
||||
|
||||
let params = {
|
||||
"filename": file,
|
||||
"type": "input",
|
||||
}
|
||||
|
||||
previewWidget.parentEl.hidden = previewWidget.value.hidden;
|
||||
// previewWidget.audioEl.autoplay = !previewWidget.value.paused && !previewWidget.value.hidden;
|
||||
let target_width = 256
|
||||
if (element.style?.width) {
|
||||
//overscale to allow scrolling. Endpoint won't return higher than native
|
||||
target_width = element.style.width.slice(0,-2)*2;
|
||||
}
|
||||
if (!params.force_size || params.force_size.includes("?") || params.force_size == "Disabled") {
|
||||
params.force_size = target_width+"x?"
|
||||
} else {
|
||||
let size = params.force_size.split("x")
|
||||
let ar = parseInt(size[0])/parseInt(size[1])
|
||||
params.force_size = target_width+"x"+(target_width/ar)
|
||||
}
|
||||
|
||||
previewWidget.audioEl.src = api.apiURL('/view?' + new URLSearchParams(params));
|
||||
|
||||
previewWidget.audioEl.hidden = false;
|
||||
previewWidget.parentEl.appendChild(previewWidget.audioEl)
|
||||
}
|
||||
|
||||
function audioUpload(node, inputName, inputData, app) {
|
||||
const audioWidget = node.widgets.find((w) => w.name === "audio");
|
||||
let uploadWidget;
|
||||
/*
|
||||
A method that returns the required style for the html
|
||||
*/
|
||||
var default_value = audioWidget.value;
|
||||
Object.defineProperty(audioWidget, "value", {
|
||||
set : function(value) {
|
||||
this._real_value = value;
|
||||
},
|
||||
|
||||
get : function() {
|
||||
let value = "";
|
||||
if (this._real_value) {
|
||||
value = this._real_value;
|
||||
} else {
|
||||
return default_value;
|
||||
}
|
||||
|
||||
if (value.filename) {
|
||||
let real_value = value;
|
||||
value = "";
|
||||
if (real_value.subfolder) {
|
||||
value = real_value.subfolder + "/";
|
||||
}
|
||||
|
||||
value += real_value.filename;
|
||||
|
||||
if(real_value.type && real_value.type !== "input")
|
||||
value += ` [${real_value.type}]`;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
});
|
||||
async function uploadFile(file, updateNode, pasted = false) {
|
||||
try {
|
||||
// Wrap file in formdata so it includes filename
|
||||
const body = new FormData();
|
||||
body.append("image", file);
|
||||
if (pasted) body.append("subfolder", "pasted");
|
||||
const resp = await api.fetchApi("/upload/image", {
|
||||
method: "POST",
|
||||
body,
|
||||
});
|
||||
|
||||
if (resp.status === 200) {
|
||||
const data = await resp.json();
|
||||
// Add the file to the dropdown list and update the widget value
|
||||
let path = data.name;
|
||||
if (data.subfolder) path = data.subfolder + "/" + path;
|
||||
|
||||
if (!audioWidget.options.values.includes(path)) {
|
||||
audioWidget.options.values.push(path);
|
||||
}
|
||||
|
||||
if (updateNode) {
|
||||
audioWidget.value = path;
|
||||
previewAudio(node,path)
|
||||
|
||||
}
|
||||
} else {
|
||||
alert(resp.status + " - " + resp.statusText);
|
||||
}
|
||||
} catch (error) {
|
||||
alert(error);
|
||||
}
|
||||
}
|
||||
|
||||
const fileInput = document.createElement("input");
|
||||
Object.assign(fileInput, {
|
||||
type: "file",
|
||||
accept: "audio/mp3,audio/wav,audio/flac,audio/m4a",
|
||||
style: "display: none",
|
||||
onchange: async () => {
|
||||
if (fileInput.files.length) {
|
||||
await uploadFile(fileInput.files[0], true);
|
||||
}
|
||||
},
|
||||
});
|
||||
document.body.append(fileInput);
|
||||
|
||||
// Create the button widget for selecting the files
|
||||
uploadWidget = node.addWidget("button", "choose audio file to upload", "Audio", () => {
|
||||
fileInput.click();
|
||||
});
|
||||
|
||||
uploadWidget.serialize = false;
|
||||
|
||||
previewAudio(node, audioWidget.value);
|
||||
const cb = node.callback;
|
||||
audioWidget.callback = function () {
|
||||
previewAudio(node,audioWidget.value);
|
||||
if (cb) {
|
||||
return cb.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
|
||||
return { widget: uploadWidget };
|
||||
}
|
||||
|
||||
ComfyWidgets.AUDIOPLOAD = audioUpload;
|
||||
|
||||
app.registerExtension({
|
||||
name: "RVC-Studio.LoadAudio",
|
||||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||
if (nodeData?.name == "LoadAudio" && nodeData.category?.includes("RVC")) {
|
||||
console.log({nodeData})
|
||||
nodeData.input.required.upload = ["AUDIOPLOAD"];
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user