17 Commits
Author SHA1 Message Date
Pedro Gabriel Gengo Lourenço d783188eeb Merge pull request #12 from lumalabs/feature/adding-im2vid-and-flash-and-audio
Adding support for ray 2 and ray2 flash in all tasks, and adding audi…
2025-03-24 22:17:42 -03:00
Pedro gengo 822248eb37 Updating version 2025-03-24 22:11:12 -03:00
Pedro gengo 07dd99197e Small fixes + updating documentation 2025-03-24 22:09:47 -03:00
Pedro gengo a53576c98b Exposing new nodes 2025-03-24 21:33:24 -03:00
Pedro gengo 04c8f37f73 Adding support for ray 2 and ray2 flash in all tasks, and adding audio and upscale tasks 2025-03-24 21:25:38 -03:00
Pedro Gabriel Gengo Lourenço 0a7f4cea0c Merge pull request #11 from lumalabs/fix/imgbb_output
fixing output from imgbb
2025-02-14 15:49:47 -03:00
Pedro gengo df5efb5b95 fixing output from imgbb 2025-02-14 18:49:13 +00:00
Pedro Gabriel Gengo Lourenço 89dc09b04d Merge pull request #8 from lumalabs/feat/add-ray2
Feat/add ray2
2025-01-27 19:37:15 -03:00
Pedro gengo 4cc646b343 removing print 2025-01-27 22:34:24 +00:00
Pedro gengo 0264bf9ab5 updated docs 2025-01-27 22:10:31 +00:00
Pedro gengo 53cf9a8267 Adding ray2 node 2025-01-27 22:03:48 +00:00
Pedro Gabriel Gengo Lourenço e6edbc0f97 Merge pull request #6 from lumalabs/enhancement/misc
bypassing cache and increasing sleep time to avoid rate limit
2024-12-09 23:28:07 -03:00
Pedro gengo b9ec910206 bypassing cache and increasing sleep time to avoid rate limit 2024-12-09 23:27:23 -03:00
Pedro Gabriel Gengo Lourenço 577be739fb Bumping version 2024-12-03 18:34:07 -03:00
Pedro Gabriel Gengo Lourenço 2c24934900 Merge pull request #4 from lumalabs/feat/adding-photon
Adding new image api
2024-12-03 18:32:51 -03:00
Pedro gengo 7fb8e6b73c Adding new image api 2024-12-03 17:31:34 -03:00
Pedro Gengo 4f64a66ba3 Improving docs for windows 2024-09-20 19:53:32 -03:00
16 changed files with 2798 additions and 88 deletions
+41 -6
View File
@@ -4,7 +4,7 @@
</p>
This is a custom node for ComfyUI that allows you to use the Luma AI API directly in ComfyUI. Luma AI API is based on top of [Dream Machine](https://lumalabs.ai/dream-machine/api), which is a video generation model developed by Luma. For more information, see [Luma AI API Documentation](https://docs.lumalabs.ai/docs/api).
This is a custom node for ComfyUI that allows you to use the Luma AI API directly in ComfyUI. Luma AI API is based on top of [Dream Machine](https://lumalabs.ai/dream-machine/api), which is a complete suite of models for image and video generation. For more information, see [Luma AI API Documentation](https://docs.lumalabs.ai/docs/api).
## Requirements
@@ -22,7 +22,10 @@ Before using this node, you need to have an LumaAI API key. Get one [here](https
```
The path should be `ComfyUI/custom_nodes/ComfyUI-LumaAI-API/*`, where `*` represents all the files in this repo.
3. Run `cd ComfyUI-LumaAI-API && pip install -r requirements.txt` to install the dependencies.
3. Install the dependencies:
- If you are using Windows (ComfyUI portable) run: `.\python_embeded\python.exe -m pip install -r ComfyUI\custom_nodes\ComfyUI-LumaAI-API\requirements.txt`
- If you are using Linux or MacOS, run: `cd ComfyUI-LumaAI-API && pip install -r requirements.txt` to install the dependencies.
4. If you don't want to expose your Luma API key, you can add it to the `config.ini` file and keep it empty in the node.
@@ -40,25 +43,37 @@ Before using this node, you need to have an LumaAI API key. Get one [here](https
Most of the nodes allow you to save locally the output video. If you keep the default `filename` (empty string), the video will be saved in the `outputs` folder using the `generation_id` as the name.
For images, the node will always save the image locally, but you can set the `filename` to save it with a custom name.
**Notes: Duration and resolution parameters are only supported for Ray 2 and Ray 2 Flash models.**
### LumaAIClient
This node is used to create a LumaAI client.
### LumaText2Video
This node is used to generate a video from a text prompt.
This node is used to generate a video from a text prompt. You can now choose between different models and resolutions.
### LumaImage2Video
This node is used to generate a video from an image. The image can be used as the first or last frame.
This node is used to generate a video from an image. The image can be used as the first or last frame. You can now choose between different models and resolutions.
### LumaInterpolateGenerations
This node is used to interpolate between two generations.
This node is used to interpolate between two generations. You can now choose between different models and resolutions.
### LumaExtendGeneration
This node is used to extend a generation. You can choose to extend before or after the generation.
This node is used to extend a generation. You can choose to extend before or after the generation. You can now choose between different models and resolutions.
### LumaUpscaleGeneration
This node is used to upscale a generation.
### LumaAddAudio2Video
This node is used to add audio to a video. When using this node with `Preview Video` node, you will notice that the audio won't play in comfy. For that, right click on the video and select `Open Image`, which will open the video in a new tab and you will be able to hear the audio.
### LumaPreviewVideo
@@ -69,6 +84,26 @@ This node is used to preview a video. The video is resized to 768px to look bett
This node is used to upload an image to ImgBB and return the URL. We need this because Luma API currently only supports image urls as input.
To use this node, you need to have an ImgBB API key. Create an account and get one [here](https://api.imgbb.com/).
### Reference
This node is used to create a reference from an image URL. It is used for style and image references.
### ConcatReferences
This node is used to concatenate a list of references.
### CharacterReference
This node is used to create a character reference from a list of image URLs.
### ImageGeneration
This node is used to generate an image from a prompt.
### ModifyImage
This node is used to modify an image.
## Examples
For examples, see [workflows folder](./workflows). To use, just download the workflow json and import it into ComfyUI.
+1 -1
View File
@@ -1,4 +1,4 @@
from .py.nodes import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
from .py.luma_nodes import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
WEB_DIRECTORY = "./web"
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS", "WEB_DIRECTORY"]
+1 -1
View File
@@ -60,7 +60,7 @@ class ImgBBUpload:
result = response.json()
if result.get("success"):
return result["data"]["url"], result["data"]["delete_url"]
return (result["data"]["url"],)
else:
error_message = result.get("error", {}).get("message", "Unknown error")
raise ValueError(f"Error: {error_message}")
+21
View File
@@ -5,6 +5,13 @@ from .lumaai_api_node import (
InterpolateGenerations,
ExtendGeneration,
PreviewVideo,
Reference,
ConcatReferences,
CharacterReference,
ImageGeneration,
ModifyImage,
AddAudio2Video,
UpscaleGeneration,
)
from .imgbb_node import ImgBBUpload
@@ -16,6 +23,13 @@ NODE_CLASS_MAPPINGS = {
"LumaInterpolateGenerations": InterpolateGenerations,
"LumaExtendGeneration": ExtendGeneration,
"LumaPreviewVideo": PreviewVideo,
"Reference": Reference,
"ConcatReferences": ConcatReferences,
"CharacterReference": CharacterReference,
"LumaImageGeneration": ImageGeneration,
"LumaModifyImage": ModifyImage,
"LumaAddAudio2Video": AddAudio2Video,
"LumaUpscaleGeneration": UpscaleGeneration,
}
NODE_DISPLAY_NAME_MAPPINGS = {
@@ -26,4 +40,11 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"LumaInterpolateGenerations": "Interpolate Generations",
"LumaExtendGeneration": "Extend Generation",
"LumaPreviewVideo": "LumaAI Preview Video",
"LumaReference": "Reference",
"LumaConcatReferences": "Concat References",
"LumaCharacterReference": "Character Reference",
"LumaImageGeneration": "Image Generation",
"LumaModifyImage": "Modify Image",
"LumaAddAudio2Video": "Add Audio to Video",
"LumaUpscaleGeneration": "Upscale Generation",
}
+447 -74
View File
@@ -5,6 +5,7 @@ import time
from lumaai import LumaAI
import folder_paths
import nodes
current_dir = os.path.dirname(os.path.abspath(__file__))
parent_dir = os.path.dirname(current_dir)
@@ -14,7 +15,7 @@ config = configparser.ConfigParser()
config.read(config_path)
try:
luma_api_key = config['API']['LUMAAI_API_KEY']
luma_api_key = config["API"]["LUMAAI_API_KEY"]
if luma_api_key != "":
os.environ["LUMAAI_API_KEY"] = luma_api_key
else:
@@ -29,6 +30,7 @@ def download_file(url, file_name):
file.write(response.content)
print(f"File downloaded as {file_name}")
def parse_filename(filename):
# Remove file extension if present
filename = os.path.splitext(filename)[0]
@@ -51,6 +53,27 @@ def parse_filename(filename):
return directory, filename
def wait_for_generation(client, generation_id, save, filename, output_dir):
completed = False
while not completed:
generation = client.generations.get(id=generation_id)
if generation.state == "completed":
completed = True
elif generation.state == "failed":
raise ValueError(f"Generation failed: {generation.failure_reason}")
time.sleep(3)
video_url = generation.assets.video
if save:
directory, filename = parse_filename(filename)
if filename == "":
filename = generation_id
download_file(
video_url, os.path.join(output_dir, directory, filename + ".mp4")
)
return video_url
class LumaAIClient:
@classmethod
def INPUT_TYPES(cls):
@@ -85,19 +108,25 @@ class LumaAIClient:
return (client,)
class Text2Video:
def __init__(self):
self.output_dir = folder_paths.get_output_directory()
@classmethod
def IS_CHANGED(cls, *args, **kwargs):
return float("NaN")
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"client": ("LUMACLIENT", {"forceInput": True}),
"model": (["ray-flash-2", "ray-2", "ray-1.6"],),
"prompt": ("STRING", {"multiline": True, "default": ""}),
"duration": (["5s", "9s"],),
"loop": ("BOOLEAN", {"default": False}),
"aspect_ratio": (["9:16", "3:4", "1:1", "4:3", "16:9", "21:9"],),
"resolution": (["540p", "720p"],),
"save": ("BOOLEAN", {"default": True}),
},
"optional": {"filename": ("STRING", {"default": ""})},
@@ -107,9 +136,9 @@ class Text2Video:
RETURN_NAMES = ("video_url", "generation_id")
OUTPUT_NODE = True
FUNCTION = "run"
CATEGORY = "LumaAI"
CATEGORY = "LumaAI/Ray"
def run(self, client, prompt, loop, aspect_ratio, save, filename):
def run(self, client, model, prompt, duration, loop, aspect_ratio, resolution, save, filename):
"""
Generate a video from a text prompt.
"""
@@ -117,24 +146,15 @@ class Text2Video:
raise ValueError("Prompt is required")
generation = client.generations.create(
prompt=prompt, loop=loop, aspect_ratio=aspect_ratio
prompt=prompt,
model=model,
loop=loop,
aspect_ratio=aspect_ratio,
duration=duration,
resolution=resolution,
)
generation_id = generation.id
completed = False
while not completed:
generation = client.generations.get(id=generation_id)
if generation.state == "completed":
completed = True
elif generation.state == "failed":
raise ValueError(f"Generation failed: {generation.failure_reason}")
time.sleep(3)
video_url = generation.assets.video
if save:
directory, filename = parse_filename(filename)
if filename == "":
filename = generation_id
download_file(video_url, os.path.join(self.output_dir, directory, filename + ".mp4"))
video_url = wait_for_generation(client, generation_id, save, filename, self.output_dir)
return {
"ui": {"text": [generation_id]},
@@ -148,6 +168,10 @@ class Text2Video:
class Image2Video:
def __init__(self):
self.output_dir = folder_paths.get_output_directory()
@classmethod
def IS_CHANGED(cls, *args, **kwargs):
return float("NaN")
@classmethod
def INPUT_TYPES(cls):
@@ -155,7 +179,10 @@ class Image2Video:
"required": {
"client": ("LUMACLIENT", {"forceInput": True}),
"prompt": ("STRING", {"multiline": True, "default": ""}),
"model": (["ray-flash-2", "ray-2", "ray-1.6"],),
"duration": (["5s", "9s"],),
"loop": ("BOOLEAN", {"default": False}),
"resolution": (["540p", "720p"],),
"save": ("BOOLEAN", {"default": True}),
},
"optional": {
@@ -168,13 +195,16 @@ class Image2Video:
RETURN_TYPES = ("STRING", "STRING")
RETURN_NAMES = ("video_url", "generation_id")
FUNCTION = "run"
CATEGORY = "LumaAI"
CATEGORY = "LumaAI/Ray"
def run(
self,
client,
model,
prompt,
duration,
loop,
resolution,
save,
init_image_url="",
final_image_url="",
@@ -193,24 +223,15 @@ class Image2Video:
keyframes["frame1"] = {"type": "image", "url": final_image_url}
generation = client.generations.create(
prompt=prompt, loop=loop, keyframes=keyframes
prompt=prompt,
model=model,
loop=loop,
duration=duration,
resolution=resolution,
keyframes=keyframes,
)
generation_id = generation.id
completed = False
while not completed:
generation = client.generations.get(id=generation_id)
if generation.state == "completed":
completed = True
elif generation.state == "failed":
raise ValueError(f"Generation failed: {generation.failure_reason}")
time.sleep(3)
video_url = generation.assets.video
if save:
directory, filename = parse_filename(filename)
if filename == "":
filename = generation_id
download_file(video_url, os.path.join(self.output_dir, directory, filename + ".mp4"))
video_url = wait_for_generation(client, generation_id, save, filename, self.output_dir)
return {
"ui": {"text": [generation_id]},
@@ -224,6 +245,10 @@ class Image2Video:
class InterpolateGenerations:
def __init__(self):
self.output_dir = folder_paths.get_output_directory()
@classmethod
def IS_CHANGED(cls, *args, **kwargs):
return float("NaN")
@classmethod
def INPUT_TYPES(cls):
@@ -231,6 +256,8 @@ class InterpolateGenerations:
"required": {
"client": ("LUMACLIENT", {"forceInput": True}),
"prompt": ("STRING", {"multiline": True, "default": ""}),
"model": (["ray-flash-2", "ray-2", "ray-1.6"],),
"resolution": (["540p", "720p"],),
"save": ("BOOLEAN", {"default": True}),
"generation_id_1": ("STRING", {"default": "", "forceInput": True}),
"generation_id_2": ("STRING", {"default": "", "forceInput": True}),
@@ -241,12 +268,14 @@ class InterpolateGenerations:
RETURN_TYPES = ("STRING", "STRING")
RETURN_NAMES = ("video_url", "generation_id")
FUNCTION = "run"
CATEGORY = "LumaAI"
CATEGORY = "LumaAI/Ray"
def run(
self,
client,
model,
prompt,
resolution,
save,
generation_id_1,
generation_id_2,
@@ -264,23 +293,11 @@ class InterpolateGenerations:
"frame0": {"type": "generation", "id": generation_id_1},
"frame1": {"type": "generation", "id": generation_id_2},
},
model=model,
resolution=resolution,
)
generation_id = generation.id
completed = False
while not completed:
generation = client.generations.get(id=generation_id)
if generation.state == "completed":
completed = True
elif generation.state == "failed":
raise ValueError(f"Generation failed: {generation.failure_reason}")
time.sleep(3)
video_url = generation.assets.video
if save:
directory, filename = parse_filename(filename)
if filename == "":
filename = generation_id
download_file(video_url, os.path.join(self.output_dir, directory, filename + ".mp4"))
video_url = wait_for_generation(client, generation_id, save, filename, self.output_dir)
return {
"ui": {"text": [generation_id]},
@@ -294,6 +311,10 @@ class InterpolateGenerations:
class ExtendGeneration:
def __init__(self):
self.output_dir = folder_paths.get_output_directory()
@classmethod
def IS_CHANGED(cls, *args, **kwargs):
return float("NaN")
@classmethod
def INPUT_TYPES(cls):
@@ -301,6 +322,9 @@ class ExtendGeneration:
"required": {
"client": ("LUMACLIENT", {"forceInput": True}),
"prompt": ("STRING", {"multiline": True, "default": ""}),
"model": (["ray-flash-2", "ray-2", "ray-1.6"],),
"loop": ("BOOLEAN", {"default": False}),
"resolution": (["540p", "720p"],),
"save": ("BOOLEAN", {"default": True}),
},
"optional": {
@@ -315,12 +339,15 @@ class ExtendGeneration:
RETURN_TYPES = ("STRING", "STRING")
RETURN_NAMES = ("video_url", "generation_id")
FUNCTION = "run"
CATEGORY = "LumaAI"
CATEGORY = "LumaAI/Ray"
def run(
self,
client,
model,
prompt,
loop,
resolution,
save,
init_image_url="",
final_image_url="",
@@ -352,29 +379,121 @@ class ExtendGeneration:
if final_generation_id != "":
keyframes["frame1"] = {"type": "generation", "id": final_generation_id}
generation = client.generations.create(prompt=prompt, keyframes=keyframes)
new_generation_id = generation.id
completed = False
while not completed:
generation = client.generations.get(id=new_generation_id)
if generation.state == "completed":
completed = True
elif generation.state == "failed":
raise ValueError(f"Generation failed: {generation.failure_reason}")
time.sleep(3)
video_url = generation.assets.video
if save:
directory, filename = parse_filename(filename)
if filename == "":
filename = new_generation_id
download_file(video_url, os.path.join(self.output_dir, directory, filename + ".mp4"))
generation = client.generations.create(
prompt=prompt,
model=model,
loop=loop,
resolution=resolution,
keyframes=keyframes,
)
generation_id = generation.id
video_url = wait_for_generation(client, generation_id, save, filename, self.output_dir)
return {
"ui": {"text": [new_generation_id]},
"ui": {"text": [generation_id]},
"result": (
video_url,
new_generation_id,
generation_id,
),
}
class UpscaleGeneration:
def __init__(self):
self.output_dir = folder_paths.get_output_directory()
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"client": ("LUMACLIENT", {"forceInput": True}),
"generation_id": ("STRING", {"default": "", "forceInput": True}),
"resolution": (["540p", "720p", "1080p", "4k"],),
"save": ("BOOLEAN", {"default": True}),
},
"optional": {
"filename": ("STRING", {"default": ""}),
},
}
RETURN_TYPES = ("STRING", "STRING")
RETURN_NAMES = ("video_url", "generation_id")
FUNCTION = "run"
CATEGORY = "LumaAI/Upscale"
def run(
self,
client,
generation_id,
resolution,
save,
filename="",
):
"""
Upscale a generation.
"""
generation = client.generations.upscale(id=generation_id, resolution=resolution)
upscaled_generation_id = generation.id
video_url = wait_for_generation(client, upscaled_generation_id, save, filename, self.output_dir)
return {
"ui": {"text": [upscaled_generation_id]},
"result": (
video_url,
upscaled_generation_id,
),
}
class AddAudio2Video:
def __init__(self):
self.output_dir = folder_paths.get_output_directory()
@classmethod
def IS_CHANGED(cls, *args, **kwargs):
return float("NaN")
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"client": ("LUMACLIENT", {"forceInput": True}),
"generation_id": ("STRING", {"default": "", "forceInput": True}),
"prompt": ("STRING", {"multiline": True, "default": ""}),
"negative_prompt": ("STRING", {"multiline": True, "default": ""}),
"save": ("BOOLEAN", {"default": True}),
},
"optional": {
"filename": ("STRING", {"default": ""}),
},
}
RETURN_TYPES = ("STRING", "STRING")
RETURN_NAMES = ("video_url", "generation_id")
FUNCTION = "run"
CATEGORY = "LumaAI/Audio"
def run(
self,
client,
generation_id,
prompt,
negative_prompt,
save,
filename="",
):
"""
Upscale a generation.
"""
generation = client.generations.audio(id=generation_id, prompt=prompt, negative_prompt=negative_prompt)
with_audio_generation_id = generation.id
video_url = wait_for_generation(client, with_audio_generation_id, save, filename, self.output_dir)
return {
"ui": {"text": [with_audio_generation_id]},
"result": (
video_url,
with_audio_generation_id,
),
}
@@ -388,10 +507,264 @@ class PreviewVideo:
}
}
@classmethod
def IS_CHANGED(cls, *args, **kwargs):
return float("NaN")
OUTPUT_NODE = True
FUNCTION = "run"
CATEGORY = "LumaAI"
CATEGORY = "LumaAI/Utils"
RETURN_TYPES = ()
def run(self, video_url):
return {"ui": {"video_url": [video_url]}}
class Reference:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"image_url": ("STRING", {"forceInput": True}),
"weight": (
"FLOAT",
{"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01},
),
}
}
RETURN_TYPES = ("REFERENCE",)
RETURN_NAMES = ("reference",)
FUNCTION = "run"
CATEGORY = "LumaAI/Photon"
def run(self, image_url, weight):
"""
Create a reference from an image URL and a weight.
"""
return ({"url": image_url, "weight": weight},)
class ConcatReferences:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"reference_1": ("REFERENCE", {"forceInput": True}),
"reference_2": ("REFERENCE", {"forceInput": True}),
"reference_3": ("REFERENCE", {"forceInput": True}),
"reference_4": ("REFERENCE", {"forceInput": True}),
},
}
RETURN_TYPES = ("CONCAT_REFERENCES",)
RETURN_NAMES = ("concat_references",)
FUNCTION = "run"
CATEGORY = "LumaAI/Photon"
def run(
self, reference_1=None, reference_2=None, reference_3=None, reference_4=None
):
"""
Concatenate a list of references.
"""
references = []
for reference in [reference_1, reference_2, reference_3, reference_4]:
if reference is not None:
references.append(reference)
if len(references) == 0:
raise ValueError("You must provide at least one reference")
return (references,)
class CharacterReference:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"character_image_url_1": ("STRING", {"forceInput": True}),
"character_image_url_2": ("STRING", {"forceInput": True}),
"character_image_url_3": ("STRING", {"forceInput": True}),
"character_image_url_4": ("STRING", {"forceInput": True}),
},
}
RETURN_TYPES = ("CHARACTER_REFERENCE",)
RETURN_NAMES = ("character_reference",)
FUNCTION = "run"
CATEGORY = "LumaAI/Photon"
def run(
self,
character_image_url_1=None,
character_image_url_2=None,
character_image_url_3=None,
character_image_url_4=None,
):
"""
Create a character reference from a list of image URLs.
"""
urls = []
for url in [
character_image_url_1,
character_image_url_2,
character_image_url_3,
character_image_url_4,
]:
if url is not None:
urls.append(url)
if len(urls) == 0:
raise ValueError("You must provide at least one character image URL")
character_reference = {"identity0": {"images": urls}}
return (character_reference,)
class ImageGeneration:
def __init__(self):
self.output_dir = folder_paths.get_output_directory()
@classmethod
def IS_CHANGED(cls, *args, **kwargs):
return float("NaN")
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"client": ("LUMACLIENT", {"forceInput": True}),
"model": (["photon-1", "photon-flash-1"],),
"prompt": ("STRING", {"forceInput": True, }),
"aspect_ratio": (["9:16", "3:4", "1:1", "4:3", "16:9", "21:9"],),
},
"optional": {
"image_ref": ("CONCAT_REFERENCES", {"forceInput": True}),
"style_ref": ("REFERENCE", {"forceInput": True}),
"character_ref": ("CHARACTER_REFERENCE", {"forceInput": True}),
"filename": ("STRING", {"default": ""}),
},
}
RETURN_TYPES = ("STRING", "STRING", "IMAGE")
RETURN_NAMES = ("image_url", "generation_id", "image")
OUTPUT_NODE = True
FUNCTION = "run"
CATEGORY = "LumaAI/Photon"
def run(
self,
client,
model,
prompt,
aspect_ratio,
image_ref=None,
style_ref=None,
character_ref=None,
filename="",
):
"""
Generate an image from a text prompt and optional references.
"""
if style_ref is not None:
style_ref = [style_ref]
generation = client.generations.image.create(
prompt=prompt,
model=model,
aspect_ratio=aspect_ratio,
image_ref=image_ref,
style_ref=style_ref,
character_ref=character_ref,
)
generation_id = generation.id
completed = False
while not completed:
generation = client.generations.get(id=generation_id)
if generation.state == "completed":
completed = True
elif generation.state == "failed":
raise ValueError(f"Generation failed: {generation.failure_reason}")
time.sleep(1)
image_url = generation.assets.image
directory, filename = parse_filename(filename)
if filename == "":
filename = generation_id
download_file(
image_url, os.path.join(self.output_dir, directory, filename + ".jpg")
)
image, _ = nodes.LoadImage().load_image(
os.path.join(self.output_dir, directory, filename + ".jpg")
)
return {
"ui": {"text": [generation_id]},
"result": (image_url, generation_id, image),
}
class ModifyImage:
def __init__(self):
self.output_dir = folder_paths.get_output_directory()
@classmethod
def IS_CHANGED(cls, *args, **kwargs):
return float("NaN")
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"client": ("LUMACLIENT", {"forceInput": True}),
"model": (["photon-1", "photon-flash-1"],),
"prompt": ("STRING", {"forceInput": True, }),
"modify_image_ref": ("REFERENCE", {"forceInput": True}),
},
}
RETURN_TYPES = ("STRING", "STRING", "IMAGE")
RETURN_NAMES = ("image_url", "generation_id", "image")
OUTPUT_NODE = True
FUNCTION = "run"
CATEGORY = "LumaAI/Photon"
def run(self, client, model, prompt, modify_image_ref, filename=""):
"""
Modify an image.
"""
generation = client.generations.image.create(
prompt=prompt,
model=model,
modify_image_ref=modify_image_ref,
)
generation_id = generation.id
completed = False
while not completed:
generation = client.generations.get(id=generation_id)
if generation.state == "completed":
completed = True
elif generation.state == "failed":
raise ValueError(f"Generation failed: {generation.failure_reason}")
time.sleep(3)
image_url = generation.assets.image
directory, filename = parse_filename(filename)
if filename == "":
filename = generation_id
download_file(
image_url, os.path.join(self.output_dir, directory, filename + ".jpg")
)
image, _ = nodes.LoadImage().load_image(
os.path.join(self.output_dir, directory, filename + ".jpg")
)
return {
"ui": {"text": [generation_id]},
"result": (image_url, generation_id, image),
}
+2 -2
View File
@@ -1,9 +1,9 @@
[project]
name = "comfyui-lumaai-api"
description = "This custom node allows you to use the LumaAI API."
version = "1.0.2"
version = "1.1.0"
license = {file = "LICENSE"}
dependencies = ["lumaai", "requests", "numpy", "Pillow"]
dependencies = ["lumaai==1.7.0", "requests", "numpy", "Pillow"]
[project.urls]
Repository = "https://github.com/lumalabs/ComfyUI-LumaAI-API"
+1 -1
View File
@@ -1,4 +1,4 @@
lumaai
lumaai==1.7.0
requests
numpy
Pillow
+9 -3
View File
@@ -7,7 +7,13 @@ import { ComfyWidgets } from "../../../scripts/widgets.js";
app.registerExtension({
name: "lumaai.showgenerationid",
async beforeRegisterNodeDef(nodeType, nodeData, app) {
if (nodeData.name == "LumaText2Video" || nodeData.name == "LumaImage2Video" || nodeData.name == "LumaInterpolateGenerations" || nodeData.name == "LumaExtendGeneration") {
if (nodeData.name == "LumaText2Video"
|| nodeData.name == "LumaImage2Video"
|| nodeData.name == "LumaInterpolateGenerations"
|| nodeData.name == "LumaExtendGeneration"
|| nodeData.name == "LumaImageGeneration"
|| nodeData.name == "LumaModifyImage"
) {
function populate(text) {
const v = [...text];
if (this.widgets) {
@@ -31,7 +37,7 @@ app.registerExtension({
sz[1] = this.size[1];
}
this.onResize?.(sz);
app.graph.setDirtyCanvas(true, false);
app.graph.setDirtyCanvas(true, true);
});
}
@@ -49,4 +55,4 @@ app.registerExtension({
};
}
},
});
});
+227
View File
@@ -0,0 +1,227 @@
{
"last_node_id": 5,
"last_link_id": 5,
"nodes": [
{
"id": 2,
"type": "LumaText2Video",
"pos": [
460.42974853515625,
274.5
],
"size": [
400,
364
],
"flags": {},
"order": 1,
"mode": 0,
"inputs": [
{
"name": "client",
"type": "LUMACLIENT",
"link": 1
}
],
"outputs": [
{
"name": "video_url",
"type": "STRING",
"links": []
},
{
"name": "generation_id",
"type": "STRING",
"links": [
3
],
"slot_index": 1
}
],
"properties": {
"Node name for S&R": "LumaText2Video"
},
"widgets_values": [
"ray-flash-2",
"a car",
"5s",
false,
"9:16",
"540p",
true,
"",
"ee4e9dad-9f1e-4e5e-bc37-1b46e9825750",
"ee4e9dad-9f1e-4e5e-bc37-1b46e9825750"
]
},
{
"id": 1,
"type": "LumaAddAudio2Video",
"pos": [
1003.0374755859375,
442.3060607910156
],
"size": [
400,
200
],
"flags": {},
"order": 2,
"mode": 0,
"inputs": [
{
"name": "client",
"type": "LUMACLIENT",
"link": 5
},
{
"name": "generation_id",
"type": "STRING",
"link": 3,
"widget": {
"name": "generation_id"
}
}
],
"outputs": [
{
"name": "video_url",
"type": "STRING",
"links": [
4
]
},
{
"name": "generation_id",
"type": "STRING",
"links": null
}
],
"properties": {
"Node name for S&R": "LumaAddAudio2Video"
},
"widgets_values": [
"",
"noise from a car",
"",
true,
""
]
},
{
"id": 5,
"type": "LumaPreviewVideo",
"pos": [
1583.6092529296875,
145.96243286132812
],
"size": [
768,
768
],
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"name": "video_url",
"type": "STRING",
"link": 4,
"widget": {
"name": "video_url"
}
}
],
"outputs": [],
"properties": {
"Node name for S&R": "LumaPreviewVideo"
},
"widgets_values": [
"",
null
]
},
{
"id": 3,
"type": "LumaAIClient",
"pos": [
32.32486343383789,
344.1593933105469
],
"size": [
315,
58
],
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "client",
"type": "LUMACLIENT",
"links": [
1,
5
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "LumaAIClient"
},
"widgets_values": [
""
]
}
],
"links": [
[
1,
3,
0,
2,
0,
"LUMACLIENT"
],
[
3,
2,
1,
1,
1,
"STRING"
],
[
4,
1,
0,
5,
0,
"STRING"
],
[
5,
3,
0,
1,
0,
"LUMACLIENT"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 0.5559917313492242,
"offset": [
230.56296338473092,
95.73040355967112
]
},
"node_versions": {
"ComfyUI-LumaAI-API": "a53576c98bcffffca5474451a8ca5de4faf92943"
}
},
"version": 0.4
}
+150
View File
@@ -0,0 +1,150 @@
{
"last_node_id": 31,
"last_link_id": 18,
"nodes": [
{
"id": 30,
"type": "LumaPreviewVideo",
"pos": [
1033,
378
],
"size": [
768,
768
],
"flags": {},
"order": 2,
"mode": 0,
"inputs": [
{
"name": "video_url",
"type": "STRING",
"link": 18,
"widget": {
"name": "video_url"
}
}
],
"outputs": [],
"properties": {
"Node name for S&R": "LumaPreviewVideo"
},
"widgets_values": [
""
]
},
{
"id": 29,
"type": "LumaAIClient",
"pos": [
124,
375
],
"size": [
315,
58
],
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "client",
"type": "LUMACLIENT",
"links": [
17
],
"slot_index": 0,
"shape": 3
}
],
"properties": {
"Node name for S&R": "LumaAIClient"
},
"widgets_values": [
""
]
},
{
"id": 31,
"type": "Ray2Text2Video",
"pos": [
503.9627990722656,
379.0370178222656
],
"size": [
400,
240
],
"flags": {},
"order": 1,
"mode": 0,
"inputs": [
{
"name": "client",
"type": "LUMACLIENT",
"link": 17
}
],
"outputs": [
{
"name": "video_url",
"type": "STRING",
"links": [
18
],
"slot_index": 0
},
{
"name": "generation_id",
"type": "STRING",
"links": null
}
],
"properties": {
"Node name for S&R": "Ray2Text2Video"
},
"widgets_values": [
"",
"5s",
"720p",
false,
"9:16",
true,
""
]
}
],
"links": [
[
17,
29,
0,
31,
0,
"LUMACLIENT"
],
[
18,
31,
0,
30,
0,
"STRING"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 0.9090909090909091,
"offset": [
-21.961315505259062,
-186.1272521835087
]
}
},
"version": 0.4
}
+227
View File
@@ -0,0 +1,227 @@
{
"last_node_id": 6,
"last_link_id": 9,
"nodes": [
{
"id": 5,
"type": "LumaPreviewVideo",
"pos": [
1583.6092529296875,
145.96243286132812
],
"size": [
768,
768
],
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"name": "video_url",
"type": "STRING",
"link": 8,
"widget": {
"name": "video_url"
}
}
],
"outputs": [],
"properties": {
"Node name for S&R": "LumaPreviewVideo"
},
"widgets_values": [
"",
null
]
},
{
"id": 2,
"type": "LumaText2Video",
"pos": [
460.42974853515625,
274.5
],
"size": [
400,
364
],
"flags": {},
"order": 1,
"mode": 0,
"inputs": [
{
"name": "client",
"type": "LUMACLIENT",
"link": 1
}
],
"outputs": [
{
"name": "video_url",
"type": "STRING",
"links": []
},
{
"name": "generation_id",
"type": "STRING",
"links": [
7
],
"slot_index": 1
}
],
"properties": {
"Node name for S&R": "LumaText2Video"
},
"widgets_values": [
"ray-flash-2",
"a car",
"5s",
false,
"9:16",
"540p",
true,
"",
"24ed0260-bc59-419f-8f10-794d291176d0",
"24ed0260-bc59-419f-8f10-794d291176d0"
]
},
{
"id": 6,
"type": "LumaUpscaleGeneration",
"pos": [
1056.1300048828125,
351.3099365234375
],
"size": [
315,
150
],
"flags": {},
"order": 2,
"mode": 0,
"inputs": [
{
"name": "client",
"type": "LUMACLIENT",
"link": 9
},
{
"name": "generation_id",
"type": "STRING",
"link": 7,
"widget": {
"name": "generation_id"
}
}
],
"outputs": [
{
"name": "video_url",
"type": "STRING",
"links": [
8
],
"slot_index": 0
},
{
"name": "generation_id",
"type": "STRING",
"links": null
}
],
"properties": {
"Node name for S&R": "LumaUpscaleGeneration"
},
"widgets_values": [
"",
"1080p",
true,
""
]
},
{
"id": 3,
"type": "LumaAIClient",
"pos": [
32.32486343383789,
344.1593933105469
],
"size": [
315,
58
],
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "client",
"type": "LUMACLIENT",
"links": [
1,
9
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "LumaAIClient"
},
"widgets_values": [
""
]
}
],
"links": [
[
1,
3,
0,
2,
0,
"LUMACLIENT"
],
[
7,
2,
1,
6,
1,
"STRING"
],
[
8,
6,
0,
5,
0,
"STRING"
],
[
9,
3,
0,
6,
0,
"LUMACLIENT"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 0.9849732675807652,
"offset": [
-92.60513941852872,
-55.773528450482495
]
},
"node_versions": {
"ComfyUI-LumaAI-API": "a53576c98bcffffca5474451a8ca5de4faf92943"
}
},
"version": 0.4
}
+385
View File
@@ -0,0 +1,385 @@
{
"last_node_id": 8,
"last_link_id": 8,
"nodes": [
{
"id": 5,
"type": "LoadImage",
"pos": [
-309,
482
],
"size": [
315,
314.00002670288086
],
"flags": {},
"order": 0,
"mode": 0,
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
3
],
"shape": 3,
"slot_index": 0
},
{
"name": "MASK",
"type": "MASK",
"links": null,
"shape": 3
}
],
"properties": {
"Node name for S&R": "LoadImage"
},
"widgets_values": [
"00014.png",
"image"
]
},
{
"id": 2,
"type": "PreviewImage",
"pos": [
1248,
237
],
"size": {
"0": 210,
"1": 26
},
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 1
}
],
"properties": {
"Node name for S&R": "PreviewImage"
}
},
{
"id": 1,
"type": "LumaImageGeneration",
"pos": [
772,
199
],
"size": [
342.5999755859375,
190
],
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"name": "client",
"type": "LUMACLIENT",
"link": 2
},
{
"name": "image_ref",
"type": "CONCAT_REFERENCES",
"link": null
},
{
"name": "style_ref",
"type": "REFERENCE",
"link": null
},
{
"name": "character_ref",
"type": "CHARACTER_REFERENCE",
"link": 8
},
{
"name": "prompt",
"type": "STRING",
"link": 6,
"widget": {
"name": "prompt"
}
}
],
"outputs": [
{
"name": "image_url",
"type": "STRING",
"links": null,
"shape": 3
},
{
"name": "generation_id",
"type": "STRING",
"links": null,
"shape": 3
},
{
"name": "image",
"type": "IMAGE",
"links": [
1
],
"shape": 3,
"slot_index": 2
}
],
"properties": {
"Node name for S&R": "LumaImageGeneration"
},
"widgets_values": [
"photon-1",
"",
"9:16",
""
]
},
{
"id": 3,
"type": "LumaAIClient",
"pos": [
380,
195
],
"size": {
"0": 315,
"1": 58
},
"flags": {},
"order": 1,
"mode": 0,
"outputs": [
{
"name": "client",
"type": "LUMACLIENT",
"links": [
2
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "LumaAIClient"
},
"widgets_values": [
""
]
},
{
"id": 7,
"type": "PrimitiveNode",
"pos": [
392,
318
],
"size": {
"0": 210,
"1": 58
},
"flags": {},
"order": 2,
"mode": 0,
"outputs": [
{
"name": "STRING",
"type": "STRING",
"links": [
6
],
"slot_index": 0,
"widget": {
"name": "prompt"
}
}
],
"properties": {
"Run widget replace on values": false
},
"widgets_values": [
""
]
},
{
"id": 4,
"type": "ImgBBUpload",
"pos": [
58,
482
],
"size": {
"0": 315,
"1": 106
},
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"name": "image",
"type": "IMAGE",
"link": 3
}
],
"outputs": [
{
"name": "image_url",
"type": "STRING",
"links": [
7
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "ImgBBUpload"
},
"widgets_values": [
"",
false,
60
]
},
{
"id": 8,
"type": "CharacterReference",
"pos": [
406,
485
],
"size": [
346,
94
],
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"name": "character_image_url_1",
"type": "STRING",
"link": 7,
"widget": {
"name": "character_image_url_1"
}
},
{
"name": "character_image_url_2",
"type": "STRING",
"link": null,
"widget": {
"name": "character_image_url_2"
}
},
{
"name": "character_image_url_3",
"type": "STRING",
"link": null,
"widget": {
"name": "character_image_url_3"
}
},
{
"name": "character_image_url_4",
"type": "STRING",
"link": null,
"widget": {
"name": "character_image_url_4"
}
}
],
"outputs": [
{
"name": "character_reference",
"type": "CHARACTER_REFERENCE",
"links": [
8
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "CharacterReference"
},
"widgets_values": [
"",
"",
"",
""
]
}
],
"links": [
[
1,
1,
2,
2,
0,
"IMAGE"
],
[
2,
3,
0,
1,
0,
"LUMACLIENT"
],
[
3,
5,
0,
4,
0,
"IMAGE"
],
[
6,
7,
0,
1,
4,
"STRING"
],
[
7,
4,
0,
8,
0,
"STRING"
],
[
8,
8,
0,
1,
3,
"CHARACTER_REFERENCE"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 0.8264462809917354,
"offset": [
357.39093750000023,
135.43187500000013
]
}
},
"version": 0.4
}
+418
View File
@@ -0,0 +1,418 @@
{
"last_node_id": 10,
"last_link_id": 11,
"nodes": [
{
"id": 5,
"type": "LoadImage",
"pos": [
-309,
482
],
"size": [
315,
314.00002670288086
],
"flags": {},
"order": 0,
"mode": 0,
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
3
],
"shape": 3,
"slot_index": 0
},
{
"name": "MASK",
"type": "MASK",
"links": null,
"shape": 3
}
],
"properties": {
"Node name for S&R": "LoadImage"
},
"widgets_values": [
"00014.png",
"image"
]
},
{
"id": 2,
"type": "PreviewImage",
"pos": [
1248,
237
],
"size": {
"0": 210,
"1": 26
},
"flags": {},
"order": 7,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 1
}
],
"properties": {
"Node name for S&R": "PreviewImage"
}
},
{
"id": 3,
"type": "LumaAIClient",
"pos": [
380,
195
],
"size": {
"0": 315,
"1": 58
},
"flags": {},
"order": 1,
"mode": 0,
"outputs": [
{
"name": "client",
"type": "LUMACLIENT",
"links": [
2
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "LumaAIClient"
},
"widgets_values": [
""
]
},
{
"id": 7,
"type": "PrimitiveNode",
"pos": [
392,
318
],
"size": {
"0": 210,
"1": 58
},
"flags": {},
"order": 2,
"mode": 0,
"outputs": [
{
"name": "STRING",
"type": "STRING",
"links": [
6
],
"slot_index": 0,
"widget": {
"name": "prompt"
}
}
],
"properties": {
"Run widget replace on values": false
},
"widgets_values": [
""
]
},
{
"id": 4,
"type": "ImgBBUpload",
"pos": [
58,
482
],
"size": {
"0": 315,
"1": 106
},
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"name": "image",
"type": "IMAGE",
"link": 3
}
],
"outputs": [
{
"name": "image_url",
"type": "STRING",
"links": [
9
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "ImgBBUpload"
},
"widgets_values": [
"",
false,
60
]
},
{
"id": 9,
"type": "Reference",
"pos": [
473,
702
],
"size": [
315,
82
],
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"name": "image_url",
"type": "STRING",
"link": 9,
"widget": {
"name": "image_url"
}
}
],
"outputs": [
{
"name": "reference",
"type": "REFERENCE",
"links": [
10
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "Reference"
},
"widgets_values": [
"",
1
]
},
{
"id": 10,
"type": "ConcatReferences",
"pos": [
437,
496
],
"size": {
"0": 367.79998779296875,
"1": 86
},
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"name": "reference_1",
"type": "REFERENCE",
"link": 10
},
{
"name": "reference_2",
"type": "REFERENCE",
"link": null
},
{
"name": "reference_3",
"type": "REFERENCE",
"link": null
},
{
"name": "reference_4",
"type": "REFERENCE",
"link": null
}
],
"outputs": [
{
"name": "concat_references",
"type": "CONCAT_REFERENCES",
"links": [
11
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "ConcatReferences"
}
},
{
"id": 1,
"type": "LumaImageGeneration",
"pos": [
833,
197
],
"size": [
342.5999755859375,
190
],
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"name": "client",
"type": "LUMACLIENT",
"link": 2
},
{
"name": "image_ref",
"type": "CONCAT_REFERENCES",
"link": 11
},
{
"name": "style_ref",
"type": "REFERENCE",
"link": null
},
{
"name": "character_ref",
"type": "CHARACTER_REFERENCE",
"link": null
},
{
"name": "prompt",
"type": "STRING",
"link": 6,
"widget": {
"name": "prompt"
}
}
],
"outputs": [
{
"name": "image_url",
"type": "STRING",
"links": null,
"shape": 3
},
{
"name": "generation_id",
"type": "STRING",
"links": null,
"shape": 3
},
{
"name": "image",
"type": "IMAGE",
"links": [
1
],
"shape": 3,
"slot_index": 2
}
],
"properties": {
"Node name for S&R": "LumaImageGeneration"
},
"widgets_values": [
"photon-1",
"",
"9:16",
""
]
}
],
"links": [
[
1,
1,
2,
2,
0,
"IMAGE"
],
[
2,
3,
0,
1,
0,
"LUMACLIENT"
],
[
3,
5,
0,
4,
0,
"IMAGE"
],
[
6,
7,
0,
1,
4,
"STRING"
],
[
9,
4,
0,
9,
0,
"STRING"
],
[
10,
9,
0,
10,
0,
"REFERENCE"
],
[
11,
10,
0,
1,
1,
"CONCAT_REFERENCES"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 0.8264462809917354,
"offset": [
357.39093750000023,
135.15300781250014
]
}
},
"version": 0.4
}
+297
View File
@@ -0,0 +1,297 @@
{
"last_node_id": 11,
"last_link_id": 14,
"nodes": [
{
"id": 11,
"type": "PreviewImage",
"pos": [
1127,
122
],
"size": [
210,
246
],
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 14,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "PreviewImage"
}
},
{
"id": 8,
"type": "LumaModifyImage",
"pos": [
641,
120
],
"size": [
380.4000244140625,
122
],
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"name": "client",
"type": "LUMACLIENT",
"link": 10,
"slot_index": 0
},
{
"name": "modify_image_ref",
"type": "REFERENCE",
"link": 12
},
{
"name": "prompt",
"type": "STRING",
"link": 13,
"widget": {
"name": "prompt"
}
}
],
"outputs": [
{
"name": "image_url",
"type": "STRING",
"links": null,
"shape": 3
},
{
"name": "generation_id",
"type": "STRING",
"links": null,
"shape": 3
},
{
"name": "image",
"type": "IMAGE",
"links": [
14
],
"shape": 3
}
],
"properties": {
"Node name for S&R": "LumaModifyImage"
},
"widgets_values": [
"photon-1",
"transform all the flowers to sunflowers"
]
},
{
"id": 9,
"type": "Reference",
"pos": [
248,
194
],
"size": [
315,
82
],
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"name": "image_url",
"type": "STRING",
"link": 11,
"widget": {
"name": "image_url"
},
"slot_index": 0
}
],
"outputs": [
{
"name": "reference",
"type": "REFERENCE",
"links": [
12
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "Reference"
},
"widgets_values": [
"https://staging.storage.cdn-luma.com/dream_machine/400460d3-cc24-47ae-a015-d4d1c6296aba/38cc78d7-95aa-4e6e-b1ac-4123ce24725e_image0c73fa8a463114bf89e30892a301c532e.jpg",
0.5
]
},
{
"id": 5,
"type": "PrimitiveNode",
"pos": [
2,
199
],
"size": {
"0": 210,
"1": 58
},
"flags": {},
"order": 0,
"mode": 0,
"outputs": [
{
"name": "STRING",
"type": "STRING",
"links": [
11
],
"slot_index": 0,
"widget": {
"name": "image_url"
}
}
],
"properties": {
"Run widget replace on values": false
},
"widgets_values": [
"https://staging.storage.cdn-luma.com/dream_machine/400460d3-cc24-47ae-a015-d4d1c6296aba/38cc78d7-95aa-4e6e-b1ac-4123ce24725e_image0c73fa8a463114bf89e30892a301c532e.jpg"
]
},
{
"id": 10,
"type": "PrimitiveNode",
"pos": [
120,
334
],
"size": [
442.6914007018429,
133.1652411475094
],
"flags": {},
"order": 1,
"mode": 0,
"outputs": [
{
"name": "STRING",
"type": "STRING",
"links": [
13
],
"slot_index": 0,
"widget": {
"name": "prompt"
}
}
],
"properties": {
"Run widget replace on values": false
},
"widgets_values": [
"transform all the flowers to sunflowers"
]
},
{
"id": 1,
"type": "LumaAIClient",
"pos": [
246,
66
],
"size": {
"0": 315,
"1": 58
},
"flags": {
"collapsed": false
},
"order": 2,
"mode": 0,
"outputs": [
{
"name": "client",
"type": "LUMACLIENT",
"links": [
10
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "LumaAIClient"
},
"widgets_values": [
""
]
}
],
"links": [
[
10,
1,
0,
8,
0,
"LUMACLIENT"
],
[
11,
5,
0,
9,
0,
"STRING"
],
[
12,
9,
0,
8,
1,
"REFERENCE"
],
[
13,
10,
0,
8,
2,
"STRING"
],
[
14,
8,
2,
11,
0,
"IMAGE"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 1.2100000000000002,
"offset": [
75.7556407983373,
175.25858695772177
]
}
},
"version": 0.4
}
+359
View File
@@ -0,0 +1,359 @@
{
"last_node_id": 7,
"last_link_id": 6,
"nodes": [
{
"id": 5,
"type": "LoadImage",
"pos": [
-309,
482
],
"size": [
315,
314.00002670288086
],
"flags": {},
"order": 0,
"mode": 0,
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
3
],
"shape": 3,
"slot_index": 0
},
{
"name": "MASK",
"type": "MASK",
"links": null,
"shape": 3
}
],
"properties": {
"Node name for S&R": "LoadImage"
},
"widgets_values": [
"00014.png",
"image"
]
},
{
"id": 4,
"type": "ImgBBUpload",
"pos": [
58,
482
],
"size": {
"0": 315,
"1": 106
},
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"name": "image",
"type": "IMAGE",
"link": 3
}
],
"outputs": [
{
"name": "image_url",
"type": "STRING",
"links": [
4
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "ImgBBUpload"
},
"widgets_values": [
"",
false,
60
]
},
{
"id": 2,
"type": "PreviewImage",
"pos": [
1248,
237
],
"size": {
"0": 210,
"1": 26
},
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 1
}
],
"properties": {
"Node name for S&R": "PreviewImage"
}
},
{
"id": 1,
"type": "LumaImageGeneration",
"pos": [
772,
199
],
"size": [
342.5999755859375,
190
],
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"name": "client",
"type": "LUMACLIENT",
"link": 2
},
{
"name": "image_ref",
"type": "CONCAT_REFERENCES",
"link": null
},
{
"name": "style_ref",
"type": "REFERENCE",
"link": 5
},
{
"name": "character_ref",
"type": "CHARACTER_REFERENCE",
"link": null
},
{
"name": "prompt",
"type": "STRING",
"link": 6,
"widget": {
"name": "prompt"
}
}
],
"outputs": [
{
"name": "image_url",
"type": "STRING",
"links": null,
"shape": 3
},
{
"name": "generation_id",
"type": "STRING",
"links": null,
"shape": 3
},
{
"name": "image",
"type": "IMAGE",
"links": [
1
],
"shape": 3,
"slot_index": 2
}
],
"properties": {
"Node name for S&R": "LumaImageGeneration"
},
"widgets_values": [
"photon-1",
"",
"9:16",
""
]
},
{
"id": 3,
"type": "LumaAIClient",
"pos": [
380,
195
],
"size": {
"0": 315,
"1": 58
},
"flags": {},
"order": 1,
"mode": 0,
"outputs": [
{
"name": "client",
"type": "LUMACLIENT",
"links": [
2
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "LumaAIClient"
},
"widgets_values": [
""
]
},
{
"id": 6,
"type": "Reference",
"pos": [
400,
489
],
"size": [
315,
82
],
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"name": "image_url",
"type": "STRING",
"link": 4,
"widget": {
"name": "image_url"
}
}
],
"outputs": [
{
"name": "reference",
"type": "REFERENCE",
"links": [
5
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "Reference"
},
"widgets_values": [
"",
1
]
},
{
"id": 7,
"type": "PrimitiveNode",
"pos": [
392,
318
],
"size": {
"0": 210,
"1": 58
},
"flags": {},
"order": 2,
"mode": 0,
"outputs": [
{
"name": "STRING",
"type": "STRING",
"links": [
6
],
"slot_index": 0,
"widget": {
"name": "prompt"
}
}
],
"properties": {
"Run widget replace on values": false
},
"widgets_values": [
""
]
}
],
"links": [
[
1,
1,
2,
2,
0,
"IMAGE"
],
[
2,
3,
0,
1,
0,
"LUMACLIENT"
],
[
3,
5,
0,
4,
0,
"IMAGE"
],
[
4,
4,
0,
6,
0,
"STRING"
],
[
5,
6,
0,
1,
2,
"REFERENCE"
],
[
6,
7,
0,
1,
4,
"STRING"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 0.8264462809917354,
"offset": [
357.39093750000023,
135.43187500000013
]
}
},
"version": 0.4
}
+212
View File
@@ -0,0 +1,212 @@
{
"last_node_id": 10,
"last_link_id": 11,
"nodes": [
{
"id": 2,
"type": "PreviewImage",
"pos": [
1248,
237
],
"size": {
"0": 210,
"1": 26
},
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 1
}
],
"properties": {
"Node name for S&R": "PreviewImage"
}
},
{
"id": 3,
"type": "LumaAIClient",
"pos": [
380,
195
],
"size": {
"0": 315,
"1": 58
},
"flags": {},
"order": 0,
"mode": 0,
"outputs": [
{
"name": "client",
"type": "LUMACLIENT",
"links": [
2
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "LumaAIClient"
},
"widgets_values": [
""
]
},
{
"id": 7,
"type": "PrimitiveNode",
"pos": [
392,
318
],
"size": {
"0": 210,
"1": 58
},
"flags": {},
"order": 1,
"mode": 0,
"outputs": [
{
"name": "STRING",
"type": "STRING",
"links": [
6
],
"slot_index": 0,
"widget": {
"name": "prompt"
}
}
],
"properties": {
"Run widget replace on values": false
},
"widgets_values": [
""
]
},
{
"id": 1,
"type": "LumaImageGeneration",
"pos": [
833,
197
],
"size": [
342.5999755859375,
190
],
"flags": {},
"order": 2,
"mode": 0,
"inputs": [
{
"name": "client",
"type": "LUMACLIENT",
"link": 2
},
{
"name": "image_ref",
"type": "CONCAT_REFERENCES",
"link": null
},
{
"name": "style_ref",
"type": "REFERENCE",
"link": null
},
{
"name": "character_ref",
"type": "CHARACTER_REFERENCE",
"link": null
},
{
"name": "prompt",
"type": "STRING",
"link": 6,
"widget": {
"name": "prompt"
}
}
],
"outputs": [
{
"name": "image_url",
"type": "STRING",
"links": null,
"shape": 3
},
{
"name": "generation_id",
"type": "STRING",
"links": null,
"shape": 3
},
{
"name": "image",
"type": "IMAGE",
"links": [
1
],
"shape": 3,
"slot_index": 2
}
],
"properties": {
"Node name for S&R": "LumaImageGeneration"
},
"widgets_values": [
"photon-1",
"",
"9:16",
""
]
}
],
"links": [
[
1,
1,
2,
2,
0,
"IMAGE"
],
[
2,
3,
0,
1,
0,
"LUMACLIENT"
],
[
6,
7,
0,
1,
4,
"STRING"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 0.8264462809917354,
"offset": [
357.39093750000023,
135.15300781250014
]
}
},
"version": 0.4
}