Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,9 @@ This node pack focuses on **image / video / edit** — see the full **[node cata
| AtlasCloud Grok Imagine Edit | xai/grok-imagine-image/edit |
| AtlasCloud GPT Image-2 Edit | openai/gpt-image-2/edit |
| AtlasCloud GPT Image-2 Developer Edit | openai/gpt-image-2-developer/edit |
| AtlasCloud LTX 2.3 Quality Text-to-Video | ltx-2.3-quality/text-to-video |
| AtlasCloud LTX 2.3 Quality Image-to-Video | ltx-2.3-quality/image-to-video |
| AtlasCloud LTX 2.3 Quality Extend Video | ltx-2.3-quality/extend-video |

> Nodes are continuously expanded as new models are added to AtlasCloud.

Expand Down
116 changes: 116 additions & 0 deletions src/atlascloud_comfyui/nodes/video/ltx_2_3_quality_extend_video.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
from __future__ import annotations

from typing import Any, Dict, Tuple

from ..auth.atlas_client_node import AtlasClientHandle

_RESOLUTIONS = [
"square_hd",
"square",
"portrait_3_4",
"portrait_9_16",
"landscape_4_3",
"landscape_16_9",
]

_DEFAULT_PROMPT = "Continue the scene naturally, maintaining the same style, lighting, and motion dynamics"


class AtlasLtx23QualityExtendVideo:
"""
ComfyUI Node: AtlasCloud LTX 2.3 Quality Extend Video (remote API)
Outputs: video_url, prediction_id
"""

CATEGORY = "AtlasCloud/Video"
FUNCTION = "run"
RETURN_TYPES = ("STRING", "STRING")
RETURN_NAMES = ("video_url", "prediction_id")

@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"atlas_client": ("ATLAS_CLIENT",),
"video_url": ("STRING", {"default": "", "tooltip": "Input video URL or data:video/...;base64,..."}),
"prompt": ("STRING", {"multiline": True, "default": _DEFAULT_PROMPT, "tooltip": "Extended scene description (max 2000 chars)"}),
},
"optional": {
"extend_direction": (
["forward", "backward"],
{"default": "forward", "tooltip": "Forward (append after) or Backward (prepend before)"},
),
"num_frames": (
"INT",
{"default": 81, "min": 9, "max": 481, "tooltip": "Extension frames; multiple of 8 plus 1"},
),
"resolution": (_RESOLUTIONS, {"default": "landscape_16_9", "tooltip": "Output resolution preset"}),
"match_input_fps": ("BOOLEAN", {"default": True, "tooltip": "Match the input video FPS"}),
"generate_audio": ("BOOLEAN", {"default": False, "tooltip": "Generate accompanying audio"}),
"randomize_seed": ("BOOLEAN", {"default": True, "tooltip": "开启后每次随机;关闭后使用下方固定 seed"}),
"seed": ("INT", {"default": 0, "min": 0, "max": 2**31 - 1, "tooltip": "固定 seed(仅在随机开关关闭时生效)"}),
"poll_interval_sec": ("FLOAT", {"default": 2.0, "min": 0.5, "max": 10.0, "tooltip": "Polling interval (seconds)"}),
"timeout_sec": ("INT", {"default": 900, "min": 30, "max": 7200, "tooltip": "Timeout (seconds)"}),
},
}

def run(
self,
atlas_client: AtlasClientHandle,
video_url: str,
prompt: str,
extend_direction: str = "forward",
num_frames: int = 81,
resolution: str = "landscape_16_9",
match_input_fps: bool = True,
generate_audio: bool = False,
randomize_seed: bool = True,
seed: int = 0,
poll_interval_sec: float = 2.0,
timeout_sec: int = 900,
) -> Tuple[str, str]:
video_url = (video_url or "").strip()
if not video_url:
raise RuntimeError("video_url is required for LTX 2.3 Quality Extend Video")

