mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-01-24 21:30:15 +08:00
feat(api-nodes): extend ByteDance nodes with seedance-1-5-pro model (#11871)
Some checks are pending
Python Linting / Run Ruff (push) Waiting to run
Python Linting / Run Pylint (push) Waiting to run
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.11, [self-hosted Linux], stable) (push) Waiting to run
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.12, [self-hosted Linux], stable) (push) Waiting to run
Execution Tests / test (windows-latest) (push) Waiting to run
Test server launches without errors / test (push) Waiting to run
Unit Tests / test (macos-latest) (push) Waiting to run
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.10, [self-hosted Linux], stable) (push) Waiting to run
Full Comfy CI Workflow Runs / test-unix-nightly (12.1, , linux, 3.11, [self-hosted Linux], nightly) (push) Waiting to run
Execution Tests / test (macos-latest) (push) Waiting to run
Execution Tests / test (ubuntu-latest) (push) Waiting to run
Unit Tests / test (ubuntu-latest) (push) Waiting to run
Unit Tests / test (windows-2022) (push) Waiting to run
Some checks are pending
Python Linting / Run Ruff (push) Waiting to run
Python Linting / Run Pylint (push) Waiting to run
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.11, [self-hosted Linux], stable) (push) Waiting to run
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.12, [self-hosted Linux], stable) (push) Waiting to run
Execution Tests / test (windows-latest) (push) Waiting to run
Test server launches without errors / test (push) Waiting to run
Unit Tests / test (macos-latest) (push) Waiting to run
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.10, [self-hosted Linux], stable) (push) Waiting to run
Full Comfy CI Workflow Runs / test-unix-nightly (12.1, , linux, 3.11, [self-hosted Linux], nightly) (push) Waiting to run
Execution Tests / test (macos-latest) (push) Waiting to run
Execution Tests / test (ubuntu-latest) (push) Waiting to run
Unit Tests / test (ubuntu-latest) (push) Waiting to run
Unit Tests / test (windows-2022) (push) Waiting to run
This commit is contained in:
parent
732b707397
commit
9125613b53
@ -65,11 +65,13 @@ class TaskImageContent(BaseModel):
|
|||||||
class Text2VideoTaskCreationRequest(BaseModel):
|
class Text2VideoTaskCreationRequest(BaseModel):
|
||||||
model: str = Field(...)
|
model: str = Field(...)
|
||||||
content: list[TaskTextContent] = Field(..., min_length=1)
|
content: list[TaskTextContent] = Field(..., min_length=1)
|
||||||
|
generate_audio: bool | None = Field(...)
|
||||||
|
|
||||||
|
|
||||||
class Image2VideoTaskCreationRequest(BaseModel):
|
class Image2VideoTaskCreationRequest(BaseModel):
|
||||||
model: str = Field(...)
|
model: str = Field(...)
|
||||||
content: list[TaskTextContent | TaskImageContent] = Field(..., min_length=2)
|
content: list[TaskTextContent | TaskImageContent] = Field(..., min_length=2)
|
||||||
|
generate_audio: bool | None = Field(...)
|
||||||
|
|
||||||
|
|
||||||
class TaskCreationResponse(BaseModel):
|
class TaskCreationResponse(BaseModel):
|
||||||
@ -141,4 +143,9 @@ VIDEO_TASKS_EXECUTION_TIME = {
|
|||||||
"720p": 65,
|
"720p": 65,
|
||||||
"1080p": 100,
|
"1080p": 100,
|
||||||
},
|
},
|
||||||
|
"seedance-1-5-pro-251215": {
|
||||||
|
"480p": 80,
|
||||||
|
"720p": 100,
|
||||||
|
"1080p": 150,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -477,7 +477,12 @@ class ByteDanceTextToVideoNode(IO.ComfyNode):
|
|||||||
inputs=[
|
inputs=[
|
||||||
IO.Combo.Input(
|
IO.Combo.Input(
|
||||||
"model",
|
"model",
|
||||||
options=["seedance-1-0-pro-250528", "seedance-1-0-lite-t2v-250428", "seedance-1-0-pro-fast-251015"],
|
options=[
|
||||||
|
"seedance-1-5-pro-251215",
|
||||||
|
"seedance-1-0-pro-250528",
|
||||||
|
"seedance-1-0-lite-t2v-250428",
|
||||||
|
"seedance-1-0-pro-fast-251015",
|
||||||
|
],
|
||||||
default="seedance-1-0-pro-fast-251015",
|
default="seedance-1-0-pro-fast-251015",
|
||||||
),
|
),
|
||||||
IO.String.Input(
|
IO.String.Input(
|
||||||
@ -528,6 +533,12 @@ class ByteDanceTextToVideoNode(IO.ComfyNode):
|
|||||||
tooltip='Whether to add an "AI generated" watermark to the video.',
|
tooltip='Whether to add an "AI generated" watermark to the video.',
|
||||||
optional=True,
|
optional=True,
|
||||||
),
|
),
|
||||||
|
IO.Boolean.Input(
|
||||||
|
"generate_audio",
|
||||||
|
default=False,
|
||||||
|
tooltip="This parameter is ignored for any model except seedance-1-5-pro.",
|
||||||
|
optional=True,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
outputs=[
|
outputs=[
|
||||||
IO.Video.Output(),
|
IO.Video.Output(),
|
||||||
@ -552,7 +563,10 @@ class ByteDanceTextToVideoNode(IO.ComfyNode):
|
|||||||
seed: int,
|
seed: int,
|
||||||
camera_fixed: bool,
|
camera_fixed: bool,
|
||||||
watermark: bool,
|
watermark: bool,
|
||||||
|
generate_audio: bool = False,
|
||||||
) -> IO.NodeOutput:
|
) -> IO.NodeOutput:
|
||||||
|
if model == "seedance-1-5-pro-251215" and duration < 4:
|
||||||
|
raise ValueError("Minimum supported duration for Seedance 1.5 Pro is 4 seconds.")
|
||||||
validate_string(prompt, strip_whitespace=True, min_length=1)
|
validate_string(prompt, strip_whitespace=True, min_length=1)
|
||||||
raise_if_text_params(prompt, ["resolution", "ratio", "duration", "seed", "camerafixed", "watermark"])
|
raise_if_text_params(prompt, ["resolution", "ratio", "duration", "seed", "camerafixed", "watermark"])
|
||||||
|
|
||||||
@ -567,7 +581,11 @@ class ByteDanceTextToVideoNode(IO.ComfyNode):
|
|||||||
)
|
)
|
||||||
return await process_video_task(
|
return await process_video_task(
|
||||||
cls,
|
cls,
|
||||||
payload=Text2VideoTaskCreationRequest(model=model, content=[TaskTextContent(text=prompt)]),
|
payload=Text2VideoTaskCreationRequest(
|
||||||
|
model=model,
|
||||||
|
content=[TaskTextContent(text=prompt)],
|
||||||
|
generate_audio=generate_audio if model == "seedance-1-5-pro-251215" else None,
|
||||||
|
),
|
||||||
estimated_duration=max(1, math.ceil(VIDEO_TASKS_EXECUTION_TIME[model][resolution] * (duration / 10.0))),
|
estimated_duration=max(1, math.ceil(VIDEO_TASKS_EXECUTION_TIME[model][resolution] * (duration / 10.0))),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -584,7 +602,12 @@ class ByteDanceImageToVideoNode(IO.ComfyNode):
|
|||||||
inputs=[
|
inputs=[
|
||||||
IO.Combo.Input(
|
IO.Combo.Input(
|
||||||
"model",
|
"model",
|
||||||
options=["seedance-1-0-pro-250528", "seedance-1-0-lite-t2v-250428", "seedance-1-0-pro-fast-251015"],
|
options=[
|
||||||
|
"seedance-1-5-pro-251215",
|
||||||
|
"seedance-1-0-pro-250528",
|
||||||
|
"seedance-1-0-lite-i2v-250428",
|
||||||
|
"seedance-1-0-pro-fast-251015",
|
||||||
|
],
|
||||||
default="seedance-1-0-pro-fast-251015",
|
default="seedance-1-0-pro-fast-251015",
|
||||||
),
|
),
|
||||||
IO.String.Input(
|
IO.String.Input(
|
||||||
@ -639,6 +662,12 @@ class ByteDanceImageToVideoNode(IO.ComfyNode):
|
|||||||
tooltip='Whether to add an "AI generated" watermark to the video.',
|
tooltip='Whether to add an "AI generated" watermark to the video.',
|
||||||
optional=True,
|
optional=True,
|
||||||
),
|
),
|
||||||
|
IO.Boolean.Input(
|
||||||
|
"generate_audio",
|
||||||
|
default=False,
|
||||||
|
tooltip="This parameter is ignored for any model except seedance-1-5-pro.",
|
||||||
|
optional=True,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
outputs=[
|
outputs=[
|
||||||
IO.Video.Output(),
|
IO.Video.Output(),
|
||||||
@ -664,7 +693,10 @@ class ByteDanceImageToVideoNode(IO.ComfyNode):
|
|||||||
seed: int,
|
seed: int,
|
||||||
camera_fixed: bool,
|
camera_fixed: bool,
|
||||||
watermark: bool,
|
watermark: bool,
|
||||||
|
generate_audio: bool = False,
|
||||||
) -> IO.NodeOutput:
|
) -> IO.NodeOutput:
|
||||||
|
if model == "seedance-1-5-pro-251215" and duration < 4:
|
||||||
|
raise ValueError("Minimum supported duration for Seedance 1.5 Pro is 4 seconds.")
|
||||||
validate_string(prompt, strip_whitespace=True, min_length=1)
|
validate_string(prompt, strip_whitespace=True, min_length=1)
|
||||||
raise_if_text_params(prompt, ["resolution", "ratio", "duration", "seed", "camerafixed", "watermark"])
|
raise_if_text_params(prompt, ["resolution", "ratio", "duration", "seed", "camerafixed", "watermark"])
|
||||||
validate_image_dimensions(image, min_width=300, min_height=300, max_width=6000, max_height=6000)
|
validate_image_dimensions(image, min_width=300, min_height=300, max_width=6000, max_height=6000)
|
||||||
@ -686,6 +718,7 @@ class ByteDanceImageToVideoNode(IO.ComfyNode):
|
|||||||
payload=Image2VideoTaskCreationRequest(
|
payload=Image2VideoTaskCreationRequest(
|
||||||
model=model,
|
model=model,
|
||||||
content=[TaskTextContent(text=prompt), TaskImageContent(image_url=TaskImageContentUrl(url=image_url))],
|
content=[TaskTextContent(text=prompt), TaskImageContent(image_url=TaskImageContentUrl(url=image_url))],
|
||||||
|
generate_audio=generate_audio if model == "seedance-1-5-pro-251215" else None,
|
||||||
),
|
),
|
||||||
estimated_duration=max(1, math.ceil(VIDEO_TASKS_EXECUTION_TIME[model][resolution] * (duration / 10.0))),
|
estimated_duration=max(1, math.ceil(VIDEO_TASKS_EXECUTION_TIME[model][resolution] * (duration / 10.0))),
|
||||||
)
|
)
|
||||||
@ -703,7 +736,7 @@ class ByteDanceFirstLastFrameNode(IO.ComfyNode):
|
|||||||
inputs=[
|
inputs=[
|
||||||
IO.Combo.Input(
|
IO.Combo.Input(
|
||||||
"model",
|
"model",
|
||||||
options=["seedance-1-0-pro-250528", "seedance-1-0-lite-i2v-250428"],
|
options=["seedance-1-5-pro-251215", "seedance-1-0-pro-250528", "seedance-1-0-lite-i2v-250428"],
|
||||||
default="seedance-1-0-lite-i2v-250428",
|
default="seedance-1-0-lite-i2v-250428",
|
||||||
),
|
),
|
||||||
IO.String.Input(
|
IO.String.Input(
|
||||||
@ -762,6 +795,12 @@ class ByteDanceFirstLastFrameNode(IO.ComfyNode):
|
|||||||
tooltip='Whether to add an "AI generated" watermark to the video.',
|
tooltip='Whether to add an "AI generated" watermark to the video.',
|
||||||
optional=True,
|
optional=True,
|
||||||
),
|
),
|
||||||
|
IO.Boolean.Input(
|
||||||
|
"generate_audio",
|
||||||
|
default=False,
|
||||||
|
tooltip="This parameter is ignored for any model except seedance-1-5-pro.",
|
||||||
|
optional=True,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
outputs=[
|
outputs=[
|
||||||
IO.Video.Output(),
|
IO.Video.Output(),
|
||||||
@ -788,7 +827,10 @@ class ByteDanceFirstLastFrameNode(IO.ComfyNode):
|
|||||||
seed: int,
|
seed: int,
|
||||||
camera_fixed: bool,
|
camera_fixed: bool,
|
||||||
watermark: bool,
|
watermark: bool,
|
||||||
|
generate_audio: bool = False,
|
||||||
) -> IO.NodeOutput:
|
) -> IO.NodeOutput:
|
||||||
|
if model == "seedance-1-5-pro-251215" and duration < 4:
|
||||||
|
raise ValueError("Minimum supported duration for Seedance 1.5 Pro is 4 seconds.")
|
||||||
validate_string(prompt, strip_whitespace=True, min_length=1)
|
validate_string(prompt, strip_whitespace=True, min_length=1)
|
||||||
raise_if_text_params(prompt, ["resolution", "ratio", "duration", "seed", "camerafixed", "watermark"])
|
raise_if_text_params(prompt, ["resolution", "ratio", "duration", "seed", "camerafixed", "watermark"])
|
||||||
for i in (first_frame, last_frame):
|
for i in (first_frame, last_frame):
|
||||||
@ -821,6 +863,7 @@ class ByteDanceFirstLastFrameNode(IO.ComfyNode):
|
|||||||
TaskImageContent(image_url=TaskImageContentUrl(url=str(download_urls[0])), role="first_frame"),
|
TaskImageContent(image_url=TaskImageContentUrl(url=str(download_urls[0])), role="first_frame"),
|
||||||
TaskImageContent(image_url=TaskImageContentUrl(url=str(download_urls[1])), role="last_frame"),
|
TaskImageContent(image_url=TaskImageContentUrl(url=str(download_urls[1])), role="last_frame"),
|
||||||
],
|
],
|
||||||
|
generate_audio=generate_audio if model == "seedance-1-5-pro-251215" else None,
|
||||||
),
|
),
|
||||||
estimated_duration=max(1, math.ceil(VIDEO_TASKS_EXECUTION_TIME[model][resolution] * (duration / 10.0))),
|
estimated_duration=max(1, math.ceil(VIDEO_TASKS_EXECUTION_TIME[model][resolution] * (duration / 10.0))),
|
||||||
)
|
)
|
||||||
@ -896,7 +939,41 @@ class ByteDanceImageReferenceNode(IO.ComfyNode):
|
|||||||
IO.Hidden.unique_id,
|
IO.Hidden.unique_id,
|
||||||
],
|
],
|
||||||
is_api_node=True,
|
is_api_node=True,
|
||||||
price_badge=PRICE_BADGE_VIDEO,
|
price_badge=IO.PriceBadge(
|
||||||
|
depends_on=IO.PriceBadgeDepends(widgets=["model", "duration", "resolution"]),
|
||||||
|
expr="""
|
||||||
|
(
|
||||||
|
$priceByModel := {
|
||||||
|
"seedance-1-0-pro": {
|
||||||
|
"480p":[0.23,0.24],
|
||||||
|
"720p":[0.51,0.56]
|
||||||
|
},
|
||||||
|
"seedance-1-0-lite": {
|
||||||
|
"480p":[0.17,0.18],
|
||||||
|
"720p":[0.37,0.41]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
$model := widgets.model;
|
||||||
|
$modelKey :=
|
||||||
|
$contains($model, "seedance-1-0-pro") ? "seedance-1-0-pro" :
|
||||||
|
"seedance-1-0-lite";
|
||||||
|
$resolution := widgets.resolution;
|
||||||
|
$resKey :=
|
||||||
|
$contains($resolution, "720") ? "720p" :
|
||||||
|
"480p";
|
||||||
|
$modelPrices := $lookup($priceByModel, $modelKey);
|
||||||
|
$baseRange := $lookup($modelPrices, $resKey);
|
||||||
|
$min10s := $baseRange[0];
|
||||||
|
$max10s := $baseRange[1];
|
||||||
|
$scale := widgets.duration / 10;
|
||||||
|
$minCost := $min10s * $scale;
|
||||||
|
$maxCost := $max10s * $scale;
|
||||||
|
($minCost = $maxCost)
|
||||||
|
? {"type":"usd","usd": $minCost}
|
||||||
|
: {"type":"range_usd","min_usd": $minCost, "max_usd": $maxCost}
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -967,10 +1044,15 @@ def raise_if_text_params(prompt: str, text_params: list[str]) -> None:
|
|||||||
|
|
||||||
|
|
||||||
PRICE_BADGE_VIDEO = IO.PriceBadge(
|
PRICE_BADGE_VIDEO = IO.PriceBadge(
|
||||||
depends_on=IO.PriceBadgeDepends(widgets=["model", "duration", "resolution"]),
|
depends_on=IO.PriceBadgeDepends(widgets=["model", "duration", "resolution", "generate_audio"]),
|
||||||
expr="""
|
expr="""
|
||||||
(
|
(
|
||||||
$priceByModel := {
|
$priceByModel := {
|
||||||
|
"seedance-1-5-pro": {
|
||||||
|
"480p":[0.12,0.12],
|
||||||
|
"720p":[0.26,0.26],
|
||||||
|
"1080p":[0.58,0.59]
|
||||||
|
},
|
||||||
"seedance-1-0-pro": {
|
"seedance-1-0-pro": {
|
||||||
"480p":[0.23,0.24],
|
"480p":[0.23,0.24],
|
||||||
"720p":[0.51,0.56],
|
"720p":[0.51,0.56],
|
||||||
@ -989,6 +1071,7 @@ PRICE_BADGE_VIDEO = IO.PriceBadge(
|
|||||||
};
|
};
|
||||||
$model := widgets.model;
|
$model := widgets.model;
|
||||||
$modelKey :=
|
$modelKey :=
|
||||||
|
$contains($model, "seedance-1-5-pro") ? "seedance-1-5-pro" :
|
||||||
$contains($model, "seedance-1-0-pro-fast") ? "seedance-1-0-pro-fast" :
|
$contains($model, "seedance-1-0-pro-fast") ? "seedance-1-0-pro-fast" :
|
||||||
$contains($model, "seedance-1-0-pro") ? "seedance-1-0-pro" :
|
$contains($model, "seedance-1-0-pro") ? "seedance-1-0-pro" :
|
||||||
"seedance-1-0-lite";
|
"seedance-1-0-lite";
|
||||||
@ -1002,11 +1085,12 @@ PRICE_BADGE_VIDEO = IO.PriceBadge(
|
|||||||
$min10s := $baseRange[0];
|
$min10s := $baseRange[0];
|
||||||
$max10s := $baseRange[1];
|
$max10s := $baseRange[1];
|
||||||
$scale := widgets.duration / 10;
|
$scale := widgets.duration / 10;
|
||||||
$minCost := $min10s * $scale;
|
$audioMultiplier := ($modelKey = "seedance-1-5-pro" and widgets.generate_audio) ? 2 : 1;
|
||||||
$maxCost := $max10s * $scale;
|
$minCost := $min10s * $scale * $audioMultiplier;
|
||||||
|
$maxCost := $max10s * $scale * $audioMultiplier;
|
||||||
($minCost = $maxCost)
|
($minCost = $maxCost)
|
||||||
? {"type":"usd","usd": $minCost}
|
? {"type":"usd","usd": $minCost, "format": { "approximate": true }}
|
||||||
: {"type":"range_usd","min_usd": $minCost, "max_usd": $maxCost}
|
: {"type":"range_usd","min_usd": $minCost, "max_usd": $maxCost, "format": { "approximate": true }}
|
||||||
)
|
)
|
||||||
""",
|
""",
|
||||||
)
|
)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user