Files
PauldeLavallaz-comfyui-ltx-…/__init__.py
T

192 lines
6.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
ComfyUI custom node for LTX-2 Video Generation API (ltx.video)
"""
import os
import requests
import tempfile
import folder_paths
import numpy as np
import torch
LTX_BASE_URL = "https://api.ltx.video/v1"
class LTXTextToVideo:
"""Generate video from text prompt using LTX-2 API"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"api_key": ("STRING", {"default": "", "multiline": False}),
"prompt": ("STRING", {"default": "", "multiline": True}),
"model": (["ltx-2-3-pro", "ltx-2-3-fast"], {"default": "ltx-2-3-pro"}),
"duration": ("INT", {"default": 8, "min": 6, "max": 20}),
"resolution": (["1920x1080", "1080x1920", "1440x1080", "1080x1440", "4096x2160"], {"default": "1920x1080"}),
"fps": (["24", "25", "48", "50"], {"default": "24"}),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("video_path",)
FUNCTION = "generate"
CATEGORY = "LTX Video"
OUTPUT_NODE = True
def generate(self, api_key, prompt, model, duration, resolution, fps):
if not api_key:
raise ValueError("LTX API Key requerida")
payload = {
"prompt": prompt,
"model": model,
"duration": duration,
"resolution": resolution,
}
print(f"[LTX] Generando video | model={model} | {resolution} | {duration}s")
r = requests.post(
f"{LTX_BASE_URL}/text-to-video",
headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
json=payload,
timeout=300,
stream=True
)
if r.status_code != 200:
raise RuntimeError(f"LTX API error {r.status_code}: {r.text[:300]}")
output_dir = folder_paths.get_output_directory()
out_path = os.path.join(output_dir, f"ltx_t2v_{hash(prompt) % 100000}.mp4")
with open(out_path, "wb") as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)
print(f"[LTX] Video guardado: {out_path}")
return (out_path,)
class LTXImageToVideo:
"""Animate a static image using LTX-2 API"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"api_key": ("STRING", {"default": "", "multiline": False}),
"image_url": ("STRING", {"default": "", "multiline": False}),
"prompt": ("STRING", {"default": "", "multiline": True}),
"model": (["ltx-2-3-pro", "ltx-2-3-fast"], {"default": "ltx-2-3-pro"}),
"duration": ("INT", {"default": 8, "min": 6, "max": 20}),
"resolution": (["1920x1080", "1080x1920", "1440x1080", "4096x2160"], {"default": "1920x1080"}),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("video_path",)
FUNCTION = "generate"
CATEGORY = "LTX Video"
OUTPUT_NODE = True
def generate(self, api_key, image_url, prompt, model, duration, resolution):
if not api_key:
raise ValueError("LTX API Key requerida")
payload = {
"image_uri": image_url,
"prompt": prompt,
"model": model,
"duration": duration,
"resolution": resolution,
}
print(f"[LTX] Image-to-Video | model={model} | {resolution} | {duration}s")
r = requests.post(
f"{LTX_BASE_URL}/image-to-video",
headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
json=payload,
timeout=300,
stream=True
)
if r.status_code != 200:
raise RuntimeError(f"LTX API error {r.status_code}: {r.text[:300]}")
output_dir = folder_paths.get_output_directory()
out_path = os.path.join(output_dir, f"ltx_i2v_{hash(image_url + prompt) % 100000}.mp4")
with open(out_path, "wb") as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)
print(f"[LTX] Video guardado: {out_path}")
return (out_path,)
class LTXExtendVideo:
"""Extend an existing video using LTX-2 API"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"api_key": ("STRING", {"default": "", "multiline": False}),
"video_url": ("STRING", {"default": "", "multiline": False}),
"prompt": ("STRING", {"default": "", "multiline": True}),
"model": (["ltx-2-3-pro", "ltx-2-3-fast"], {"default": "ltx-2-3-pro"}),
"duration": ("INT", {"default": 6, "min": 2, "max": 20}),
"direction": (["end", "beginning"], {"default": "end"}),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("video_path",)
FUNCTION = "extend"
CATEGORY = "LTX Video"
OUTPUT_NODE = True
def extend(self, api_key, video_url, prompt, model, duration, direction):
payload = {
"video_uri": video_url,
"prompt": prompt,
"model": model,
"duration": duration,
"direction": direction,
}
r = requests.post(
f"{LTX_BASE_URL}/extend",
headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
json=payload,
timeout=300,
stream=True
)
if r.status_code != 200:
raise RuntimeError(f"LTX API error {r.status_code}: {r.text[:300]}")
output_dir = folder_paths.get_output_directory()
out_path = os.path.join(output_dir, f"ltx_extend_{hash(video_url) % 100000}.mp4")
with open(out_path, "wb") as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)
return (out_path,)
NODE_CLASS_MAPPINGS = {
"LTXTextToVideo": LTXTextToVideo,
"LTXImageToVideo": LTXImageToVideo,
"LTXExtendVideo": LTXExtendVideo,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"LTXTextToVideo": "LTX Text to Video 🎬",
"LTXImageToVideo": "LTX Image to Video 🖼️➡️🎬",
"LTXExtendVideo": "LTX Extend Video ➕",
}
print("[LTX] Nodo LTX-2 Video API cargado ✅")