Skip to content

Commit 9125613

Browse files
authored
feat(api-nodes): extend ByteDance nodes with seedance-1-5-pro model (Comfy-Org#11871)
1 parent 732b707 commit 9125613

2 files changed

Lines changed: 101 additions & 10 deletions

File tree

comfy_api_nodes/apis/bytedance_api.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,13 @@ class TaskImageContent(BaseModel):
6565
class Text2VideoTaskCreationRequest(BaseModel):
6666
model: str = Field(...)
6767
content: list[TaskTextContent] = Field(..., min_length=1)
68+
generate_audio: bool | None = Field(...)
6869

6970

7071
class Image2VideoTaskCreationRequest(BaseModel):
7172
model: str = Field(...)
7273
content: list[TaskTextContent | TaskImageContent] = Field(..., min_length=2)
74+
generate_audio: bool | None = Field(...)
7375

7476

7577
class TaskCreationResponse(BaseModel):
@@ -141,4 +143,9 @@ class TaskStatusResponse(BaseModel):
141143
"720p": 65,
142144
"1080p": 100,
143145
},
146+
"seedance-1-5-pro-251215": {
147+
"480p": 80,
148+
"720p": 100,
149+
"1080p": 150,
150+
},
144151
}

comfy_api_nodes/nodes_bytedance.py

