mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-05-06 15:22:31 +08:00
[Partner Nodes] Topaz Astra 2 model (#13672)
* feat(api-nodes): add Topaz Astra 2 model Signed-off-by: bigcat88 <bigcat88@icloud.com> * feat(api-nodes): make Astra 2 the default Topaz upscaler model Reorder UPSCALER_MODELS_MAP and the upscaler_model dynamic combo so "Astra 2" appears first, surfacing it as the default selection. --------- Signed-off-by: bigcat88 <bigcat88@icloud.com> Co-authored-by: Marwan Mostafa <marawan206@gmail.com>
This commit is contained in:
parent
0aaa202a1d
commit
5a252d437e
@ -1,4 +1,4 @@
|
|||||||
from typing import Optional, Union
|
from typing import Optional
|
||||||
|
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
@ -72,8 +72,11 @@ class VideoEnhancementFilter(BaseModel):
|
|||||||
grain: Optional[float] = Field(None, description="Grain after AI model processing")
|
grain: Optional[float] = Field(None, description="Grain after AI model processing")
|
||||||
grainSize: Optional[float] = Field(None, description="Size of generated grain")
|
grainSize: Optional[float] = Field(None, description="Size of generated grain")
|
||||||
recoverOriginalDetailValue: Optional[float] = Field(None, description="Source details into the output video")
|
recoverOriginalDetailValue: Optional[float] = Field(None, description="Source details into the output video")
|
||||||
creativity: Optional[str] = Field(None, description="Creativity level(high, low) for slc-1 only")
|
creativity: float | str | None = Field(None, description="slc-1/slp-2.5: enum (low/middle/high). ast-2: decimal 0.0-1.0.")
|
||||||
isOptimizedMode: Optional[bool] = Field(None, description="Set to true for Starlight Creative (slc-1) only")
|
isOptimizedMode: Optional[bool] = Field(None, description="Set to true for Starlight Creative (slc-1) only")
|
||||||
|
prompt: str | None = Field(None, description="Descriptive scene prompt (ast-2 only)")
|
||||||
|
sharp: float | None = Field(None, description="ast-2 pre-enhance sharpness")
|
||||||
|
realism: float | None = Field(None, description="ast-2 realism control")
|
||||||
|
|
||||||
|
|
||||||
class OutputInformationVideo(BaseModel):
|
class OutputInformationVideo(BaseModel):
|
||||||
@ -90,7 +93,7 @@ class Overrides(BaseModel):
|
|||||||
|
|
||||||
class CreateVideoRequest(BaseModel):
|
class CreateVideoRequest(BaseModel):
|
||||||
source: CreateVideoRequestSource = Field(...)
|
source: CreateVideoRequestSource = Field(...)
|
||||||
filters: list[Union[VideoFrameInterpolationFilter, VideoEnhancementFilter]] = Field(...)
|
filters: list[VideoFrameInterpolationFilter | VideoEnhancementFilter] = Field(...)
|
||||||
output: OutputInformationVideo = Field(...)
|
output: OutputInformationVideo = Field(...)
|
||||||
overrides: Overrides = Field(Overrides(isPaidDiffusion=True))
|
overrides: Overrides = Field(Overrides(isPaidDiffusion=True))
|
||||||
|
|
||||||
|
|||||||
@ -36,11 +36,15 @@ from comfy_api_nodes.util import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
UPSCALER_MODELS_MAP = {
|
UPSCALER_MODELS_MAP = {
|
||||||
|
"Astra 2": "ast-2",
|
||||||
"Starlight (Astra) Fast": "slf-1",
|
"Starlight (Astra) Fast": "slf-1",
|
||||||
"Starlight (Astra) Creative": "slc-1",
|
"Starlight (Astra) Creative": "slc-1",
|
||||||
"Starlight Precise 2.5": "slp-2.5",
|
"Starlight Precise 2.5": "slp-2.5",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AST2_MAX_FRAMES = 9000
|
||||||
|
AST2_MAX_FRAMES_WITH_PROMPT = 450
|
||||||
|
|
||||||
|
|
||||||
class TopazImageEnhance(IO.ComfyNode):
|
class TopazImageEnhance(IO.ComfyNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -230,13 +234,20 @@ class TopazVideoEnhance(IO.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return IO.Schema(
|
return IO.Schema(
|
||||||
node_id="TopazVideoEnhance",
|
node_id="TopazVideoEnhance",
|
||||||
display_name="Topaz Video Enhance",
|
display_name="Topaz Video Enhance (Legacy)",
|
||||||
category="api node/video/Topaz",
|
category="api node/video/Topaz",
|
||||||
description="Breathe new life into video with powerful upscaling and recovery technology.",
|
description="Breathe new life into video with powerful upscaling and recovery technology.",
|
||||||
inputs=[
|
inputs=[
|
||||||
IO.Video.Input("video"),
|
IO.Video.Input("video"),
|
||||||
IO.Boolean.Input("upscaler_enabled", default=True),
|
IO.Boolean.Input("upscaler_enabled", default=True),
|
||||||
IO.Combo.Input("upscaler_model", options=list(UPSCALER_MODELS_MAP.keys())),
|
IO.Combo.Input(
|
||||||
|
"upscaler_model",
|
||||||
|
options=[
|
||||||
|
"Starlight (Astra) Fast",
|
||||||
|
"Starlight (Astra) Creative",
|
||||||
|
"Starlight Precise 2.5",
|
||||||
|
],
|
||||||
|
),
|
||||||
IO.Combo.Input("upscaler_resolution", options=["FullHD (1080p)", "4K (2160p)"]),
|
IO.Combo.Input("upscaler_resolution", options=["FullHD (1080p)", "4K (2160p)"]),
|
||||||
IO.Combo.Input(
|
IO.Combo.Input(
|
||||||
"upscaler_creativity",
|
"upscaler_creativity",
|
||||||
@ -304,6 +315,7 @@ class TopazVideoEnhance(IO.ComfyNode):
|
|||||||
IO.Hidden.unique_id,
|
IO.Hidden.unique_id,
|
||||||
],
|
],
|
||||||
is_api_node=True,
|
is_api_node=True,
|
||||||
|
is_deprecated=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -457,12 +469,357 @@ class TopazVideoEnhance(IO.ComfyNode):
|
|||||||
return IO.NodeOutput(await download_url_to_video_output(final_response.download.url))
|
return IO.NodeOutput(await download_url_to_video_output(final_response.download.url))
|
||||||
|
|
||||||
|
|
||||||
|
class TopazVideoEnhanceV2(IO.ComfyNode):
|
||||||
|
@classmethod
|
||||||
|
def define_schema(cls):
|
||||||
|
return IO.Schema(
|
||||||
|
node_id="TopazVideoEnhanceV2",
|
||||||
|
display_name="Topaz Video Enhance",
|
||||||
|
category="api node/video/Topaz",
|
||||||
|
description="Breathe new life into video with powerful upscaling and recovery technology.",
|
||||||
|
inputs=[
|
||||||
|
IO.Video.Input("video"),
|
||||||
|
IO.DynamicCombo.Input(
|
||||||
|
"upscaler_model",
|
||||||
|
options=[
|
||||||
|
IO.DynamicCombo.Option(
|
||||||
|
"Astra 2",
|
||||||
|
[
|
||||||
|
IO.Combo.Input("upscaler_resolution", options=["FullHD (1080p)", "4K (2160p)"]),
|
||||||
|
IO.Float.Input(
|
||||||
|
"creativity",
|
||||||
|
default=0.5,
|
||||||
|
min=0.0,
|
||||||
|
max=1.0,
|
||||||
|
step=0.1,
|
||||||
|
display_mode=IO.NumberDisplay.slider,
|
||||||
|
tooltip="Creative strength of the upscale.",
|
||||||
|
),
|
||||||
|
IO.String.Input(
|
||||||
|
"prompt",
|
||||||
|
multiline=True,
|
||||||
|
default="",
|
||||||
|
tooltip="Optional descriptive (not instructive) scene prompt."
|
||||||
|
f"Capping input at {AST2_MAX_FRAMES_WITH_PROMPT} frames (~15s @ 30fps) when set.",
|
||||||
|
),
|
||||||
|
IO.Float.Input(
|
||||||
|
"sharp",
|
||||||
|
default=0.5,
|
||||||
|
min=0.0,
|
||||||
|
max=1.0,
|
||||||
|
step=0.01,
|
||||||
|
display_mode=IO.NumberDisplay.slider,
|
||||||
|
tooltip="Pre-enhance sharpness: "
|
||||||
|
"0.0=Gaussian blur, 0.5=passthrough (default), 1.0=USM sharpening.",
|
||||||
|
advanced=True,
|
||||||
|
),
|
||||||
|
IO.Float.Input(
|
||||||
|
"realism",
|
||||||
|
default=0.0,
|
||||||
|
min=0.0,
|
||||||
|
max=1.0,
|
||||||
|
step=0.01,
|
||||||
|
display_mode=IO.NumberDisplay.slider,
|
||||||
|
tooltip="Pulls output toward photographic realism."
|
||||||
|
"Leave at 0 for the model default.",
|
||||||
|
advanced=True,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
IO.DynamicCombo.Option(
|
||||||
|
"Starlight (Astra) Fast",
|
||||||
|
[IO.Combo.Input("upscaler_resolution", options=["FullHD (1080p)", "4K (2160p)"]),],
|
||||||
|
),
|
||||||
|
IO.DynamicCombo.Option(
|
||||||
|
"Starlight (Astra) Creative",
|
||||||
|
[
|
||||||
|
IO.Combo.Input("upscaler_resolution", options=["FullHD (1080p)", "4K (2160p)"]),
|
||||||
|
IO.Combo.Input(
|
||||||
|
"creativity",
|
||||||
|
options=["low", "middle", "high"],
|
||||||
|
default="low",
|
||||||
|
tooltip="Creative strength of the upscale.",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
IO.DynamicCombo.Option(
|
||||||
|
"Starlight Precise 2.5",
|
||||||
|
[IO.Combo.Input("upscaler_resolution", options=["FullHD (1080p)", "4K (2160p)"])],
|
||||||
|
),
|
||||||
|
IO.DynamicCombo.Option("Disabled", []),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
IO.DynamicCombo.Input(
|
||||||
|
"interpolation_model",
|
||||||
|
options=[
|
||||||
|
IO.DynamicCombo.Option("Disabled", []),
|
||||||
|
IO.DynamicCombo.Option(
|
||||||
|
"apo-8",
|
||||||
|
[
|
||||||
|
IO.Int.Input(
|
||||||
|
"interpolation_frame_rate",
|
||||||
|
default=60,
|
||||||
|
min=15,
|
||||||
|
max=240,
|
||||||
|
display_mode=IO.NumberDisplay.number,
|
||||||
|
tooltip="Output frame rate.",
|
||||||
|
),
|
||||||
|
IO.Int.Input(
|
||||||
|
"interpolation_slowmo",
|
||||||
|
default=1,
|
||||||
|
min=1,
|
||||||
|
max=16,
|
||||||
|
display_mode=IO.NumberDisplay.number,
|
||||||
|
tooltip="Slow-motion factor applied to the input video. "
|
||||||
|
"For example, 2 makes the output twice as slow and doubles the duration.",
|
||||||
|
advanced=True,
|
||||||
|
),
|
||||||
|
IO.Boolean.Input(
|
||||||
|
"interpolation_duplicate",
|
||||||
|
default=False,
|
||||||
|
tooltip="Analyze the input for duplicate frames and remove them.",
|
||||||
|
advanced=True,
|
||||||
|
),
|
||||||
|
IO.Float.Input(
|
||||||
|
"interpolation_duplicate_threshold",
|
||||||
|
default=0.01,
|
||||||
|
min=0.001,
|
||||||
|
max=0.1,
|
||||||
|
step=0.001,
|
||||||
|
display_mode=IO.NumberDisplay.number,
|
||||||
|
tooltip="Detection sensitivity for duplicate frames.",
|
||||||
|
advanced=True,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
IO.Combo.Input(
|
||||||
|
"dynamic_compression_level",
|
||||||
|
options=["Low", "Mid", "High"],
|
||||||
|
default="Low",
|
||||||
|
tooltip="CQP level.",
|
||||||
|
optional=True,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
outputs=[
|
||||||
|
IO.Video.Output(),
|
||||||
|
],
|
||||||
|
hidden=[
|
||||||
|
IO.Hidden.auth_token_comfy_org,
|
||||||
|
IO.Hidden.api_key_comfy_org,
|
||||||
|
IO.Hidden.unique_id,
|
||||||
|
],
|
||||||
|
is_api_node=True,
|
||||||
|
price_badge=IO.PriceBadge(
|
||||||
|
depends_on=IO.PriceBadgeDepends(widgets=[
|
||||||
|
"upscaler_model",
|
||||||
|
"upscaler_model.upscaler_resolution",
|
||||||
|
"interpolation_model",
|
||||||
|
]),
|
||||||
|
expr="""
|
||||||
|
(
|
||||||
|
$model := $lookup(widgets, "upscaler_model");
|
||||||
|
$res := $lookup(widgets, "upscaler_model.upscaler_resolution");
|
||||||
|
$interp := $lookup(widgets, "interpolation_model");
|
||||||
|
$is4k := $contains($res, "4k");
|
||||||
|
$hasInterp := $interp != "disabled";
|
||||||
|
$rates := {
|
||||||
|
"starlight (astra) fast": {"hd": 0.43, "uhd": 0.85},
|
||||||
|
"starlight precise 2.5": {"hd": 0.70, "uhd": 1.54},
|
||||||
|
"astra 2": {"hd": 1.72, "uhd": 2.85},
|
||||||
|
"starlight (astra) creative": {"hd": 2.25, "uhd": 3.99}
|
||||||
|
};
|
||||||
|
$surcharge := $is4k ? 0.28 : 0.14;
|
||||||
|
$entry := $lookup($rates, $model);
|
||||||
|
$base := $is4k ? $entry.uhd : $entry.hd;
|
||||||
|
$hi := $base + ($hasInterp ? $surcharge : 0);
|
||||||
|
$model = "disabled"
|
||||||
|
? {"type":"text","text":"Interpolation only"}
|
||||||
|
: ($hasInterp
|
||||||
|
? {"type":"text","text":"~" & $string($base) & "–" & $string($hi) & " credits/src frame"}
|
||||||
|
: {"type":"text","text":"~" & $string($base) & " credits/src frame"})
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def execute(
|
||||||
|
cls,
|
||||||
|
video: Input.Video,
|
||||||
|
upscaler_model: dict,
|
||||||
|
interpolation_model: dict,
|
||||||
|
dynamic_compression_level: str = "Low",
|
||||||
|
) -> IO.NodeOutput:
|
||||||
|
upscaler_choice = upscaler_model["upscaler_model"]
|
||||||
|
interpolation_choice = interpolation_model["interpolation_model"]
|
||||||
|
if upscaler_choice == "Disabled" and interpolation_choice == "Disabled":
|
||||||
|
raise ValueError("There is nothing to do: both upscaling and interpolation are disabled.")
|
||||||
|
validate_container_format_is_mp4(video)
|
||||||
|
src_width, src_height = video.get_dimensions()
|
||||||
|
src_frame_rate = int(video.get_frame_rate())
|
||||||
|
duration_sec = video.get_duration()
|
||||||
|
src_video_stream = video.get_stream_source()
|
||||||
|
target_width = src_width
|
||||||
|
target_height = src_height
|
||||||
|
target_frame_rate = src_frame_rate
|
||||||
|
filters = []
|
||||||
|
if upscaler_choice != "Disabled":
|
||||||
|
if "1080p" in upscaler_model["upscaler_resolution"]:
|
||||||
|
target_pixel_p = 1080
|
||||||
|
max_long_side = 1920
|
||||||
|
else:
|
||||||
|
target_pixel_p = 2160
|
||||||
|
max_long_side = 3840
|
||||||
|
ar = src_width / src_height
|
||||||
|
if src_width >= src_height:
|
||||||
|
# Landscape or Square; Attempt to set height to target (e.g., 2160), calculate width
|
||||||
|
target_height = target_pixel_p
|
||||||
|
target_width = int(target_height * ar)
|
||||||
|
# Check if width exceeds standard bounds (for ultra-wide e.g., 21:9 ARs)
|
||||||
|
if target_width > max_long_side:
|
||||||
|
target_width = max_long_side
|
||||||
|
target_height = int(target_width / ar)
|
||||||
|
else:
|
||||||
|
# Portrait; Attempt to set width to target (e.g., 2160), calculate height
|
||||||
|
target_width = target_pixel_p
|
||||||
|
target_height = int(target_width / ar)
|
||||||
|
# Check if height exceeds standard bounds
|
||||||
|
if target_height > max_long_side:
|
||||||
|
target_height = max_long_side
|
||||||
|
target_width = int(target_height * ar)
|
||||||
|
if target_width % 2 != 0:
|
||||||
|
target_width += 1
|
||||||
|
if target_height % 2 != 0:
|
||||||
|
target_height += 1
|
||||||
|
model_id = UPSCALER_MODELS_MAP[upscaler_choice]
|
||||||
|
if model_id == "slc-1":
|
||||||
|
filters.append(
|
||||||
|
VideoEnhancementFilter(
|
||||||
|
model=model_id,
|
||||||
|
creativity=upscaler_model["creativity"],
|
||||||
|
isOptimizedMode=True,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif model_id == "ast-2":
|
||||||
|
n_frames = video.get_frame_count()
|
||||||
|
ast2_prompt = (upscaler_model["prompt"] or "").strip()
|
||||||
|
if ast2_prompt and n_frames > AST2_MAX_FRAMES_WITH_PROMPT:
|
||||||
|
raise ValueError(
|
||||||
|
f"Astra 2 with a prompt is limited to {AST2_MAX_FRAMES_WITH_PROMPT} input frames "
|
||||||
|
f"(~15s @ 30fps); video has {n_frames}. Clear the prompt or shorten the clip."
|
||||||
|
)
|
||||||
|
if n_frames > AST2_MAX_FRAMES:
|
||||||
|
raise ValueError(f"Astra 2 is limited to {AST2_MAX_FRAMES} input frames; video has {n_frames}.")
|
||||||
|
realism = upscaler_model["realism"]
|
||||||
|
filters.append(
|
||||||
|
VideoEnhancementFilter(
|
||||||
|
model=model_id,
|
||||||
|
creativity=upscaler_model["creativity"],
|
||||||
|
prompt=(ast2_prompt or None),
|
||||||
|
sharp=upscaler_model["sharp"],
|
||||||
|
realism=(realism if realism > 0 else None),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
filters.append(VideoEnhancementFilter(model=model_id))
|
||||||
|
if interpolation_choice != "Disabled":
|
||||||
|
target_frame_rate = interpolation_model["interpolation_frame_rate"]
|
||||||
|
filters.append(
|
||||||
|
VideoFrameInterpolationFilter(
|
||||||
|
model=interpolation_choice,
|
||||||
|
slowmo=interpolation_model["interpolation_slowmo"],
|
||||||
|
fps=interpolation_model["interpolation_frame_rate"],
|
||||||
|
duplicate=interpolation_model["interpolation_duplicate"],
|
||||||
|
duplicate_threshold=interpolation_model["interpolation_duplicate_threshold"],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
initial_res = await sync_op(
|
||||||
|
cls,
|
||||||
|
ApiEndpoint(path="/proxy/topaz/video/", method="POST"),
|
||||||
|
response_model=CreateVideoResponse,
|
||||||
|
data=CreateVideoRequest(
|
||||||
|
source=CreateVideoRequestSource(
|
||||||
|
container="mp4",
|
||||||
|
size=get_fs_object_size(src_video_stream),
|
||||||
|
duration=int(duration_sec),
|
||||||
|
frameCount=video.get_frame_count(),
|
||||||
|
frameRate=src_frame_rate,
|
||||||
|
resolution=Resolution(width=src_width, height=src_height),
|
||||||
|
),
|
||||||
|
filters=filters,
|
||||||
|
output=OutputInformationVideo(
|
||||||
|
resolution=Resolution(width=target_width, height=target_height),
|
||||||
|
frameRate=target_frame_rate,
|
||||||
|
audioCodec="AAC",
|
||||||
|
audioTransfer="Copy",
|
||||||
|
dynamicCompressionLevel=dynamic_compression_level,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
wait_label="Creating task",
|
||||||
|
final_label_on_success="Task created",
|
||||||
|
)
|
||||||
|
upload_res = await sync_op(
|
||||||
|
cls,
|
||||||
|
ApiEndpoint(
|
||||||
|
path=f"/proxy/topaz/video/{initial_res.requestId}/accept",
|
||||||
|
method="PATCH",
|
||||||
|
),
|
||||||
|
response_model=VideoAcceptResponse,
|
||||||
|
wait_label="Preparing upload",
|
||||||
|
final_label_on_success="Upload started",
|
||||||
|
)
|
||||||
|
if len(upload_res.urls) > 1:
|
||||||
|
raise NotImplementedError(
|
||||||
|
"Large files are not currently supported. Please open an issue in the ComfyUI repository."
|
||||||
|
)
|
||||||
|
async with aiohttp.ClientSession(headers={"Content-Type": "video/mp4"}) as session:
|
||||||
|
if isinstance(src_video_stream, BytesIO):
|
||||||
|
src_video_stream.seek(0)
|
||||||
|
async with session.put(upload_res.urls[0], data=src_video_stream, raise_for_status=True) as res:
|
||||||
|
upload_etag = res.headers["Etag"]
|
||||||
|
else:
|
||||||
|
with builtins.open(src_video_stream, "rb") as video_file:
|
||||||
|
async with session.put(upload_res.urls[0], data=video_file, raise_for_status=True) as res:
|
||||||
|
upload_etag = res.headers["Etag"]
|
||||||
|
await sync_op(
|
||||||
|
cls,
|
||||||
|
ApiEndpoint(
|
||||||
|
path=f"/proxy/topaz/video/{initial_res.requestId}/complete-upload",
|
||||||
|
method="PATCH",
|
||||||
|
),
|
||||||
|
response_model=VideoCompleteUploadResponse,
|
||||||
|
data=VideoCompleteUploadRequest(
|
||||||
|
uploadResults=[
|
||||||
|
VideoCompleteUploadRequestPart(
|
||||||
|
partNum=1,
|
||||||
|
eTag=upload_etag,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
wait_label="Finalizing upload",
|
||||||
|
final_label_on_success="Upload completed",
|
||||||
|
)
|
||||||
|
final_response = await poll_op(
|
||||||
|
cls,
|
||||||
|
ApiEndpoint(path=f"/proxy/topaz/video/{initial_res.requestId}/status"),
|
||||||
|
response_model=VideoStatusResponse,
|
||||||
|
status_extractor=lambda x: x.status,
|
||||||
|
progress_extractor=lambda x: getattr(x, "progress", 0),
|
||||||
|
price_extractor=lambda x: (x.estimates.cost[0] * 0.08 if x.estimates and x.estimates.cost[0] else None),
|
||||||
|
poll_interval=10.0,
|
||||||
|
)
|
||||||
|
return IO.NodeOutput(await download_url_to_video_output(final_response.download.url))
|
||||||
|
|
||||||
|
|
||||||
class TopazExtension(ComfyExtension):
|
class TopazExtension(ComfyExtension):
|
||||||
@override
|
@override
|
||||||
async def get_node_list(self) -> list[type[IO.ComfyNode]]:
|
async def get_node_list(self) -> list[type[IO.ComfyNode]]:
|
||||||
return [
|
return [
|
||||||
TopazImageEnhance,
|
TopazImageEnhance,
|
||||||
TopazVideoEnhance,
|
TopazVideoEnhance,
|
||||||
|
TopazVideoEnhanceV2,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user