p = (prompt or "").strip()
if not p:
raise RuntimeError("prompt is required for LTX 2.3 Quality Extend Video")

client = atlas_client.client

payload: Dict[str, Any] = {
"model": "ltx-2.3-quality/extend-video",
"prompt": p,
"video_url": video_url,
"extend_direction": extend_direction,
"num_frames": int(num_frames),
"resolution": resolution,
"match_input_fps": bool(match_input_fps),
"generate_audio": bool(generate_audio),
}
if not randomize_seed:
payload["seed"] = int(seed)

prediction_id = client.generate_video(payload)
result = client.poll_prediction(
prediction_id,
poll_interval_sec=float(poll_interval_sec),
timeout_sec=float(timeout_sec),
)

outputs = (result.get("data") or {}).get("outputs") or []
if not outputs:
raise RuntimeError(f"No outputs returned for prediction {prediction_id}: {result}")

first = outputs[0]
if isinstance(first, dict):
url = first.get("url") or first.get("video") or first.get("output")
if isinstance(url, str) and url.strip():
return (url, prediction_id)
raise RuntimeError(f"Unexpected output object for prediction {prediction_id}: {first}")

if not isinstance(first, str):
raise RuntimeError(f"Unexpected output type for prediction {prediction_id}: {type(first).__name__} {first!r}")

return (first, prediction_id)
105 changes: 105 additions & 0 deletions src/atlascloud_comfyui/nodes/video/ltx_2_3_quality_i2v.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
from __future__ import annotations

from typing import Any, Dict, Tuple

from ..auth.atlas_client_node import AtlasClientHandle

_RESOLUTIONS = [
"square_hd",
"square",
"portrait_3_4",
"portrait_9_16",
"landscape_4_3",
"landscape_16_9",
]


class AtlasLtx23QualityImageToVideo:
"""
ComfyUI Node: AtlasCloud LTX 2.3 Quality Image-to-Video (remote API)
Outputs: video_url, prediction_id
"""

CATEGORY = "AtlasCloud/Video"
FUNCTION = "run"
RETURN_TYPES = ("STRING", "STRING")
RETURN_NAMES = ("video_url", "prediction_id")

@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"atlas_client": ("ATLAS_CLIENT",),
"prompt": ("STRING", {"multiline": True, "tooltip": "Motion/style prompt (max 2000 chars)"}),
"image_url": ("STRING", {"default": "", "tooltip": "Input image URL or data:image/...;base64,..."}),
},
"optional": {
"num_frames": (
"INT",
{"default": 121, "min": 25, "max": 257, "tooltip": "Frames; multiple of 8 plus 1"},
),
"resolution": (_RESOLUTIONS, {"default": "landscape_16_9", "tooltip": "Output resolution preset"}),
"generate_audio": ("BOOLEAN", {"default": False, "tooltip": "Generate accompanying audio"}),
"randomize_seed": ("BOOLEAN", {"default": True, "tooltip": "开启后每次随机;关闭后使用下方固定 seed"}),
"seed": ("INT", {"default": 0, "min": 0, "max": 2**31 - 1, "tooltip": "固定 seed(仅在随机开关关闭时生效)"}),
"poll_interval_sec": ("FLOAT", {"default": 2.0, "min": 0.5, "max": 10.0, "tooltip": "Polling interval (seconds)"}),
"timeout_sec": ("INT", {"default": 900, "min": 30, "max": 7200, "tooltip": "Timeout (seconds)"}),
},
}

def run(
self,
atlas_client: AtlasClientHandle,
prompt: str,
image_url: str,
num_frames: int = 121,
resolution: str = "landscape_16_9",
generate_audio: bool = False,
randomize_seed: bool = True,
seed: int = 0,
poll_interval_sec: float = 2.0,
timeout_sec: int = 900,
) -> Tuple[str, str]:
image_url = (image_url or "").strip()
if not image_url:
raise RuntimeError("image_url is required for LTX 2.3 Quality Image-to-Video")