Lines changed: 94 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,12 @@ def define_schema(cls):
477477
inputs=[
478478
IO.Combo.Input(
479479
"model",
480-
options=["seedance-1-0-pro-250528", "seedance-1-0-lite-t2v-250428", "seedance-1-0-pro-fast-251015"],
480+
options=[
481+
"seedance-1-5-pro-251215",
482+
"seedance-1-0-pro-250528",
483+
"seedance-1-0-lite-t2v-250428",
484+
"seedance-1-0-pro-fast-251015",
485+
],
481486
default="seedance-1-0-pro-fast-251015",
482487
),
483488
IO.String.Input(
@@ -528,6 +533,12 @@ def define_schema(cls):
528533
tooltip='Whether to add an "AI generated" watermark to the video.',
529534
optional=True,
530535
),
536+
IO.Boolean.Input(
537+
"generate_audio",
538+
default=False,
539+
tooltip="This parameter is ignored for any model except seedance-1-5-pro.",
540+
optional=True,
541+
),
531542
],
532543
outputs=[
533544
IO.Video.Output(),
@@ -552,7 +563,10 @@ async def execute(
552563
seed: int,
553564
camera_fixed: bool,
554565
watermark: bool,
566+
generate_audio: bool = False,
555567
) -> IO.NodeOutput:
568+
if model == "seedance-1-5-pro-251215" and duration < 4:
569+
raise ValueError("Minimum supported duration for Seedance 1.5 Pro is 4 seconds.")
556570
validate_string(prompt, strip_whitespace=True, min_length=1)
557571
raise_if_text_params(prompt, ["resolution", "ratio", "duration", "seed", "camerafixed", "watermark"])
558572

@@ -567,7 +581,11 @@ async def execute(
567581
)
568582
return await process_video_task(
569583
cls,
570-
payload=Text2VideoTaskCreationRequest(model=model, content=[TaskTextContent(text=prompt)]),
584+
payload=Text2VideoTaskCreationRequest(
585+
model=model,
586+
content=[TaskTextContent(text=prompt)],
587+
generate_audio=generate_audio if model == "seedance-1-5-pro-251215" else None,
588+
),
571589
estimated_duration=max(1, math.ceil(VIDEO_TASKS_EXECUTION_TIME[model][resolution] * (duration / 10.0))),
572590
)
573591

@@ -584,7 +602,12 @@ def define_schema(cls):
584602
inputs=[
585603
IO.Combo.Input(
586604
"model",
587-
options=["seedance-1-0-pro-250528", "seedance-1-0-lite-t2v-250428", "seedance-1-0-pro-fast-251015"],
605+
options=[
606+
"seedance-1-5-pro-251215",
607+
"seedance-1-0-pro-250528",
608+
"seedance-1-0-lite-i2v-250428",
609+
"seedance-1-0-pro-fast-251015",
610+
],
588611
default="seedance-1-0-pro-fast-251015",
589612
),
590613
IO.String.Input(
@@ -639,6 +662,12 @@ def define_schema(cls):
639662
tooltip='Whether to add an "AI generated" watermark to the video.',
640663
optional=True,
641664
),
665+
IO.Boolean.Input(
666+
"generate_audio",
667+
default=False,
668+
tooltip="This parameter is ignored for any model except seedance-1-5-pro.",
669+
optional=True,
670+
),
642671
],
643672
outputs=[
644673
IO.Video.Output(),
@@ -664,7 +693,10 @@ async def execute(
664693
seed: int,
665694
camera_fixed: bool,
666695
watermark: bool,
696+
generate_audio: bool = False,
667697
) -> IO.NodeOutput:
698+
if model == "seedance-1-5-pro-251215" and duration < 4:
699+
raise ValueError("Minimum supported duration for Seedance 1.5 Pro is 4 seconds.")
668700
validate_string(prompt, strip_whitespace=True, min_length=1)
669701
raise_if_text_params(prompt, ["resolution", "ratio", "duration", "seed", "camerafixed", "watermark"])
670702
validate_image_dimensions(image, min_width=300, min_height=300, max_width=6000, max_height=6000)
@@ -686,6 +718,7 @@ async def execute(
686718
payload=Image2VideoTaskCreationRequest(
687719
model=model,
688720
content=[TaskTextContent(text=prompt), TaskImageContent(image_url=TaskImageContentUrl(url=image_url))],
721+
generate_audio=generate_audio if model == "seedance-1-5-pro-251215" else None,
689722
),
690723
estimated_duration=max(1, math.ceil(VIDEO_TASKS_EXECUTION_TIME[model][resolution] * (duration / 10.0))),
691724
)
@@ -703,7 +736,7 @@ def define_schema(cls):
703736
inputs=[
704737
IO.Combo.Input(
705738
"model",
706-
options=["seedance-1-0-pro-250528", "seedance-1-0-lite-i2v-250428"],
739+
options=["seedance-1-5-pro-251215", "seedance-1-0-pro-250528", "seedance-1-0-lite-i2v-250428"],
707740
default="seedance-1-0-lite-i2v-250428",
708741
),
709742
IO.String.Input(
@@ -762,6 +795,12 @@ def define_schema(cls):
762795
tooltip='Whether to add an "AI generated" watermark to the video.',
763796
optional=True,
764797
),
798+
IO.Boolean.Input(
799+
"generate_audio",
800+
default=False,
801+
tooltip="This parameter is ignored for any model except seedance-1-5-pro.",
802+
optional=True,
803+
),
765804
],
766805
outputs=[
767806
IO.Video.Output(),
@@ -788,7 +827,10 @@ async def execute(
788827
seed: int,
789828
camera_fixed: bool,
790829
watermark: bool,
830+
generate_audio: bool = False,
791831
) -> IO.NodeOutput:
832+
if model == "seedance-1-5-pro-251215" and duration < 4:
833+
raise ValueError("Minimum supported duration for Seedance 1.5 Pro is 4 seconds.")
792834
validate_string(prompt, strip_whitespace=True, min_length=1)
793835
raise_if_text_params(prompt, ["resolution", "ratio", "duration", "seed", "camerafixed", "watermark"])
794836
for i in (first_frame, last_frame):
@@ -821,6 +863,7 @@ async def execute(
821863
TaskImageContent(image_url=TaskImageContentUrl(url=str(download_urls[0])), role="first_frame"),
822864
TaskImageContent(image_url=TaskImageContentUrl(url=str(download_urls[1])), role="last_frame"),
823865
],
866+
generate_audio=generate_audio if model == "seedance-1-5-pro-251215" else None,
824867
),
825868
estimated_duration=max(1, math.ceil(VIDEO_TASKS_EXECUTION_TIME[model][resolution] * (duration / 10.0))),
826869
)
@@ -896,7 +939,41 @@ def define_schema(cls):
896939
IO.Hidden.unique_id,
897940
],
898941
is_api_node=True,
899-
price_badge=PRICE_BADGE_VIDEO,
942+
price_badge=IO.PriceBadge(
943+
depends_on=IO.PriceBadgeDepends(widgets=["model", "duration", "resolution"]),
944+
expr="""
945+
(
946+
$priceByModel := {
947+
"seedance-1-0-pro": {
948+
"480p":[0.23,0.24],
949+
"720p":[0.51,0.56]
950+
},
951+
"seedance-1-0-lite": {
952+
"480p":[0.17,0.18],
953+
"720p":[0.37,0.41]
954+
}
955+
};
956+
$model := widgets.model;
957+
$modelKey :=
958+
$contains($model, "seedance-1-0-pro") ? "seedance-1-0-pro" :
959+
"seedance-1-0-lite";
960+
$resolution := widgets.resolution;
961+
$resKey :=
962+
$contains($resolution, "720") ? "720p" :
963+
"480p";
964+
$modelPrices := $lookup($priceByModel, $modelKey);
965+
$baseRange := $lookup($modelPrices, $resKey);
966+
$min10s := $baseRange[0];
967+
$max10s := $baseRange[1];
968+
$scale := widgets.duration / 10;
969+
$minCost := $min10s * $scale;
970+
$maxCost := $max10s * $scale;
971+
($minCost = $maxCost)
972+
? {"type":"usd","usd": $minCost}
973+
: {"type":"range_usd","min_usd": $minCost, "max_usd": $maxCost}
974+
)
975+
""",
976+
),
900977
)
901978

