Skip to content

Commit 5284e6b

Browse files
authored
feat(api-nodes): add "viduq3-turbo" model and Vidu3StartEnd node; fix the price badges (#12482)
1 parent 44f8598 commit 5284e6b

1 file changed

Lines changed: 218 additions & 8 deletions

File tree

comfy_api_nodes/nodes_vidu.py

Lines changed: 218 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ async def execute_task(
5454
response_model=TaskStatusResponse,
5555
status_extractor=lambda r: r.state,
5656
progress_extractor=lambda r: r.progress,
57+
price_extractor=lambda r: r.credits * 0.005 if r.credits is not None else None,
5758
max_poll_attempts=max_poll_attempts,
5859
)
5960
if not response.creations:
@@ -1306,6 +1307,36 @@ def define_schema(cls):
13061307
),
13071308
],
13081309
),
1310+
IO.DynamicCombo.Option(
1311+
"viduq3-turbo",
1312+
[
1313+
IO.Combo.Input(
1314+
"aspect_ratio",
1315+
options=["16:9", "9:16", "3:4", "4:3", "1:1"],
1316+
tooltip="The aspect ratio of the output video.",
1317+
),
1318+
IO.Combo.Input(
1319+
"resolution",
1320+
options=["720p", "1080p"],
1321+
tooltip="Resolution of the output video.",
1322+
),
1323+
IO.Int.Input(
1324+
"duration",
1325+
default=5,
1326+
min=1,
1327+
max=16,
1328+
step=1,
1329+
display_mode=IO.NumberDisplay.slider,
1330+
tooltip="Duration of the output video in seconds.",
1331+
),
1332+
IO.Boolean.Input(
1333+
"audio",
1334+
default=False,
1335+
tooltip="When enabled, outputs video with sound "
1336+
"(including dialogue and sound effects).",
1337+
),
1338+
],
1339+
),
13091340
],
13101341
tooltip="Model to use for video generation.",
13111342
),
@@ -1334,13 +1365,20 @@ def define_schema(cls):
13341365
],
13351366
is_api_node=True,
13361367
price_badge=IO.PriceBadge(
1337-
depends_on=IO.PriceBadgeDepends(widgets=["model.duration", "model.resolution"]),
1368+
depends_on=IO.PriceBadgeDepends(widgets=["model", "model.duration", "model.resolution"]),
13381369
expr="""
13391370
(
13401371
$res := $lookup(widgets, "model.resolution");
1341-
$base := $lookup({"720p": 0.075, "1080p": 0.1}, $res);
1342-
$perSec := $lookup({"720p": 0.025, "1080p": 0.05}, $res);
1343-
{"type":"usd","usd": $base + $perSec * ($lookup(widgets, "model.duration") - 1)}
1372+
$d := $lookup(widgets, "model.duration");
1373+
$contains(widgets.model, "turbo")
1374+
? (
1375+
$rate := $lookup({"720p": 0.06, "1080p": 0.08}, $res);
1376+
{"type":"usd","usd": $rate * $d}
1377+
)
1378+
: (
1379+
$rate := $lookup({"720p": 0.15, "1080p": 0.16}, $res);
1380+
{"type":"usd","usd": $rate * $d}
1381+
)
13441382
)
13451383
""",
13461384
),
@@ -1409,6 +1447,31 @@ def define_schema(cls):
14091447
),
14101448
],
14111449
),
1450+
IO.DynamicCombo.Option(
1451+
"viduq3-turbo",
1452+
[
1453+
IO.Combo.Input(
1454+
"resolution",
1455+
options=["720p", "1080p"],
1456+
tooltip="Resolution of the output video.",
1457+
),
1458+
IO.Int.Input(
1459+
"duration",
1460+
default=5,
1461+
min=1,
1462+
max=16,
1463+
step=1,
1464+
display_mode=IO.NumberDisplay.slider,
1465+
tooltip="Duration of the output video in seconds.",
1466+
),
1467+
IO.Boolean.Input(
1468+
"audio",
1469+
default=False,
1470+
tooltip="When enabled, outputs video with sound "
1471+
"(including dialogue and sound effects).",
1472+
),
1473+
],
1474+
),
14121475
],
14131476
tooltip="Model to use for video generation.",
14141477
),
@@ -1442,13 +1505,20 @@ def define_schema(cls):
14421505
],
14431506
is_api_node=True,
14441507
price_badge=IO.PriceBadge(
1445-
depends_on=IO.PriceBadgeDepends(widgets=["model.duration", "model.resolution"]),
1508+
depends_on=IO.PriceBadgeDepends(widgets=["model", "model.duration", "model.resolution"]),
14461509
expr="""
14471510
(
14481511
$res := $lookup(widgets, "model.resolution");
1449-
$base := $lookup({"720p": 0.075, "1080p": 0.275, "2k": 0.35}, $res);
1450-
$perSec := $lookup({"720p": 0.05, "1080p": 0.075, "2k": 0.075}, $res);
1451-
{"type":"usd","usd": $base + $perSec * ($lookup(widgets, "model.duration") - 1)}
1512+
$d := $lookup(widgets, "model.duration");
1513+
$contains(widgets.model, "turbo")
1514+
? (
1515+
$rate := $lookup({"720p": 0.06, "1080p": 0.08}, $res);
1516+
{"type":"usd","usd": $rate * $d}
1517+
)
1518+
: (
1519+
$rate := $lookup({"720p": 0.15, "1080p": 0.16, "2k": 0.2}, $res);
1520+
{"type":"usd","usd": $rate * $d}
1521+
)
14521522
)
14531523
""",
14541524
),
@@ -1481,6 +1551,145 @@ async def execute(
14811551
return IO.NodeOutput(await download_url_to_video_output(results[0].url))
14821552

14831553

1554+
class Vidu3StartEndToVideoNode(IO.ComfyNode):
1555+
1556+
@classmethod
1557+
def define_schema(cls):
1558+
return IO.Schema(
1559+
node_id="Vidu3StartEndToVideoNode",
1560+
display_name="Vidu Q3 Start/End Frame-to-Video Generation",
1561+
category="api node/video/Vidu",
1562+
description="Generate a video from a start frame, an end frame, and a prompt.",
1563+
inputs=[
1564+
IO.DynamicCombo.Input(
1565+
"model",
1566+
options=[
1567+
IO.DynamicCombo.Option(
1568+
"viduq3-pro",
1569+
[
1570+
IO.Combo.Input(
1571+
"resolution",
1572+
options=["720p", "1080p"],
1573+
tooltip="Resolution of the output video.",
1574+
),
1575+
IO.Int.Input(
1576+
"duration",
1577+
default=5,
1578+
min=1,
1579+
max=16,
1580+
step=1,
1581+
display_mode=IO.NumberDisplay.slider,
1582+
tooltip="Duration of the output video in seconds.",
1583+
),
1584+
IO.Boolean.Input(
1585+
"audio",
1586+
default=False,
1587+
tooltip="When enabled, outputs video with sound "
1588+
"(including dialogue and sound effects).",
1589+
),
1590+
],
1591+
),
1592+
IO.DynamicCombo.Option(
1593+
"viduq3-turbo",
1594+
[
1595+
IO.Combo.Input(
1596+
"resolution",
1597+
options=["720p", "1080p"],
1598+
tooltip="Resolution of the output video.",
1599+
),
1600+
IO.Int.Input(
1601+
"duration",
1602+
default=5,
1603+
min=1,
1604+
max=16,
1605+
step=1,
1606+
display_mode=IO.NumberDisplay.slider,
1607+
tooltip="Duration of the output video in seconds.",
1608+
),
1609+
IO.Boolean.Input(
1610+
"audio",
1611+
default=False,
1612+
tooltip="When enabled, outputs video with sound "
1613+
"(including dialogue and sound effects).",
1614+
),
1615+
],
1616+
),
1617+
],
1618+
tooltip="Model to use for video generation.",
1619+
),
1620+
IO.Image.Input("first_frame"),
1621+
IO.Image.Input("end_frame"),
1622+
IO.String.Input(
1623+
"prompt",
1624+
multiline=True,
1625+
tooltip="Prompt description (max 2000 characters).",
1626+
),
1627+
IO.Int.Input(
1628+
"seed",
1629+
default=1,
1630+
min=0,
1631+
max=2147483647,
1632+
step=1,
1633+
display_mode=IO.NumberDisplay.number,
1634+
control_after_generate=True,
1635+
),
1636+
],
1637+
outputs=[
1638+
IO.Video.Output(),
1639+
],
1640+
hidden=[
1641+
IO.Hidden.auth_token_comfy_org,
1642+
IO.Hidden.api_key_comfy_org,
1643+
IO.Hidden.unique_id,
1644+
],
1645+
is_api_node=True,
1646+
price_badge=IO.PriceBadge(
1647+
depends_on=IO.PriceBadgeDepends(widgets=["model", "model.duration", "model.resolution"]),
1648+
expr="""
1649+
(
1650+
$res := $lookup(widgets, "model.resolution");
1651+
$d := $lookup(widgets, "model.duration");
1652+
$contains(widgets.model, "turbo")
1653+
? (
1654+
$rate := $lookup({"720p": 0.06, "1080p": 0.08}, $res);
1655+
{"type":"usd","usd": $rate * $d}
1656+
)
1657+
: (
1658+
$rate := $lookup({"720p": 0.15, "1080p": 0.16}, $res);
1659+
{"type":"usd","usd": $rate * $d}
1660+
)
1661+
)
1662+
""",
1663+
),
1664+
)
1665+
1666+
@classmethod
1667+
async def execute(
1668+
cls,
1669+
model: dict,
1670+
first_frame: Input.Image,
1671+
end_frame: Input.Image,
1672+
prompt: str,
1673+
seed: int,
1674+
) -> IO.NodeOutput:
1675+
validate_string(prompt, max_length=2000)
1676+
validate_images_aspect_ratio_closeness(first_frame, end_frame, min_rel=0.8, max_rel=1.25, strict=False)
1677+
payload = TaskCreationRequest(
1678+
model=model["model"],
1679+
prompt=prompt,
1680+
duration=model["duration"],
1681+
seed=seed,
1682+
resolution=model["resolution"],
1683+
audio=model["audio"],
1684+
images=[
1685+
(await upload_images_to_comfyapi(cls, frame, max_images=1, mime_type="image/png"))[0]
1686+
for frame in (first_frame, end_frame)
1687+
],
1688+
)
1689+
results = await execute_task(cls, VIDU_START_END_VIDEO, payload)
1690+
return IO.NodeOutput(await download_url_to_video_output(results[0].url))
1691+
1692+
14841693
class ViduExtension(ComfyExtension):
14851694
@override
14861695
async def get_node_list(self) -> list[type[IO.ComfyNode]]:
@@ -1497,6 +1706,7 @@ async def get_node_list(self) -> list[type[IO.ComfyNode]]:
14971706
ViduMultiFrameVideoNode,
14981707
Vidu3TextToVideoNode,
14991708
Vidu3ImageToVideoNode,
1709+
Vidu3StartEndToVideoNode,
15001710
]
15011711

15021712

0 commit comments

Comments
 (0)