p = (prompt or "").strip()
if not p:
raise RuntimeError("prompt is required for LTX 2.3 Quality Image-to-Video")

client = atlas_client.client

payload: Dict[str, Any] = {
"model": "ltx-2.3-quality/image-to-video",
"prompt": p,
"image_url": image_url,
"num_frames": int(num_frames),
"resolution": resolution,
"generate_audio": bool(generate_audio),
}
if not randomize_seed:
payload["seed"] = int(seed)

prediction_id = client.generate_video(payload)
result = client.poll_prediction(
prediction_id,
poll_interval_sec=float(poll_interval_sec),
timeout_sec=float(timeout_sec),
)

outputs = (result.get("data") or {}).get("outputs") or []
if not outputs:
raise RuntimeError(f"No outputs returned for prediction {prediction_id}: {result}")

first = outputs[0]
if isinstance(first, dict):
url = first.get("url") or first.get("video") or first.get("output")
if isinstance(url, str) and url.strip():
return (url, prediction_id)
raise RuntimeError(f"Unexpected output object for prediction {prediction_id}: {first}")

if not isinstance(first, str):
raise RuntimeError(f"Unexpected output type for prediction {prediction_id}: {type(first).__name__} {first!r}")

return (first, prediction_id)
98 changes: 98 additions & 0 deletions src/atlascloud_comfyui/nodes/video/ltx_2_3_quality_t2v.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
from __future__ import annotations

from typing import Any, Dict, Tuple

from ..auth.atlas_client_node import AtlasClientHandle

_RESOLUTIONS = [
"square_hd",
"square",
"portrait_3_4",
"portrait_9_16",
"landscape_4_3",
"landscape_16_9",
]


class AtlasLtx23QualityTextToVideo:
"""
ComfyUI Node: AtlasCloud LTX 2.3 Quality Text-to-Video (remote API)
Outputs: video_url, prediction_id
"""

CATEGORY = "AtlasCloud/Video"
FUNCTION = "run"
RETURN_TYPES = ("STRING", "STRING")
RETURN_NAMES = ("video_url", "prediction_id")

@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"atlas_client": ("ATLAS_CLIENT",),
"prompt": ("STRING", {"multiline": True, "tooltip": "Text prompt (max 2000 chars)"}),
},
"optional": {
"num_frames": (
"INT",
{"default": 121, "min": 25, "max": 257, "tooltip": "Frames; multiple of 8 plus 1"},
),
"resolution": (_RESOLUTIONS, {"default": "landscape_16_9", "tooltip": "Output resolution preset"}),
"generate_audio": ("BOOLEAN", {"default": False, "tooltip": "Generate accompanying audio"}),
"randomize_seed": ("BOOLEAN", {"default": True, "tooltip": "开启后每次随机;关闭后使用下方固定 seed"}),
"seed": ("INT", {"default": 0, "min": 0, "max": 2**31 - 1, "tooltip": "固定 seed(仅在随机开关关闭时生效)"}),
"poll_interval_sec": ("FLOAT", {"default": 2.0, "min": 0.5, "max": 10.0, "tooltip": "Polling interval (seconds)"}),
"timeout_sec": ("INT", {"default": 900, "min": 30, "max": 7200, "tooltip": "Timeout (seconds)"}),
},
}

def run(
self,
atlas_client: AtlasClientHandle,
prompt: str,
num_frames: int = 121,
resolution: str = "landscape_16_9",
generate_audio: bool = False,
randomize_seed: bool = True,
seed: int = 0,
poll_interval_sec: float = 2.0,
timeout_sec: int = 900,
) -> Tuple[str, str]:
p = (prompt or "").strip()
if not p:
raise RuntimeError("prompt is required for LTX 2.3 Quality Text-to-Video")

client = atlas_client.client