902979
@classmethod
@@ -967,10 +1044,15 @@ def raise_if_text_params(prompt: str, text_params: list[str]) -> None:
9671044

9681045

9691046
PRICE_BADGE_VIDEO = IO.PriceBadge(
970-
depends_on=IO.PriceBadgeDepends(widgets=["model", "duration", "resolution"]),
1047+
depends_on=IO.PriceBadgeDepends(widgets=["model", "duration", "resolution", "generate_audio"]),
9711048
expr="""
9721049
(
9731050
$priceByModel := {
1051+
"seedance-1-5-pro": {
1052+
"480p":[0.12,0.12],
1053+
"720p":[0.26,0.26],
1054+
"1080p":[0.58,0.59]
1055+
},
9741056
"seedance-1-0-pro": {
9751057
"480p":[0.23,0.24],
9761058
"720p":[0.51,0.56],
@@ -989,6 +1071,7 @@ def raise_if_text_params(prompt: str, text_params: list[str]) -> None:
9891071
};
9901072
$model := widgets.model;
9911073
$modelKey :=
1074+
$contains($model, "seedance-1-5-pro") ? "seedance-1-5-pro" :
9921075
$contains($model, "seedance-1-0-pro-fast") ? "seedance-1-0-pro-fast" :
9931076
$contains($model, "seedance-1-0-pro") ? "seedance-1-0-pro" :
9941077
"seedance-1-0-lite";
@@ -1002,11 +1085,12 @@ def raise_if_text_params(prompt: str, text_params: list[str]) -> None:
10021085
$min10s := $baseRange[0];
10031086
$max10s := $baseRange[1];
10041087
$scale := widgets.duration / 10;
1005-
$minCost := $min10s * $scale;
1006-
$maxCost := $max10s * $scale;
1088+
$audioMultiplier := ($modelKey = "seedance-1-5-pro" and widgets.generate_audio) ? 2 : 1;
1089+
$minCost := $min10s * $scale * $audioMultiplier;
1090+
$maxCost := $max10s * $scale * $audioMultiplier;
10071091
($minCost = $maxCost)
1008-
? {"type":"usd","usd": $minCost}
1009-
: {"type":"range_usd","min_usd": $minCost, "max_usd": $maxCost}
1092+
? {"type":"usd","usd": $minCost, "format": { "approximate": true }}
1093+
: {"type":"range_usd","min_usd": $minCost, "max_usd": $maxCost, "format": { "approximate": true }}
10101094
)
10111095
""",
10121096
)

0 commit comments

Comments
 (0)