Mise a jour du node: Corection inputs, logique target_size et audio
This commit is contained in:
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 [VOTRE NOM OU PSEUDO]
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+98
-59
@@ -7,7 +7,6 @@ from PIL import Image
|
||||
import shutil
|
||||
import datetime
|
||||
import platform
|
||||
# NOUVEL IMPORT OBLIGATOIRE
|
||||
from scipy.io.wavfile import write as write_wav
|
||||
|
||||
class VideoCompressor:
|
||||
@@ -15,21 +14,29 @@ class VideoCompressor:
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
# --- ENTRÉES DOT ---
|
||||
"images": ("IMAGE",),
|
||||
"fps": ("FLOAT", {"default": 24.0, "min": 1.0, "max": 120.0, "step": 0.01}),
|
||||
"mode": (["2pass", "fast_crf", "gpu"], {"default": "2pass"}),
|
||||
"target_mb": ("FLOAT", {"min": 0.5, "max": 4000.0, "step": 0.5, "default": 8.0}),
|
||||
|
||||
# --- PARAMÈTRES (WIDGETS) ---
|
||||
# Si target_mb = 0, on utilise le CRF. Sinon, on vise la taille en Mo.
|
||||
"target_mb": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 10000.0, "step": 0.5, "display": "number"}),
|
||||
|
||||
# Qualité (si target_mb est à 0)
|
||||
"crf": ("INT", {"default": 23, "min": 0, "max": 51, "step": 1}),
|
||||
|
||||
# Codec : Liste explicite de chaînes de caractères pour éviter l'index "1"
|
||||
"codec": (["libx264", "libx265", "h264_nvenc", "hevc_nvenc"], {"default": "libx264"}),
|
||||
|
||||
"preset": (["ultrafast","superfast","veryfast","faster","fast","medium","slow","slower","veryslow"], {"default": "fast"}),
|
||||
},
|
||||
"optional": {
|
||||
"audio": ("*",),
|
||||
"crf": ("INT", {"default": 23, "min": 10, "max": 40}),
|
||||
"preset": (["ultrafast","superfast","veryfast","faster","fast","medium","slow","slower","veryslow"], {"default": "fast"}),
|
||||
"audio": ("AUDIO",),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING", "IMAGE", "*")
|
||||
RETURN_NAMES = ("video_path", "images", "audio")
|
||||
RETURN_TYPES = ("IMAGE", "AUDIO", "FLOAT", "STRING")
|
||||
RETURN_NAMES = ("images", "audio", "fps", "video_path")
|
||||
FUNCTION = "process"
|
||||
CATEGORY = "🎥Video Utils"
|
||||
|
||||
@@ -40,12 +47,22 @@ class VideoCompressor:
|
||||
print(f"--- ERREUR FFmpeg ---\nCommande: {' '.join(e.cmd)}\nErreur: {e.stderr}\n--- FIN ERREUR ---")
|
||||
raise e
|
||||
|
||||
def process(self, images, fps, mode, target_mb, codec, audio=None, crf=23, preset="fast"):
|
||||
gpu_codecs = ["h264_nvenc", "hevc_nvenc"]
|
||||
if mode == "gpu" and codec not in gpu_codecs: codec = "h264_nvenc"
|
||||
elif mode in ["fast_crf", "2pass"] and codec in gpu_codecs: codec = "libx264"
|
||||
def process(self, images, fps, target_mb, codec, crf=23, preset="fast", audio=None):
|
||||
# --- 1. DÉTECTION DU MODE ---
|
||||
# Si target_mb > 0, on passe en mode "Taille Cible" (2-pass), sinon mode "Qualité" (CRF)
|
||||
if target_mb > 0.0:
|
||||
mode = "target_size"
|
||||
print(f"🔧 Mode détecté : Taille cible de {target_mb} MB (2-Pass)")
|
||||
else:
|
||||
mode = "crf"
|
||||
print(f"🔧 Mode détecté : Qualité constante (CRF {crf}) - Taille cible ignorée (0)")
|
||||
|
||||
temp_dir = os.path.join(folder_paths.get_temp_directory(), "compressor_final")
|
||||
# Gestion GPU/CPU
|
||||
gpu_codecs = ["h264_nvenc", "hevc_nvenc"]
|
||||
is_gpu = codec in gpu_codecs
|
||||
|
||||
# --- 2. PRÉPARATION DOSSIERS ---
|
||||
temp_dir = os.path.join(folder_paths.get_temp_directory(), "comfy_compressor_v2")
|
||||
shutil.rmtree(temp_dir, ignore_errors=True)
|
||||
os.makedirs(temp_dir, exist_ok=True)
|
||||
|
||||
@@ -53,82 +70,104 @@ class VideoCompressor:
|
||||
clean_video_path = os.path.join(temp_dir, "clean_video.mp4")
|
||||
pass_log_file = os.path.join(temp_dir, "ffmpeg_passlog")
|
||||
|
||||
# --- LOGIQUE AUDIO FINALE ET ROBUSTE ---
|
||||
# --- 3. GESTION AUDIO (Identique, robuste) ---
|
||||
audio_file_path = None
|
||||
has_audio = False
|
||||
|
||||
if audio is not None:
|
||||
# CAS 1: L'entrée est des données audio brutes (un dictionnaire)
|
||||
if isinstance(audio, dict) and 'waveform' in audio and 'sample_rate' in audio:
|
||||
print("Détection de données audio brutes. Sauvegarde en fichier temporaire...")
|
||||
waveform = audio['waveform'].cpu().numpy()
|
||||
waveform = audio['waveform']
|
||||
sample_rate = audio['sample_rate']
|
||||
|
||||
# S'assurer que la forme est [samples, channels]
|
||||
if torch.is_tensor(waveform): waveform = waveform.cpu().numpy()
|
||||
if waveform.ndim == 3: waveform = waveform.squeeze(0)
|
||||
if waveform.shape[0] < waveform.shape[1]: waveform = waveform.T
|
||||
|
||||
# Convertir de float [-1, 1] à int16 pour le fichier .wav
|
||||
waveform_int16 = (waveform * 32767).astype(np.int16)
|
||||
|
||||
temp_wav_path = os.path.join(temp_dir, "temp_audio.wav")
|
||||
write_wav(temp_wav_path, sample_rate, waveform_int16)
|
||||
audio_file_path = temp_wav_path
|
||||
has_audio = True
|
||||
|
||||
# CAS 2: L'entrée est un chemin de fichier (string, list, ou tuple)
|
||||
else:
|
||||
path_candidate = ""
|
||||
if isinstance(audio, (list, tuple)) and len(audio) > 0:
|
||||
path_candidate = str(audio[0])
|
||||
elif isinstance(audio, str):
|
||||
path_candidate = audio
|
||||
|
||||
path_candidate = str(audio[0]) if isinstance(audio, (list, tuple)) and len(audio) > 0 else str(audio)
|
||||
if os.path.exists(path_candidate):
|
||||
audio_file_path = path_candidate
|
||||
has_audio = True
|
||||
else:
|
||||
print(f"Avertissement: L'entrée audio fournie '{audio}' n'est ni des données brutes, ni un chemin valide.")
|
||||
|
||||
try:
|
||||
# --- 4. SAUVEGARDE IMAGES ---
|
||||
print(f"💾 Écriture des {len(images)} frames...")
|
||||
for i, img in enumerate(images):
|
||||
Image.fromarray((255. * img.cpu().numpy()).astype(np.uint8)).save(frames_path % i)
|
||||
|
||||
self._run_ffmpeg(["ffmpeg", "-y", "-framerate", str(fps), "-i", frames_path, "-c:v", "libx264", "-pix_fmt", "yuv420p", clean_video_path])
|
||||
# --- 5. CRÉATION VIDÉO SOURCE ---
|
||||
self._run_ffmpeg([
|
||||
"ffmpeg", "-y", "-framerate", str(fps), "-i", frames_path,
|
||||
"-c:v", "libx264", "-pix_fmt", "yuv420p", clean_video_path
|
||||
])
|
||||
|
||||
duration_s = len(images) / float(fps)
|
||||
if duration_s <= 0.001: duration_s = 1.0 # Sécurité division par zéro
|
||||
|
||||
# --- 6. CALCUL BITRATE ET PARAMÈTRES ---
|
||||
output_dir = os.path.join(folder_paths.get_output_directory(), "compressed_videos")
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
timestamp = datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
|
||||
final_video_path = os.path.join(output_dir, f"compressed_{timestamp}.mp4")
|
||||
final_video_path = os.path.join(output_dir, f"render_{timestamp}.mp4")
|
||||
|
||||
if duration_s <= 0: raise ValueError("Durée invalide.")
|
||||
|
||||
# ... Le reste du code utilise 'audio_file_path' qui est maintenant GARANTI d'être un chemin valide ...
|
||||
|
||||
if mode == "2pass":
|
||||
audio_bitrate_bits = 128000 if has_audio else 0
|
||||
video_bitrate_k = int(((target_mb * 1024 * 1024 * 8 / duration_s) - audio_bitrate_bits) / 1000)
|
||||
if video_bitrate_k < 100: video_bitrate_k = 100
|
||||
if mode == "target_size":
|
||||
# Calcul Bitrate : (Taille_MB * 8192) / durée
|
||||
# On soustrait l'audio (approx 128k)
|
||||
audio_bitrate_kbps = 128 if has_audio else 0
|
||||
total_bitrate_kbps = (target_mb * 8192) / duration_s
|
||||
video_bitrate_k = int(total_bitrate_kbps - audio_bitrate_kbps)
|
||||
|
||||
if video_bitrate_k < 50:
|
||||
print("⚠️ Bitrate calculé trop bas, forcé à 100k.")
|
||||
video_bitrate_k = 100
|
||||
|
||||
pass1_cmd = ["ffmpeg", "-y", "-i", clean_video_path, "-c:v", codec, "-b:v", f"{video_bitrate_k}k", "-preset", preset, "-pass", "1", "-an", "-f", "mp4", "-passlogfile", pass_log_file, "NUL" if platform.system() == "Windows" else "/dev/null"]
|
||||
self._run_ffmpeg(pass1_cmd)
|
||||
# --- ENCODAGE 2 PASSES (Target Size) ---
|
||||
print(f"⚙️ Compression 2-Passes -> Cible: {target_mb}MB (Bitrate: {video_bitrate_k}k)")
|
||||
|
||||
# Pass 1
|
||||
pass1 = [
|
||||
"ffmpeg", "-y", "-i", clean_video_path, "-c:v", codec,
|
||||
"-b:v", f"{video_bitrate_k}k", "-preset", preset,
|
||||
"-pass", "1", "-an", "-f", "mp4",
|
||||
"-passlogfile", pass_log_file,
|
||||
"NUL" if platform.system() == "Windows" else "/dev/null"
|
||||
]
|
||||
self._run_ffmpeg(pass1)
|
||||
|
||||
# Pass 2
|
||||
pass2 = ["ffmpeg", "-y", "-i", clean_video_path]
|
||||
if has_audio: pass2.extend(["-i", audio_file_path])
|
||||
pass2.extend([
|
||||
"-c:v", codec, "-b:v", f"{video_bitrate_k}k",
|
||||
"-preset", preset, "-pass", "2", "-passlogfile", pass_log_file
|
||||
])
|
||||
if has_audio: pass2.extend(["-c:a", "aac", "-b:a", "128k", "-map", "0:v:0", "-map", "1:a:0"])
|
||||
pass2.append(final_video_path)
|
||||
self._run_ffmpeg(pass2)
|
||||
|
||||
pass2_cmd = ["ffmpeg", "-y", "-i", clean_video_path]
|
||||
if has_audio: pass2_cmd.extend(["-i", audio_file_path])
|
||||
pass2_cmd.extend(["-c:v", codec, "-b:v", f"{video_bitrate_k}k", "-preset", preset, "-pass", "2", "-passlogfile", pass_log_file])
|
||||
if has_audio: pass2_cmd.extend(["-c:a", "aac", "-b:a", "128k", "-map", "0:v:0", "-map", "1:a:0"])
|
||||
pass2_cmd.append(final_video_path)
|
||||
self._run_ffmpeg(pass2_cmd)
|
||||
else:
|
||||
final_cmd = ["ffmpeg", "-y", "-i", clean_video_path]
|
||||
if has_audio: final_cmd.extend(["-i", audio_file_path])
|
||||
quality_param = ["-crf", str(crf)] if mode == "fast_crf" else ["-cq", str(crf)]
|
||||
final_cmd.extend(["-c:v", codec, "-preset", preset] + quality_param)
|
||||
if has_audio: final_cmd.extend(["-c:a", "aac", "-b:a", "128k", "-map", "0:v:0", "-map", "1:a:0"])
|
||||
final_cmd.append(final_video_path)
|
||||
self._run_ffmpeg(final_cmd)
|
||||
# --- ENCODAGE CRF/CQ (Qualité) ---
|
||||
print(f"⚙️ Compression Qualité -> CRF: {crf}")
|
||||
cmd = ["ffmpeg", "-y", "-i", clean_video_path]
|
||||
if has_audio: cmd.extend(["-i", audio_file_path])
|
||||
|
||||
# NVENC utilise -cq au lieu de -crf, et nécessite souvent -b:v 0 pour ignorer le bitrate
|
||||
if is_gpu:
|
||||
quality_args = ["-cq", str(crf), "-b:v", "0"]
|
||||
else:
|
||||
quality_args = ["-crf", str(crf)]
|
||||
|
||||
cmd.extend(["-c:v", codec, "-preset", preset] + quality_args)
|
||||
|
||||
if has_audio: cmd.extend(["-c:a", "aac", "-b:a", "128k", "-map", "0:v:0", "-map", "1:a:0"])
|
||||
cmd.append(final_video_path)
|
||||
self._run_ffmpeg(cmd)
|
||||
|
||||
return (final_video_path, images, audio if has_audio else "")
|
||||
return (images, audio, fps, final_video_path)
|
||||
|
||||
finally:
|
||||
shutil.rmtree(temp_dir, ignore_errors=True)
|
||||
if os.path.exists(temp_dir):
|
||||
shutil.rmtree(temp_dir, ignore_errors=True)
|
||||
@@ -1 +1 @@
|
||||
{"id":"219e2ee3-b5f4-4a9e-9c4c-ed0988e62ddd","revision":0,"last_node_id":27,"last_link_id":42,"nodes":[{"id":12,"type":"LoadVideo","pos":[590.2651977539062,445.7998352050781],"size":[274.080078125,439.4400939941406],"flags":{},"order":0,"mode":0,"inputs":[{"localized_name":"file","name":"file","type":"COMBO","widget":{"name":"file"},"link":null},{"localized_name":"choose file to upload","name":"upload","type":"IMAGEUPLOAD","widget":{"name":"upload"},"link":null}],"outputs":[{"localized_name":"VIDEO","name":"VIDEO","type":"VIDEO","links":[11]}],"properties":{"cnr_id":"comfy-core","ver":"0.3.60","Node name for S&R":"LoadVideo"},"widgets_values":["Bitch elle boit de la bière.mp4","image"]},{"id":17,"type":"SaveVideo","pos":[1811.84033203125,448.95257568359375],"size":[600,898],"flags":{},"order":4,"mode":0,"inputs":[{"localized_name":"video","name":"video","type":"VIDEO","link":15},{"localized_name":"filename_prefix","name":"filename_prefix","type":"STRING","widget":{"name":"filename_prefix"},"link":null},{"localized_name":"format","name":"format","type":"COMBO","widget":{"name":"format"},"link":null},{"localized_name":"codec","name":"codec","type":"COMBO","widget":{"name":"codec"},"link":null}],"outputs":[],"properties":{"cnr_id":"comfy-core","ver":"0.3.59","Node name for S&R":"SaveVideo"},"widgets_values":["video/ComfyUI","auto","auto"]},{"id":16,"type":"CreateVideo","pos":[1508.453125,442.51605224609375],"size":[270,78],"flags":{},"order":3,"mode":0,"inputs":[{"localized_name":"images","name":"images","type":"IMAGE","link":39},{"localized_name":"audio","name":"audio","shape":7,"type":"AUDIO","link":41},{"localized_name":"fps","name":"fps","type":"FLOAT","widget":{"name":"fps"},"link":null}],"outputs":[{"localized_name":"VIDEO","name":"VIDEO","type":"VIDEO","links":[15]}],"properties":{"cnr_id":"comfy-core","ver":"0.3.59","Node name for S&R":"CreateVideo"},"widgets_values":[16]},{"id":27,"type":"VideoCompressor","pos":[1177.84326171875,536.1892700195312],"size":[270,218],"flags":{},"order":2,"mode":0,"inputs":[{"localized_name":"images","name":"images","type":"IMAGE","link":40},{"localized_name":"audio","name":"audio","shape":7,"type":"*","link":38},{"localized_name":"fps","name":"fps","type":"FLOAT","widget":{"name":"fps"},"link":42},{"localized_name":"mode","name":"mode","type":"COMBO","widget":{"name":"mode"},"link":null},{"localized_name":"target_mb","name":"target_mb","type":"FLOAT","widget":{"name":"target_mb"},"link":null},{"localized_name":"codec","name":"codec","type":"COMBO","widget":{"name":"codec"},"link":null},{"localized_name":"crf","name":"crf","shape":7,"type":"INT","widget":{"name":"crf"},"link":null},{"localized_name":"preset","name":"preset","shape":7,"type":"COMBO","widget":{"name":"preset"},"link":null}],"outputs":[{"localized_name":"video_path","name":"video_path","type":"STRING","links":null},{"localized_name":"images","name":"images","type":"IMAGE","links":[39]},{"localized_name":"audio","name":"audio","type":"*","links":[41]}],"properties":{"aux_id":"Verolelb/ComfyUI-VideoCompressor","ver":"dc549d8d5711c1695ca8dbbd6e5dec7ecc8f4efc","Node name for S&R":"VideoCompressor"},"widgets_values":[24,"2pass",8,"libx264",23,"fast"]},{"id":13,"type":"GetVideoComponents","pos":[905.2611694335938,465.8274230957031],"size":[190,70],"flags":{"collapsed":false},"order":1,"mode":0,"inputs":[{"localized_name":"video","name":"video","type":"VIDEO","link":11}],"outputs":[{"localized_name":"images","name":"images","type":"IMAGE","links":[40]},{"localized_name":"audio","name":"audio","type":"AUDIO","links":[38]},{"localized_name":"fps","name":"fps","type":"FLOAT","links":[42]}],"properties":{"cnr_id":"comfy-core","ver":"0.3.59","Node name for S&R":"GetVideoComponents"},"widgets_values":[]}],"links":[[11,12,0,13,0,"VIDEO"],[15,16,0,17,0,"VIDEO"],[38,13,1,27,1,"*"],[39,27,1,16,0,"IMAGE"],[40,13,0,27,0,"IMAGE"],[41,27,2,16,1,"AUDIO"],[42,13,2,27,2,"FLOAT"]],"groups":[],"config":{},"extra":{"ds":{"scale":0.8536167070900924,"offset":[-408.32279921247004,-230.1745309529742]}},"version":0.4}
|
||||
{"id":"219e2ee3-b5f4-4a9e-9c4c-ed0988e62ddd","revision":0,"last_node_id":33,"last_link_id":60,"nodes":[{"id":32,"type":"VideoCompressor","pos":[1242.6033816614238,508.8625343965848],"size":[210,214],"flags":{},"order":3,"mode":0,"inputs":[{"localized_name":"images","name":"images","type":"IMAGE","link":55},{"localized_name":"audio","name":"audio","shape":7,"type":"AUDIO","link":56},{"localized_name":"fps","name":"fps","type":"FLOAT","widget":{"name":"fps"},"link":57},{"localized_name":"target_mb","name":"target_mb","type":"FLOAT","widget":{"name":"target_mb"},"link":null},{"localized_name":"crf","name":"crf","type":"INT","widget":{"name":"crf"},"link":null},{"localized_name":"codec","name":"codec","type":"COMBO","widget":{"name":"codec"},"link":null},{"localized_name":"preset","name":"preset","type":"COMBO","widget":{"name":"preset"},"link":null}],"outputs":[{"localized_name":"images","name":"images","type":"IMAGE","links":[58]},{"localized_name":"audio","name":"audio","type":"AUDIO","links":[59]},{"localized_name":"fps","name":"fps","type":"FLOAT","links":[60]},{"localized_name":"video_path","name":"video_path","type":"STRING","links":null}],"properties":{"aux_id":"Verolelb/ComfyUI-VideoCompressor","ver":"30b8454f0f011ff8dd1f6b81fca54b808468487e","Node name for S&R":"VideoCompressor"},"widgets_values":[24,0,23,"libx264","fast"],"shape":2},{"id":33,"type":"Label (rgthree)","pos":[1016.8291955753649,620.8622997655148],"size":[219.45703125,12],"flags":{"allow_interaction":true},"order":1,"mode":0,"inputs":[],"outputs":[],"title":"choose your target size or let 0 by default","properties":{"fontSize":12,"fontFamily":"Arial","fontColor":"#ffffff","textAlign":"left","backgroundColor":"transparent","padding":0,"borderRadius":0,"angle":0},"color":"#fff0","bgcolor":"#fff0"},{"id":16,"type":"CreateVideo","pos":[1481.0190534504716,514.118339998011],"size":[210,78],"flags":{},"order":4,"mode":0,"inputs":[{"localized_name":"images","name":"images","type":"IMAGE","link":58},{"localized_name":"audio","name":"audio","shape":7,"type":"AUDIO","link":59},{"localized_name":"fps","name":"fps","type":"FLOAT","widget":{"name":"fps"},"link":60}],"outputs":[{"localized_name":"VIDEO","name":"VIDEO","type":"VIDEO","links":[15]}],"properties":{"cnr_id":"comfy-core","ver":"0.3.59","Node name for S&R":"CreateVideo"},"widgets_values":[16],"shape":2},{"id":17,"type":"SaveVideo","pos":[1713.7049519429584,515.6718968961251],"size":[704,329.5121951219512],"flags":{},"order":5,"mode":0,"inputs":[{"localized_name":"video","name":"video","type":"VIDEO","link":15},{"localized_name":"filename_prefix","name":"filename_prefix","type":"STRING","widget":{"name":"filename_prefix"},"link":null},{"localized_name":"format","name":"format","type":"COMBO","widget":{"name":"format"},"link":null},{"localized_name":"codec","name":"codec","type":"COMBO","widget":{"name":"codec"},"link":null}],"outputs":[],"properties":{"cnr_id":"comfy-core","ver":"0.3.59","Node name for S&R":"SaveVideo"},"widgets_values":["video/ComfyUI","auto","auto"],"shape":2},{"id":13,"type":"GetVideoComponents","pos":[1025.5227246194358,502.6749169443874],"size":[193.530859375,66],"flags":{"collapsed":false},"order":2,"mode":0,"inputs":[{"localized_name":"video","name":"video","type":"VIDEO","link":11}],"outputs":[{"localized_name":"images","name":"images","type":"IMAGE","links":[55]},{"localized_name":"audio","name":"audio","type":"AUDIO","links":[56]},{"localized_name":"fps","name":"fps","type":"FLOAT","links":[57]}],"properties":{"cnr_id":"comfy-core","ver":"0.3.59","Node name for S&R":"GetVideoComponents"},"widgets_values":[],"shape":2},{"id":12,"type":"LoadVideo","pos":[569.174190262957,501.8096446356975],"size":[378.080078125,186.2731993140244],"flags":{},"order":0,"mode":0,"inputs":[{"localized_name":"file","name":"file","type":"COMBO","widget":{"name":"file"},"link":null},{"localized_name":"choose file to upload","name":"upload","type":"IMAGEUPLOAD","widget":{"name":"upload"},"link":null}],"outputs":[{"localized_name":"VIDEO","name":"VIDEO","type":"VIDEO","links":[11]}],"properties":{"cnr_id":"comfy-core","ver":"0.3.60","Node name for S&R":"LoadVideo"},"widgets_values":["MMaudio_00001-audio.mp4","image"],"shape":2}],"links":[[11,12,0,13,0,"VIDEO"],[15,16,0,17,0,"VIDEO"],[55,13,0,32,0,"IMAGE"],[56,13,1,32,1,"AUDIO"],[57,13,2,32,2,"FLOAT"],[58,32,0,16,0,"IMAGE"],[59,32,1,16,1,"AUDIO"],[60,32,2,16,2,"FLOAT"]],"groups":[],"config":{},"extra":{"ds":{"scale":0.9389783777991051,"offset":[-342.7614907539266,-234.4700157915137]},"workflowRendererVersion":"LG"},"version":0.4}
|
||||
Reference in New Issue
Block a user