payload: Dict[str, Any] = {
"model": "ltx-2.3-quality/text-to-video",
"prompt": p,
"num_frames": int(num_frames),
"resolution": resolution,
"generate_audio": bool(generate_audio),
}
if not randomize_seed:
payload["seed"] = int(seed)

prediction_id = client.generate_video(payload)
result = client.poll_prediction(
prediction_id,
poll_interval_sec=float(poll_interval_sec),
timeout_sec=float(timeout_sec),
)

outputs = (result.get("data") or {}).get("outputs") or []
if not outputs:
raise RuntimeError(f"No outputs returned for prediction {prediction_id}: {result}")

first = outputs[0]
if isinstance(first, dict):
url = first.get("url") or first.get("video") or first.get("output")
if isinstance(url, str) and url.strip():
return (url, prediction_id)
raise RuntimeError(f"Unexpected output object for prediction {prediction_id}: {first}")

if not isinstance(first, str):
raise RuntimeError(f"Unexpected output type for prediction {prediction_id}: {type(first).__name__} {first!r}")

return (first, prediction_id)
10 changes: 10 additions & 0 deletions src/atlascloud_comfyui/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,10 @@
from atlascloud_comfyui.nodes.image.ideogram_v4_turbo_t2i import AtlasIdeogramV4TurboTextToImage
from atlascloud_comfyui.nodes.image.ideogram_v4_quality_t2i import AtlasIdeogramV4QualityTextToImage

from atlascloud_comfyui.nodes.video.ltx_2_3_quality_t2v import AtlasLtx23QualityTextToVideo
from atlascloud_comfyui.nodes.video.ltx_2_3_quality_i2v import AtlasLtx23QualityImageToVideo
from atlascloud_comfyui.nodes.video.ltx_2_3_quality_extend_video import AtlasLtx23QualityExtendVideo

from atlascloud_comfyui.nodes.video.kling_v16_multi_i2v_pro import AtlasKlingV16MultiI2VPro
from atlascloud_comfyui.nodes.video.kling_v16_multi_i2v_standard import AtlasKlingV16MultiI2VStandard
from atlascloud_comfyui.nodes.video.kling_v16_i2v_standard import AtlasKlingV16I2VStandard
Expand Down Expand Up @@ -723,6 +727,9 @@
"AtlasCloud Cosmos 3 Super Image-to-Video": AtlasCosmos3SuperImageToVideo,
"AtlasCloud Ideogram V4 Turbo Text-to-Image": AtlasIdeogramV4TurboTextToImage,
"AtlasCloud Ideogram V4 Quality Text-to-Image": AtlasIdeogramV4QualityTextToImage,
"AtlasCloud LTX 2.3 Quality Text-to-Video": AtlasLtx23QualityTextToVideo,
"AtlasCloud LTX 2.3 Quality Image-to-Video": AtlasLtx23QualityImageToVideo,
"AtlasCloud LTX 2.3 Quality Extend Video": AtlasLtx23QualityExtendVideo,
}


Expand Down Expand Up @@ -1063,6 +1070,9 @@
"AtlasCloud Cosmos 3 Super Image-to-Video": "AtlasCloud Cosmos 3 Super Image-to-Video",
"AtlasCloud Ideogram V4 Turbo Text-to-Image": "AtlasCloud Ideogram V4 Turbo Text-to-Image",
"AtlasCloud Ideogram V4 Quality Text-to-Image": "AtlasCloud Ideogram V4 Quality Text-to-Image",
"AtlasCloud LTX 2.3 Quality Text-to-Video": "AtlasCloud LTX 2.3 Quality Text-to-Video",
"AtlasCloud LTX 2.3 Quality Image-to-Video": "AtlasCloud LTX 2.3 Quality Image-to-Video",
"AtlasCloud LTX 2.3 Quality Extend Video": "AtlasCloud LTX 2.3 Quality Extend Video",
}


Expand Down
Loading
Loading