converted all s3 nodes to v3
This commit is contained in:
@@ -2,39 +2,45 @@ from pathlib import Path
|
||||
|
||||
from inopyutils import ino_is_err, InoUtilHelper
|
||||
|
||||
import folder_paths
|
||||
from comfy_api.latest import io
|
||||
|
||||
from .s3_helper import S3Helper, S3_EMPTY_CONFIG_STRING
|
||||
from ..node_helper import PARENT_FOLDER_OPTIONS, resolve_comfy_path
|
||||
|
||||
class InoS3DownloadAudio:
|
||||
|
||||
class InoS3DownloadAudio(io.ComfyNode):
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"enabled": ("BOOLEAN", {"default": True, "label_off": "OFF", "label_on": "ON"}),
|
||||
"s3_key": ("STRING", {"default": "input/example.wav"}),
|
||||
"parent_folder": (PARENT_FOLDER_OPTIONS, {"default": "temp"}),
|
||||
"folder": ("STRING", {"default": ""}),
|
||||
},
|
||||
"optional": {
|
||||
"s3_config": ("STRING", {"default": S3_EMPTY_CONFIG_STRING, "tooltip": "you can leave it empty and pass it with env vars"}),
|
||||
"bucket_name": ("STRING", {"default": "default"}),
|
||||
}
|
||||
}
|
||||
def define_schema(cls):
|
||||
return io.Schema(
|
||||
node_id="InoS3DownloadAudio",
|
||||
display_name="Ino S3 Download Audio",
|
||||
category="InoS3Helper",
|
||||
description="Downloads an audio file from S3 and returns it as an AUDIO tensor.",
|
||||
inputs=[
|
||||
io.Boolean.Input("enabled", default=True, label_off="OFF", label_on="ON"),
|
||||
io.String.Input("s3_key", default="input/example.wav"),
|
||||
io.Combo.Input("parent_folder", options=PARENT_FOLDER_OPTIONS, default="temp"),
|
||||
io.String.Input("folder", default=""),
|
||||
io.String.Input("s3_config", default=S3_EMPTY_CONFIG_STRING, optional=True, tooltip="you can leave it empty and pass it with env vars"),
|
||||
io.String.Input("bucket_name", default="default", optional=True),
|
||||
],
|
||||
outputs=[
|
||||
io.Boolean.Output(display_name="success"),
|
||||
io.String.Output(display_name="message"),
|
||||
io.String.Output(display_name="rel_path"),
|
||||
io.String.Output(display_name="abs_path"),
|
||||
io.Audio.Output(display_name="audio"),
|
||||
],
|
||||
)
|
||||
|
||||
CATEGORY = "InoS3Helper"
|
||||
RETURN_TYPES = ("BOOLEAN", "STRING", "STRING", "STRING", "AUDIO", )
|
||||
RETURN_NAMES = ("success", "message", "rel_path", "abs_path", "audio", )
|
||||
FUNCTION = "function"
|
||||
|
||||
async def function(self, enabled, s3_key, parent_folder, folder, s3_config=None, bucket_name=None):
|
||||
@classmethod
|
||||
async def execute(cls, enabled, s3_key, parent_folder, folder, s3_config=None, bucket_name=None) -> io.NodeOutput:
|
||||
if not enabled:
|
||||
return (False, "not enabled", "", "", None, )
|
||||
return io.NodeOutput(False, "not enabled", "", "", None)
|
||||
|
||||
validate_s3_key = S3Helper.validate_s3_key(s3_key)
|
||||
if not validate_s3_key["success"]:
|
||||
return (False, validate_s3_key["msg"], "", "", None, )
|
||||
return io.NodeOutput(False, validate_s3_key["msg"], "", "", None)
|
||||
|
||||
random_str = InoUtilHelper.get_date_time_utc_base64()
|
||||
file_name = f'{random_str}{Path(s3_key).suffix}'
|
||||
@@ -44,24 +50,20 @@ class InoS3DownloadAudio:
|
||||
|
||||
s3_instance = S3Helper.get_instance(s3_config)
|
||||
if ino_is_err(s3_instance):
|
||||
return (False, s3_instance["msg"], "", "", None, )
|
||||
return io.NodeOutput(False, s3_instance["msg"], "", "", None)
|
||||
s3_instance = s3_instance["instance"]
|
||||
|
||||
downloaded = await s3_instance.download_file(
|
||||
s3_key=s3_key,
|
||||
local_file_path=abs_path
|
||||
)
|
||||
downloaded = await s3_instance.download_file(s3_key=s3_key, local_file_path=abs_path)
|
||||
if not downloaded["success"]:
|
||||
return (downloaded["success"], downloaded["msg"], rel_path, abs_path, None, )
|
||||
return io.NodeOutput(downloaded["success"], downloaded["msg"], rel_path, abs_path, None)
|
||||
|
||||
from comfy_extras.nodes_audio import LoadAudio
|
||||
|
||||
# Build annotated name for ComfyUI's audio loader
|
||||
folder_suffix = folder if folder else ""
|
||||
annotated_name = f"{folder_suffix}/{file_name} [{parent_folder}]" if folder_suffix else f"{file_name} [{parent_folder}]"
|
||||
load_audio = LoadAudio.execute(audio=annotated_name)
|
||||
|
||||
if load_audio[0]:
|
||||
return (True, "Success", rel_path, abs_path, load_audio[0],)
|
||||
return io.NodeOutput(True, "Success", rel_path, abs_path, load_audio[0])
|
||||
|
||||
return (False, "failed to load the audio", rel_path, abs_path, None,)
|
||||
return io.NodeOutput(False, "failed to load the audio", rel_path, abs_path, None)
|
||||
|
||||
@@ -1,53 +1,56 @@
|
||||
from pathlib import Path
|
||||
import folder_paths
|
||||
|
||||
from inopyutils import ino_ok, ino_err, ino_is_err
|
||||
from inopyutils import ino_is_err
|
||||
|
||||
from comfy_api.latest import io
|
||||
|
||||
from .s3_helper import S3Helper, S3_EMPTY_CONFIG_STRING
|
||||
from ..node_helper import PARENT_FOLDER_OPTIONS, resolve_comfy_path
|
||||
|
||||
class InoS3DownloadFile:
|
||||
|
||||
class InoS3DownloadFile(io.ComfyNode):
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"enabled": ("BOOLEAN", {"default": True, "label_off": "OFF", "label_on": "ON"}),
|
||||
"s3_key": ("STRING", {"default": "input/example.png"}),
|
||||
"parent_folder": (PARENT_FOLDER_OPTIONS,),
|
||||
"folder": ("STRING", {"default": "s3download/"}),
|
||||
},
|
||||
"optional": {
|
||||
"s3_config": ("STRING", {"default": S3_EMPTY_CONFIG_STRING, "tooltip": "you can leave it empty and pass it with env vars"}),
|
||||
"bucket_name": ("STRING", {"default": "default"}),
|
||||
}
|
||||
}
|
||||
def define_schema(cls):
|
||||
return io.Schema(
|
||||
node_id="InoS3DownloadFile",
|
||||
display_name="Ino S3 Download File",
|
||||
category="InoS3Helper",
|
||||
description="Downloads a file from S3 to a local folder.",
|
||||
inputs=[
|
||||
io.Boolean.Input("enabled", default=True, label_off="OFF", label_on="ON"),
|
||||
io.String.Input("s3_key", default="input/example.png"),
|
||||
io.Combo.Input("parent_folder", options=PARENT_FOLDER_OPTIONS),
|
||||
io.String.Input("folder", default="s3download/"),
|
||||
io.String.Input("s3_config", default=S3_EMPTY_CONFIG_STRING, optional=True, tooltip="you can leave it empty and pass it with env vars"),
|
||||
io.String.Input("bucket_name", default="default", optional=True),
|
||||
],
|
||||
outputs=[
|
||||
io.Boolean.Output(display_name="success"),
|
||||
io.String.Output(display_name="message"),
|
||||
io.String.Output(display_name="rel_path"),
|
||||
io.String.Output(display_name="abs_path"),
|
||||
],
|
||||
)
|
||||
|
||||
CATEGORY = "InoS3Helper"
|
||||
RETURN_TYPES = ("BOOLEAN", "STRING", "STRING", "STRING", )
|
||||
RETURN_NAMES = ("success", "message", "rel_path", "abs_path", )
|
||||
FUNCTION = "function"
|
||||
|
||||
async def function(self, enabled, s3_key, parent_folder, folder, s3_config=None, bucket_name=None):
|
||||
@classmethod
|
||||
async def execute(cls, enabled, s3_key, parent_folder, folder, s3_config=None, bucket_name=None) -> io.NodeOutput:
|
||||
if not enabled:
|
||||
return (False, "not enabled", "", "",)
|
||||
return io.NodeOutput(False, "not enabled", "", "")
|
||||
|
||||
validate_s3_key = S3Helper.validate_s3_key(s3_key)
|
||||
if not validate_s3_key["success"]:
|
||||
return (False, validate_s3_key["msg"], "", "",)
|
||||
return io.NodeOutput(False, validate_s3_key["msg"], "", "")
|
||||
|
||||
save_path = S3Helper.get_save_path(s3_key, folder)
|
||||
rel_path, abs_path = resolve_comfy_path(parent_folder, save_path)
|
||||
rel_path, abs_path = resolve_comfy_path(parent_folder, str(save_path))
|
||||
|
||||
if not Path(abs_path).is_dir():
|
||||
Path(abs_path).parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
s3_instance = S3Helper.get_instance(s3_config)
|
||||
if ino_is_err(s3_instance):
|
||||
return (False, s3_instance["msg"], "", "",)
|
||||
return io.NodeOutput(False, s3_instance["msg"], "", "")
|
||||
s3_instance = s3_instance["instance"]
|
||||
|
||||
s3_result = await s3_instance.download_file(
|
||||
s3_key=s3_key,
|
||||
local_file_path=abs_path
|
||||
)
|
||||
return (s3_result["success"], s3_result["msg"], rel_path, abs_path, )
|
||||
s3_result = await s3_instance.download_file(s3_key=s3_key, local_file_path=abs_path)
|
||||
return io.NodeOutput(s3_result["success"], s3_result["msg"], rel_path, abs_path)
|
||||
|
||||
@@ -1,59 +1,59 @@
|
||||
from pathlib import Path
|
||||
|
||||
import folder_paths
|
||||
from inopyutils import ino_is_err
|
||||
|
||||
from inopyutils import ino_ok, ino_err, ino_is_err
|
||||
from comfy_api.latest import io
|
||||
|
||||
from .s3_helper import S3Helper, S3_EMPTY_CONFIG_STRING
|
||||
from ..node_helper import PARENT_FOLDER_OPTIONS, resolve_comfy_path
|
||||
|
||||
class InoS3DownloadFolder:
|
||||
|
||||
class InoS3DownloadFolder(io.ComfyNode):
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"enabled": ("BOOLEAN", {"default": True, "label_off": "OFF", "label_on": "ON"}),
|
||||
"s3_key": ("STRING", {"default": "input/example.png"}),
|
||||
"parent_folder": (PARENT_FOLDER_OPTIONS,),
|
||||
"folder": ("STRING", {"default": "input/"}),
|
||||
},
|
||||
"optional": {
|
||||
"s3_config": ("STRING", {"default": S3_EMPTY_CONFIG_STRING, "tooltip": "you can leave it empty and pass it with env vars"}),
|
||||
"bucket_name": ("STRING", {"default": "default"}),
|
||||
"max_concurrent": ("INT", {"default": 5, "min": 1, "max": 10}),
|
||||
}
|
||||
}
|
||||
def define_schema(cls):
|
||||
return io.Schema(
|
||||
node_id="InoS3DownloadFolder",
|
||||
display_name="Ino S3 Download Folder",
|
||||
category="InoS3Helper",
|
||||
description="Downloads an entire folder from S3 with concurrent downloads.",
|
||||
inputs=[
|
||||
io.Boolean.Input("enabled", default=True, label_off="OFF", label_on="ON"),
|
||||
io.String.Input("s3_key", default="input/example.png"),
|
||||
io.Combo.Input("parent_folder", options=PARENT_FOLDER_OPTIONS),
|
||||
io.String.Input("folder", default="input/"),
|
||||
io.String.Input("s3_config", default=S3_EMPTY_CONFIG_STRING, optional=True, tooltip="you can leave it empty and pass it with env vars"),
|
||||
io.String.Input("bucket_name", default="default", optional=True),
|
||||
io.Int.Input("max_concurrent", default=5, min=1, max=10, optional=True),
|
||||
],
|
||||
outputs=[
|
||||
io.Boolean.Output(display_name="success"),
|
||||
io.String.Output(display_name="message"),
|
||||
io.String.Output(display_name="rel_path"),
|
||||
io.String.Output(display_name="abs_path"),
|
||||
],
|
||||
)
|
||||
|
||||
CATEGORY = "InoS3Helper"
|
||||
RETURN_TYPES = ("BOOLEAN", "STRING", "STRING", "STRING", )
|
||||
RETURN_NAMES = ("success", "message", "rel_path", "abs_path", )
|
||||
FUNCTION = "function"
|
||||
|
||||
async def function(self, enabled, s3_key, parent_folder, folder, s3_config=None, bucket_name=None, max_concurrent=5):
|
||||
@classmethod
|
||||
async def execute(cls, enabled, s3_key, parent_folder, folder, s3_config=None, bucket_name=None, max_concurrent=5) -> io.NodeOutput:
|
||||
if not enabled:
|
||||
return (False, "not enabled", "", "", )
|
||||
return io.NodeOutput(False, "not enabled", "", "")
|
||||
|
||||
validate_s3_key = S3Helper.validate_s3_key(s3_key)
|
||||
if not validate_s3_key["success"]:
|
||||
return (False, validate_s3_key["msg"], "", "",)
|
||||
return io.NodeOutput(False, validate_s3_key["msg"], "", "")
|
||||
|
||||
rel_path, abs_path = resolve_comfy_path(parent_folder, folder)
|
||||
|
||||
if Path(abs_path).is_file():
|
||||
return (False, "Save path is a file", "", "",)
|
||||
return io.NodeOutput(False, "Save path is a file", "", "")
|
||||
|
||||
if not Path(abs_path).is_dir():
|
||||
Path(abs_path).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
s3_instance = S3Helper.get_instance(s3_config)
|
||||
if ino_is_err(s3_instance):
|
||||
return (False, s3_instance["msg"], "", "",)
|
||||
return io.NodeOutput(False, s3_instance["msg"], "", "")
|
||||
s3_instance = s3_instance["instance"]
|
||||
|
||||
s3_result = await s3_instance.download_folder(
|
||||
s3_folder_key=s3_key,
|
||||
local_folder_path=abs_path,
|
||||
max_concurrent=max_concurrent
|
||||
)
|
||||
|
||||
return (s3_result["success"], s3_result["msg"], rel_path, abs_path, )
|
||||
s3_result = await s3_instance.download_folder(s3_folder_key=s3_key, local_folder_path=abs_path, max_concurrent=max_concurrent)
|
||||
return io.NodeOutput(s3_result["success"], s3_result["msg"], rel_path, abs_path)
|
||||
|
||||
@@ -2,41 +2,48 @@ from pathlib import Path
|
||||
|
||||
from inopyutils import ino_is_err, InoUtilHelper
|
||||
|
||||
import folder_paths
|
||||
from comfy_api.latest import io
|
||||
|
||||
from .s3_helper import S3Helper, S3_EMPTY_CONFIG_STRING
|
||||
from ..node_helper import PARENT_FOLDER_OPTIONS, resolve_comfy_path, load_image
|
||||
|
||||
class InoS3DownloadImage:
|
||||
|
||||
class InoS3DownloadImage(io.ComfyNode):
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"enabled": ("BOOLEAN", {"default": True, "label_off": "OFF", "label_on": "ON"}),
|
||||
"s3_key": ("STRING", {"default": "input/example.png"}),
|
||||
"parent_folder": (PARENT_FOLDER_OPTIONS, {"default": "temp"}),
|
||||
"folder": ("STRING", {"default": ""}),
|
||||
},
|
||||
"optional": {
|
||||
"s3_config": ("STRING", {"default": S3_EMPTY_CONFIG_STRING, "tooltip": "you can leave it empty and pass it with env vars"}),
|
||||
"bucket_name": ("STRING", {"default": "default"}),
|
||||
}
|
||||
}
|
||||
def define_schema(cls):
|
||||
return io.Schema(
|
||||
node_id="InoS3DownloadImage",
|
||||
display_name="Ino S3 Download Image",
|
||||
category="InoS3Helper",
|
||||
description="Downloads an image from S3 and returns it as an IMAGE tensor with mask.",
|
||||
inputs=[
|
||||
io.Boolean.Input("enabled", default=True, label_off="OFF", label_on="ON"),
|
||||
io.String.Input("s3_key", default="input/example.png"),
|
||||
io.Combo.Input("parent_folder", options=PARENT_FOLDER_OPTIONS, default="temp"),
|
||||
io.String.Input("folder", default=""),
|
||||
io.String.Input("s3_config", default=S3_EMPTY_CONFIG_STRING, optional=True, tooltip="you can leave it empty and pass it with env vars"),
|
||||
io.String.Input("bucket_name", default="default", optional=True),
|
||||
],
|
||||
outputs=[
|
||||
io.Boolean.Output(display_name="success"),
|
||||
io.String.Output(display_name="message"),
|
||||
io.String.Output(display_name="rel_path"),
|
||||
io.String.Output(display_name="abs_path"),
|
||||
io.Image.Output(display_name="image"),
|
||||
io.Mask.Output(display_name="mask"),
|
||||
],
|
||||
)
|
||||
|
||||
CATEGORY = "InoS3Helper"
|
||||
RETURN_TYPES = ("BOOLEAN", "STRING", "STRING", "STRING", "IMAGE", "MASK", )
|
||||
RETURN_NAMES = ("success", "message", "rel_path", "abs_path", "image", "mask", )
|
||||
FUNCTION = "function"
|
||||
|
||||
async def function(self, enabled, s3_key, parent_folder, folder, s3_config=None, bucket_name=None):
|
||||
@classmethod
|
||||
async def execute(cls, enabled, s3_key, parent_folder, folder, s3_config=None, bucket_name=None) -> io.NodeOutput:
|
||||
from nodes import EmptyImage
|
||||
empty_image = EmptyImage().generate(512, 512)[0]
|
||||
if not enabled:
|
||||
return (False, "not enabled", "", "", empty_image, None, )
|
||||
return io.NodeOutput(False, "not enabled", "", "", empty_image, None)
|
||||
|
||||
validate_s3_key = S3Helper.validate_s3_key(s3_key)
|
||||
if not validate_s3_key["success"]:
|
||||
return (False, validate_s3_key["msg"], "", "", empty_image, None,)
|
||||
return io.NodeOutput(False, validate_s3_key["msg"], "", "", empty_image, None)
|
||||
|
||||
random_str = InoUtilHelper.get_date_time_utc_base64()
|
||||
file_name = f'{random_str}{Path(s3_key).suffix}'
|
||||
@@ -46,16 +53,12 @@ class InoS3DownloadImage:
|
||||
|
||||
s3_instance = S3Helper.get_instance(s3_config)
|
||||
if ino_is_err(s3_instance):
|
||||
return (False, s3_instance["msg"], "", "", empty_image, None,)
|
||||
return io.NodeOutput(False, s3_instance["msg"], "", "", empty_image, None)
|
||||
s3_instance = s3_instance["instance"]
|
||||
|
||||
downloaded = await s3_instance.download_file(
|
||||
s3_key=s3_key,
|
||||
local_file_path=abs_path
|
||||
)
|
||||
downloaded = await s3_instance.download_file(s3_key=s3_key, local_file_path=abs_path)
|
||||
if not downloaded["success"]:
|
||||
return (downloaded["success"], downloaded["msg"], rel_path, abs_path, empty_image, None, )
|
||||
return io.NodeOutput(downloaded["success"], downloaded["msg"], rel_path, abs_path, empty_image, None)
|
||||
|
||||
output_image, output_mask = load_image(downloaded["local_file"])
|
||||
|
||||
return (downloaded["success"], downloaded["msg"], rel_path, abs_path, output_image, output_mask, )
|
||||
return io.NodeOutput(downloaded["success"], downloaded["msg"], rel_path, abs_path, output_image, output_mask)
|
||||
|
||||
@@ -1,51 +1,48 @@
|
||||
from pathlib import Path
|
||||
import folder_paths
|
||||
from inopyutils import ino_is_err
|
||||
|
||||
from inopyutils import ino_ok, ino_err, ino_is_err
|
||||
from comfy_api.latest import io
|
||||
|
||||
from .s3_helper import S3Helper, S3_EMPTY_CONFIG_STRING
|
||||
|
||||
|
||||
class InoS3GetDownloadURL:
|
||||
class InoS3GetDownloadURL(io.ComfyNode):
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"enabled": ("BOOLEAN", {"default": True, "label_off": "OFF", "label_on": "ON"}),
|
||||
"s3_key": ("STRING", {"default": "input/example.png"}),
|
||||
"expires_in": ("INT", {"default": 3600, "min": 1, "max": 3600, "step": 1}),
|
||||
"as_attachment": ("BOOLEAN", {"default": False}),
|
||||
"filename": ("STRING", {"default": ""}),
|
||||
},
|
||||
"optional": {
|
||||
"s3_config": ("STRING", {"default": S3_EMPTY_CONFIG_STRING, "tooltip": "you can leave it empty and pass it with env vars"}),
|
||||
"bucket_name": ("STRING", {"default": "default"}),
|
||||
}
|
||||
}
|
||||
def define_schema(cls):
|
||||
return io.Schema(
|
||||
node_id="InoS3GetDownloadURL",
|
||||
display_name="Ino S3 Get Download URL",
|
||||
category="InoS3Helper",
|
||||
description="Generates a presigned download URL for an S3 object.",
|
||||
inputs=[
|
||||
io.Boolean.Input("enabled", default=True, label_off="OFF", label_on="ON"),
|
||||
io.String.Input("s3_key", default="input/example.png"),
|
||||
io.Int.Input("expires_in", default=3600, min=1, max=3600, step=1),
|
||||
io.Boolean.Input("as_attachment", default=False),
|
||||
io.String.Input("filename", default=""),
|
||||
io.String.Input("s3_config", default=S3_EMPTY_CONFIG_STRING, optional=True, tooltip="you can leave it empty and pass it with env vars"),
|
||||
io.String.Input("bucket_name", default="default", optional=True),
|
||||
],
|
||||
outputs=[
|
||||
io.Boolean.Output(display_name="success"),
|
||||
io.String.Output(display_name="message"),
|
||||
io.String.Output(display_name="download_url"),
|
||||
io.String.Output(display_name="filename"),
|
||||
],
|
||||
)
|
||||
|
||||
CATEGORY = "InoS3Helper"
|
||||
RETURN_TYPES = ("BOOLEAN", "STRING", "STRING", "STRING", "STRING",)
|
||||
RETURN_NAMES = ("success", "msg", "result", "download_url", "filename",)
|
||||
FUNCTION = "function"
|
||||
|
||||
async def function(self, enabled:bool, s3_key:str, expires_in:int = 3600, as_attachment:bool = False, filename:str = None, s3_config:str = "{}", bucket_name:str = "default"):
|
||||
@classmethod
|
||||
async def execute(cls, enabled, s3_key, expires_in=3600, as_attachment=False, filename=None, s3_config=None, bucket_name=None) -> io.NodeOutput:
|
||||
if not enabled:
|
||||
return (False, "not enabled", "", "", "",)
|
||||
return io.NodeOutput(False, "not enabled", "", "")
|
||||
|
||||
validate_s3_key = S3Helper.validate_s3_key(s3_key)
|
||||
if not validate_s3_key["success"]:
|
||||
return (False, validate_s3_key["msg"], "", "", "",)
|
||||
return io.NodeOutput(False, validate_s3_key["msg"], "", "")
|
||||
|
||||
s3_instance = S3Helper.get_instance(s3_config)
|
||||
if ino_is_err(s3_instance):
|
||||
return (False, s3_instance["msg"], "", "", "",)
|
||||
return io.NodeOutput(False, s3_instance["msg"], "", "")
|
||||
s3_instance = s3_instance["instance"]
|
||||
|
||||
s3_result = await s3_instance.get_download_link(
|
||||
s3_key=s3_key,
|
||||
#bucket_name=bucket_name,
|
||||
expires_in=expires_in,
|
||||
as_attachment=as_attachment,
|
||||
filename=filename,
|
||||
)
|
||||
return (s3_result["success"], s3_result["msg"], str(s3_result), s3_result["url"], s3_result["filename"],)
|
||||
s3_result = await s3_instance.get_download_link(s3_key=s3_key, expires_in=expires_in, as_attachment=as_attachment, filename=filename)
|
||||
return io.NodeOutput(s3_result["success"], s3_result["msg"], s3_result["url"], s3_result["filename"])
|
||||
|
||||
@@ -3,6 +3,8 @@ from pathlib import Path
|
||||
|
||||
from inopyutils import InoS3Helper, InoJsonHelper, ino_ok, ino_err, ino_is_err
|
||||
|
||||
from comfy_api.latest import io
|
||||
|
||||
S3_EMPTY_CONFIG = {
|
||||
"access_key_id": "",
|
||||
"access_key_secret": "",
|
||||
@@ -61,14 +63,14 @@ class S3Helper:
|
||||
if s3_config_dict.get("access_key_id", None) is None:
|
||||
return {
|
||||
"success": False,
|
||||
"msg": f"S3 configuration missing access_key_id",
|
||||
"msg": "S3 configuration missing access_key_id",
|
||||
"config": ""
|
||||
}
|
||||
|
||||
if s3_config_dict.get("access_key_secret", None) is None:
|
||||
return {
|
||||
"success": False,
|
||||
"msg": f"S3 configuration missing access_key_secret",
|
||||
"msg": "S3 configuration missing access_key_secret",
|
||||
"config": ""
|
||||
}
|
||||
|
||||
@@ -81,61 +83,19 @@ class S3Helper:
|
||||
@staticmethod
|
||||
def validate_s3_key(s3_key) -> dict:
|
||||
if not s3_key or not s3_key.strip():
|
||||
return {
|
||||
"success": False,
|
||||
"msg": "S3 key is required and cannot be empty"
|
||||
}
|
||||
return {
|
||||
"success": True,
|
||||
"msg": "S3 key is valid"
|
||||
}
|
||||
return {"success": False, "msg": "S3 key is required and cannot be empty"}
|
||||
return {"success": True, "msg": "S3 key is valid"}
|
||||
|
||||
@staticmethod
|
||||
def validate_local_path(local_path) -> dict:
|
||||
if not local_path:
|
||||
return {
|
||||
"success": False,
|
||||
"msg": "Local path is required and cannot be empty"
|
||||
}
|
||||
|
||||
return {"success": False, "msg": "Local path is required and cannot be empty"}
|
||||
if not Path(local_path).is_file() and not Path(local_path).is_dir():
|
||||
return {
|
||||
"success": False,
|
||||
"msg": "Local path does not exist"
|
||||
}
|
||||
return {
|
||||
"success": True,
|
||||
"msg": "Local path is valid"
|
||||
}
|
||||
return {"success": False, "msg": "Local path does not exist"}
|
||||
return {"success": True, "msg": "Local path is valid"}
|
||||
|
||||
@staticmethod
|
||||
def get_save_path(s3_key: str, save_path: str):
|
||||
"""
|
||||
Generate a local file path for saving an S3 object, handling various path formats.
|
||||
|
||||
This function intelligently determines the appropriate local file path based on the
|
||||
provided save_path format and the S3 key. It handles three scenarios:
|
||||
1. Directory path - saves file with original name from S3 key
|
||||
2. Full file path with extension - replaces extension with S3 object's extension
|
||||
3. File path without extension - appends S3 object's extension
|
||||
|
||||
Args:
|
||||
s3_key (str): The S3 object key (path) from which to extract the filename and extension.
|
||||
Example: "folder/subfolder/document.pdf"
|
||||
save_path (str): The desired local save path. Can be:
|
||||
- Directory path ending with '/' or '\\': "/local/dir/"
|
||||
- Full file path with extension: "/local/dir/myfile.txt"
|
||||
- File path without extension: "/local/dir/myfile"
|
||||
|
||||
Returns:
|
||||
Path: A pathlib.Path object representing the complete local file path where
|
||||
the S3 object should be saved, including the appropriate file extension.
|
||||
|
||||
Note:
|
||||
- The function preserves the file extension from the S3 key in all cases
|
||||
- Both forward slashes (/) and backslashes (\\) are recognized as directory indicators
|
||||
- If save_path has an extension, it will be replaced with the S3 object's extension
|
||||
"""
|
||||
save_path_obj = Path(save_path)
|
||||
s3_key_obj = Path(s3_key)
|
||||
|
||||
@@ -151,26 +111,31 @@ class S3Helper:
|
||||
|
||||
return local_file_path
|
||||
|
||||
class InoS3Config:
|
||||
|
||||
class InoS3Config(io.ComfyNode):
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required":{
|
||||
"access_key_id": ("STRING", {"default": ""}),
|
||||
"access_key_secret": ("STRING", {"default": ""}),
|
||||
"endpoint_url": ("STRING", {"default": ""}),
|
||||
"region_name": ("STRING", {"default": ""}),
|
||||
"bucket_name": ("STRING", {"default": ""}),
|
||||
}
|
||||
}
|
||||
def define_schema(cls):
|
||||
return io.Schema(
|
||||
node_id="InoS3Config",
|
||||
display_name="Ino S3 Config",
|
||||
category="InoS3Helper",
|
||||
description="Creates an S3 configuration string from credentials.",
|
||||
inputs=[
|
||||
io.String.Input("access_key_id", default=""),
|
||||
io.String.Input("access_key_secret", default=""),
|
||||
io.String.Input("endpoint_url", default=""),
|
||||
io.String.Input("region_name", default=""),
|
||||
io.String.Input("bucket_name", default=""),
|
||||
],
|
||||
outputs=[
|
||||
io.Boolean.Output(display_name="success"),
|
||||
io.String.Output(display_name="config"),
|
||||
],
|
||||
)
|
||||
|
||||
CATEGORY = "InoS3Helper"
|
||||
RETURN_TYPES = ("BOOLEAN", "STRING", )
|
||||
RETURN_NAMES = ("success", "config", )
|
||||
FUNCTION = "function"
|
||||
|
||||
async def function(self, access_key_id, access_key_secret, endpoint_url, region_name, bucket_name):
|
||||
s3_config ={
|
||||
@classmethod
|
||||
async def execute(cls, access_key_id, access_key_secret, endpoint_url, region_name, bucket_name) -> io.NodeOutput:
|
||||
s3_config = {
|
||||
"access_key_id": access_key_id,
|
||||
"access_key_secret": access_key_secret,
|
||||
"endpoint_url": endpoint_url,
|
||||
@@ -179,6 +144,5 @@ class InoS3Config:
|
||||
}
|
||||
dict_to_string = InoJsonHelper.dict_to_string(s3_config)
|
||||
if not dict_to_string["success"]:
|
||||
return (False, dict_to_string["msg"], )
|
||||
|
||||
return (True, dict_to_string["data"], )
|
||||
return io.NodeOutput(False, dict_to_string["msg"])
|
||||
return io.NodeOutput(True, dict_to_string["data"])
|
||||
|
||||
@@ -2,45 +2,53 @@ from pathlib import Path
|
||||
|
||||
from inopyutils import ino_is_err
|
||||
|
||||
import folder_paths
|
||||
from comfy_api.latest import io
|
||||
|
||||
from .s3_helper import S3Helper, S3_EMPTY_CONFIG_STRING
|
||||
from ..node_helper import any_type, PARENT_FOLDER_OPTIONS, resolve_comfy_path
|
||||
from ..node_helper import PARENT_FOLDER_OPTIONS, resolve_comfy_path
|
||||
|
||||
class InoS3SyncFolder:
|
||||
|
||||
class InoS3SyncFolder(io.ComfyNode):
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"execute": (any_type,),
|
||||
"enabled": ("BOOLEAN", {"default": True, "label_off": "OFF", "label_on": "ON"}),
|
||||
"s3_key": ("STRING", {"default": ""}),
|
||||
"parent_folder": (PARENT_FOLDER_OPTIONS,),
|
||||
"folder": ("STRING", {"default": "sync/"}),
|
||||
"sync_local": ("BOOLEAN", {"default": True, "label_off": "Upload (local->S3)", "label_on": "Download (S3->local)"}),
|
||||
},
|
||||
"optional": {
|
||||
"s3_config": ("STRING", {"default": S3_EMPTY_CONFIG_STRING, "tooltip": "you can leave it empty and pass it with env vars"}),
|
||||
"concurrency": ("INT", {"default": 5, "min": 1, "max": 10}),
|
||||
}
|
||||
}
|
||||
def define_schema(cls):
|
||||
return io.Schema(
|
||||
node_id="InoS3SyncFolder",
|
||||
display_name="Ino S3 Sync Folder",
|
||||
category="InoS3Helper",
|
||||
description="Syncs a local folder with S3 bidirectionally (upload or download).",
|
||||
is_output_node=True,
|
||||
inputs=[
|
||||
io.AnyType.Input("execute"),
|
||||
io.Boolean.Input("enabled", default=True, label_off="OFF", label_on="ON"),
|
||||
io.String.Input("s3_key", default=""),
|
||||
io.Combo.Input("parent_folder", options=PARENT_FOLDER_OPTIONS),
|
||||
io.String.Input("folder", default="sync/"),
|
||||
io.Boolean.Input("sync_local", default=True, label_off="Upload (local->S3)", label_on="Download (S3->local)"),
|
||||
io.String.Input("s3_config", default=S3_EMPTY_CONFIG_STRING, optional=True, tooltip="you can leave it empty and pass it with env vars"),
|
||||
io.Int.Input("concurrency", default=5, min=1, max=10, optional=True),
|
||||
],
|
||||
outputs=[
|
||||
io.Boolean.Output(display_name="success"),
|
||||
io.String.Output(display_name="message"),
|
||||
io.String.Output(display_name="rel_path"),
|
||||
io.String.Output(display_name="abs_path"),
|
||||
io.Int.Output(display_name="downloaded"),
|
||||
io.Int.Output(display_name="uploaded"),
|
||||
io.Int.Output(display_name="skipped_unchanged"),
|
||||
io.Int.Output(display_name="failed"),
|
||||
],
|
||||
)
|
||||
|
||||
CATEGORY = "InoS3Helper"
|
||||
RETURN_TYPES = ("BOOLEAN", "STRING", "STRING", "STRING", "INT", "INT", "INT", "INT",)
|
||||
RETURN_NAMES = ("success", "message", "rel_path", "abs_path", "downloaded", "uploaded", "skipped_unchanged", "failed",)
|
||||
FUNCTION = "function"
|
||||
OUTPUT_NODE = True
|
||||
|
||||
async def function(self, execute, enabled, s3_key, parent_folder, folder, sync_local, s3_config=None, concurrency=5):
|
||||
@classmethod
|
||||
async def execute(cls, execute, enabled, s3_key, parent_folder, folder, sync_local, s3_config=None, concurrency=5) -> io.NodeOutput:
|
||||
if not enabled:
|
||||
return (False, "not enabled", "", "", 0, 0, 0, 0,)
|
||||
|
||||
return io.NodeOutput(False, "not enabled", "", "", 0, 0, 0, 0)
|
||||
if not execute:
|
||||
return (False, "execute empty", "", "", 0, 0, 0, 0,)
|
||||
return io.NodeOutput(False, "execute empty", "", "", 0, 0, 0, 0)
|
||||
|
||||
validate_s3_key = S3Helper.validate_s3_key(s3_key)
|
||||
if not validate_s3_key["success"]:
|
||||
return (False, validate_s3_key["msg"], "", "", 0, 0, 0, 0,)
|
||||
return io.NodeOutput(False, validate_s3_key["msg"], "", "", 0, 0, 0, 0)
|
||||
|
||||
rel_path, abs_path = resolve_comfy_path(parent_folder, folder)
|
||||
|
||||
@@ -50,23 +58,13 @@ class InoS3SyncFolder:
|
||||
|
||||
s3_instance = S3Helper.get_instance(s3_config)
|
||||
if ino_is_err(s3_instance):
|
||||
return (False, s3_instance["msg"], "", "", 0, 0, 0, 0,)
|
||||
return io.NodeOutput(False, s3_instance["msg"], "", "", 0, 0, 0, 0)
|
||||
s3_instance = s3_instance["instance"]
|
||||
|
||||
s3_result = await s3_instance.sync_folder(
|
||||
s3_key=s3_key,
|
||||
local_folder_path=abs_path,
|
||||
sync_local=sync_local,
|
||||
concurrency=concurrency,
|
||||
)
|
||||
s3_result = await s3_instance.sync_folder(s3_key=s3_key, local_folder_path=abs_path, sync_local=sync_local, concurrency=concurrency)
|
||||
|
||||
return (
|
||||
s3_result["success"],
|
||||
s3_result["msg"],
|
||||
rel_path,
|
||||
abs_path,
|
||||
s3_result.get("downloaded", 0),
|
||||
s3_result.get("uploaded", 0),
|
||||
s3_result.get("skipped_unchanged", 0),
|
||||
s3_result.get("failed", 0),
|
||||
return io.NodeOutput(
|
||||
s3_result["success"], s3_result["msg"], rel_path, abs_path,
|
||||
s3_result.get("downloaded", 0), s3_result.get("uploaded", 0),
|
||||
s3_result.get("skipped_unchanged", 0), s3_result.get("failed", 0),
|
||||
)
|
||||
|
||||
@@ -3,94 +3,89 @@ from pathlib import Path
|
||||
from inopyutils import ino_is_err, InoUtilHelper
|
||||
|
||||
import folder_paths
|
||||
from comfy_api.latest import IO
|
||||
from comfy_api.latest import io, IO
|
||||
|
||||
from .s3_helper import S3Helper, S3_EMPTY_CONFIG_STRING
|
||||
from ..node_helper import any_type, PARENT_FOLDER_OPTIONS, resolve_comfy_path
|
||||
from ..node_helper import PARENT_FOLDER_OPTIONS, resolve_comfy_path
|
||||
|
||||
class InoS3UploadAudio:
|
||||
|
||||
class InoS3UploadAudio(io.ComfyNode):
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"execute": (any_type,),
|
||||
"enabled": ("BOOLEAN", {"default": True, "label_off": "OFF", "label_on": "ON"}),
|
||||
"audio": ("AUDIO",),
|
||||
"s3_key": ("STRING", {"default": ""}),
|
||||
"parent_folder": (PARENT_FOLDER_OPTIONS, {"default": "output"}),
|
||||
"folder": ("STRING", {"default": ""}),
|
||||
"filename": ("STRING", {"default": ""})
|
||||
},
|
||||
"optional": {
|
||||
"s3_config": ("STRING", {"default": S3_EMPTY_CONFIG_STRING, "tooltip": "you can leave it empty and pass it with env vars"}),
|
||||
"date_time_as_name": ("BOOLEAN", {"default": False}),
|
||||
},
|
||||
}
|
||||
def define_schema(cls):
|
||||
return io.Schema(
|
||||
node_id="InoS3UploadAudio",
|
||||
display_name="Ino S3 Upload Audio",
|
||||
category="InoS3Helper",
|
||||
description="Saves audio as MP3 locally then uploads it to S3.",
|
||||
is_output_node=True,
|
||||
inputs=[
|
||||
io.AnyType.Input("execute"),
|
||||
io.Boolean.Input("enabled", default=True, label_off="OFF", label_on="ON"),
|
||||
io.Audio.Input("audio"),
|
||||
io.String.Input("s3_key", default=""),
|
||||
io.Combo.Input("parent_folder", options=PARENT_FOLDER_OPTIONS, default="output"),
|
||||
io.String.Input("folder", default=""),
|
||||
io.String.Input("filename", default=""),
|
||||
io.String.Input("s3_config", default=S3_EMPTY_CONFIG_STRING, optional=True, tooltip="you can leave it empty and pass it with env vars"),
|
||||
io.Boolean.Input("date_time_as_name", default=False, optional=True),
|
||||
],
|
||||
outputs=[
|
||||
io.Audio.Output(display_name="audio"),
|
||||
io.Boolean.Output(display_name="success"),
|
||||
io.String.Output(display_name="message"),
|
||||
io.String.Output(display_name="rel_path"),
|
||||
io.String.Output(display_name="abs_path"),
|
||||
io.String.Output(display_name="file_name"),
|
||||
io.String.Output(display_name="s3_audio_path"),
|
||||
],
|
||||
)
|
||||
|
||||
RETURN_TYPES = ("AUDIO", "BOOLEAN", "STRING", "STRING", "STRING", "STRING", "STRING",)
|
||||
RETURN_NAMES = ("audio", "success", "message", "rel_path", "abs_path", "file_name", "s3_audio_path",)
|
||||
FUNCTION = "function"
|
||||
OUTPUT_NODE = True
|
||||
CATEGORY = "InoS3Helper"
|
||||
|
||||
async def function(self, execute, enabled, audio, s3_key, parent_folder, folder, filename, s3_config=None, date_time_as_name=False):
|
||||
@classmethod
|
||||
async def execute(cls, execute, enabled, audio, s3_key, parent_folder, folder, filename, s3_config=None, date_time_as_name=False) -> io.NodeOutput:
|
||||
if not enabled:
|
||||
return (audio, False, "", "", "", "", "",)
|
||||
|
||||
return io.NodeOutput(audio, False, "", "", "", "", "")
|
||||
if not execute:
|
||||
return (audio, False, "", "", "", "", "",)
|
||||
return io.NodeOutput(audio, False, "", "", "", "", "")
|
||||
|
||||
if isinstance(filename, list):
|
||||
if len(filename) == 1:
|
||||
filename = str(filename[0])
|
||||
else:
|
||||
return (audio, False, "file name is list", "", "", "", "",)
|
||||
elif isinstance(filename, str):
|
||||
pass
|
||||
else:
|
||||
return (audio, False, "file name not string", "", "", "", "",)
|
||||
return io.NodeOutput(audio, False, "file name is list", "", "", "", "")
|
||||
elif not isinstance(filename, str):
|
||||
return io.NodeOutput(audio, False, "file name not string", "", "", "", "")
|
||||
|
||||
validate_s3_key = S3Helper.validate_s3_key(s3_key)
|
||||
if not validate_s3_key["success"]:
|
||||
return (audio, False, validate_s3_key["msg"], "", "", "", "",)
|
||||
return io.NodeOutput(audio, False, validate_s3_key["msg"], "", "", "", "")
|
||||
|
||||
if date_time_as_name:
|
||||
filename = InoUtilHelper.get_date_time_utc_base64()
|
||||
|
||||
rel_path, abs_path = resolve_comfy_path(parent_folder, folder)
|
||||
|
||||
save_as = "mp3"
|
||||
file = f"{filename}.{save_as}"
|
||||
file = f"{filename}.mp3"
|
||||
|
||||
from comfy_extras.nodes_audio import SaveAudioMP3
|
||||
|
||||
audio_saver = SaveAudioMP3()
|
||||
save_audio: IO.NodeOutput = audio_saver.execute(
|
||||
audio=audio,
|
||||
filename_prefix=filename,
|
||||
format="mp3",
|
||||
quality="128k"
|
||||
)
|
||||
save_audio = audio_saver.execute(audio=audio, filename_prefix=filename, format="mp3", quality="128k")
|
||||
|
||||
try:
|
||||
saved_filename = save_audio.ui.as_dict()["audio"][0]["filename"]
|
||||
except:
|
||||
return (audio, False, "Audio saved, but failed to get filename", rel_path, abs_path, "", "",)
|
||||
return io.NodeOutput(audio, False, "Audio saved, but failed to get filename", rel_path, abs_path, "", "")
|
||||
|
||||
parent_path = folder_paths.get_output_directory()
|
||||
full_path:Path = Path(parent_path) / saved_filename
|
||||
full_path = Path(parent_path) / saved_filename
|
||||
|
||||
s3_instance = S3Helper.get_instance(s3_config)
|
||||
if ino_is_err(s3_instance):
|
||||
return (audio, False, s3_instance["msg"], rel_path, abs_path, "", "",)
|
||||
return io.NodeOutput(audio, False, s3_instance["msg"], rel_path, abs_path, "", "")
|
||||
s3_instance = s3_instance["instance"]
|
||||
|
||||
s3_full_key = f"{s3_key.rstrip('/')}/{saved_filename}"
|
||||
s3_result = await s3_instance.upload_file(
|
||||
s3_key=s3_full_key,
|
||||
local_file_path=str(full_path),
|
||||
)
|
||||
s3_result = await s3_instance.upload_file(s3_key=s3_full_key, local_file_path=str(full_path))
|
||||
if not s3_result["success"]:
|
||||
return (audio, False, s3_result["msg"], rel_path, abs_path, "", "",)
|
||||
return io.NodeOutput(audio, False, s3_result["msg"], rel_path, abs_path, "", "")
|
||||
|
||||
return (audio, True, "Success", rel_path, abs_path, file, s3_full_key, )
|
||||
return io.NodeOutput(audio, True, "Success", rel_path, abs_path, file, s3_full_key)
|
||||
|
||||
@@ -1,66 +1,66 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from inopyutils import ino_ok, ino_err, ino_is_err
|
||||
from inopyutils import ino_is_err
|
||||
|
||||
import folder_paths
|
||||
from comfy_api.latest import io
|
||||
|
||||
from .s3_helper import S3Helper, S3_EMPTY_CONFIG_STRING
|
||||
from ..node_helper import any_type, PARENT_FOLDER_OPTIONS, resolve_comfy_path
|
||||
from ..node_helper import PARENT_FOLDER_OPTIONS, resolve_comfy_path
|
||||
|
||||
class InoS3UploadFile:
|
||||
|
||||
class InoS3UploadFile(io.ComfyNode):
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required":{
|
||||
"execute": (any_type,),
|
||||
"enabled": ("BOOLEAN", {"default": True, "label_off": "OFF", "label_on": "ON"}),
|
||||
"s3_key": ("STRING", {"default": ""}),
|
||||
"parent_folder": (PARENT_FOLDER_OPTIONS,),
|
||||
"folder": ("STRING", {"default": ""}),
|
||||
"filename": ("STRING", {"default": ""}),
|
||||
"delete_local": ("BOOLEAN", {"default": True}),
|
||||
},
|
||||
"optional": {
|
||||
"s3_config": ("STRING", {"default": S3_EMPTY_CONFIG_STRING , "tooltip": "you can leave it empty and pass it with env vars"}),
|
||||
"bucket_name": ("STRING", {"default": "default"}),
|
||||
}
|
||||
}
|
||||
def define_schema(cls):
|
||||
return io.Schema(
|
||||
node_id="InoS3UploadFile",
|
||||
display_name="Ino S3 Upload File",
|
||||
category="InoS3Helper",
|
||||
description="Uploads a local file to S3 with optional local deletion.",
|
||||
is_output_node=True,
|
||||
inputs=[
|
||||
io.AnyType.Input("execute"),
|
||||
io.Boolean.Input("enabled", default=True, label_off="OFF", label_on="ON"),
|
||||
io.String.Input("s3_key", default=""),
|
||||
io.Combo.Input("parent_folder", options=PARENT_FOLDER_OPTIONS),
|
||||
io.String.Input("folder", default=""),
|
||||
io.String.Input("filename", default=""),
|
||||
io.Boolean.Input("delete_local", default=True),
|
||||
io.String.Input("s3_config", default=S3_EMPTY_CONFIG_STRING, optional=True, tooltip="you can leave it empty and pass it with env vars"),
|
||||
io.String.Input("bucket_name", default="default", optional=True),
|
||||
],
|
||||
outputs=[
|
||||
io.Boolean.Output(display_name="success"),
|
||||
io.String.Output(display_name="message"),
|
||||
io.String.Output(display_name="rel_path"),
|
||||
io.String.Output(display_name="abs_path"),
|
||||
],
|
||||
)
|
||||
|
||||
CATEGORY = "InoS3Helper"
|
||||
RETURN_TYPES = ("BOOLEAN", "STRING", "STRING", "STRING", )
|
||||
RETURN_NAMES = ("success", "message", "rel_path", "abs_path", )
|
||||
FUNCTION = "function"
|
||||
OUTPUT_NODE = True
|
||||
|
||||
async def function(self, execute, enabled:bool, s3_key:str, parent_folder:str, folder:str, filename:str, delete_local:bool, s3_config:str=None, bucket_name:str=None):
|
||||
@classmethod
|
||||
async def execute(cls, execute, enabled, s3_key, parent_folder, folder, filename, delete_local, s3_config=None, bucket_name=None) -> io.NodeOutput:
|
||||
if not enabled:
|
||||
return (False, "not enabled", "", "",)
|
||||
|
||||
return io.NodeOutput(False, "not enabled", "", "")
|
||||
if not execute:
|
||||
return (False, "execute empty", "", "", )
|
||||
return io.NodeOutput(False, "execute empty", "", "")
|
||||
|
||||
validate_s3_key = S3Helper.validate_s3_key(s3_key)
|
||||
if not validate_s3_key["success"]:
|
||||
return (False, validate_s3_key["msg"], "", "", )
|
||||
return io.NodeOutput(False, validate_s3_key["msg"], "", "")
|
||||
|
||||
rel_path, abs_path = resolve_comfy_path(parent_folder, folder, filename)
|
||||
|
||||
local_upload_path = Path(abs_path)
|
||||
validate_local_path = S3Helper.validate_local_path(local_upload_path)
|
||||
validate_local_path = S3Helper.validate_local_path(Path(abs_path))
|
||||
if not validate_local_path["success"]:
|
||||
return (False, validate_local_path["msg"], rel_path, abs_path, )
|
||||
return io.NodeOutput(False, validate_local_path["msg"], rel_path, abs_path)
|
||||
|
||||
s3_instance = S3Helper.get_instance(s3_config)
|
||||
if ino_is_err(s3_instance):
|
||||
return (False, s3_instance["msg"], rel_path, abs_path, )
|
||||
return io.NodeOutput(False, s3_instance["msg"], rel_path, abs_path)
|
||||
s3_instance = s3_instance["instance"]
|
||||
|
||||
s3_result = await s3_instance.upload_file(
|
||||
s3_key=s3_key,
|
||||
local_file_path=abs_path,
|
||||
)
|
||||
s3_result = await s3_instance.upload_file(s3_key=s3_key, local_file_path=abs_path)
|
||||
if s3_result["success"] and delete_local:
|
||||
os.remove(abs_path)
|
||||
|
||||
return (s3_result["success"], s3_result["msg"], rel_path, abs_path, )
|
||||
return io.NodeOutput(s3_result["success"], s3_result["msg"], rel_path, abs_path)
|
||||
|
||||
@@ -1,70 +1,71 @@
|
||||
import os
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
import folder_paths
|
||||
from inopyutils import ino_is_err
|
||||
|
||||
from inopyutils import ino_ok, ino_err, ino_is_err
|
||||
from comfy_api.latest import io
|
||||
|
||||
from .s3_helper import S3Helper, S3_EMPTY_CONFIG_STRING
|
||||
from ..node_helper import any_type, PARENT_FOLDER_OPTIONS, resolve_comfy_path
|
||||
from ..node_helper import PARENT_FOLDER_OPTIONS, resolve_comfy_path
|
||||
|
||||
class InoS3UploadFolder:
|
||||
|
||||
class InoS3UploadFolder(io.ComfyNode):
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required":{
|
||||
"execute": (any_type,),
|
||||
"enabled": ("BOOLEAN", {"default": True, "label_off": "OFF", "label_on": "ON"}),
|
||||
"s3_key": ("STRING", {"default": ""}),
|
||||
"parent_folder": (PARENT_FOLDER_OPTIONS, ),
|
||||
"folder": ("STRING", {"default": ""}),
|
||||
"delete_local": ("BOOLEAN", {"default": True}),
|
||||
},
|
||||
"optional": {
|
||||
"s3_config": ("STRING", {"default": S3_EMPTY_CONFIG_STRING, "tooltip": "you can leave it empty and pass it with env vars"}),
|
||||
"bucket_name": ("STRING", {"default": "default"}),
|
||||
"max_concurrent": ("INT", {"default": 5, "min": 1, "max": 10}),
|
||||
"verify_with_s3": ("BOOLEAN", {"default": False}),
|
||||
}
|
||||
}
|
||||
def define_schema(cls):
|
||||
return io.Schema(
|
||||
node_id="InoS3UploadFolder",
|
||||
display_name="Ino S3 Upload Folder",
|
||||
category="InoS3Helper",
|
||||
description="Uploads an entire local folder to S3 with concurrent uploads and optional verification.",
|
||||
is_output_node=True,
|
||||
inputs=[
|
||||
io.AnyType.Input("execute"),
|
||||
io.Boolean.Input("enabled", default=True, label_off="OFF", label_on="ON"),
|
||||
io.String.Input("s3_key", default=""),
|
||||
io.Combo.Input("parent_folder", options=PARENT_FOLDER_OPTIONS),
|
||||
io.String.Input("folder", default=""),
|
||||
io.Boolean.Input("delete_local", default=True),
|
||||
io.String.Input("s3_config", default=S3_EMPTY_CONFIG_STRING, optional=True, tooltip="you can leave it empty and pass it with env vars"),
|
||||
io.String.Input("bucket_name", default="default", optional=True),
|
||||
io.Int.Input("max_concurrent", default=5, min=1, max=10, optional=True),
|
||||
io.Boolean.Input("verify_with_s3", default=False, optional=True),
|
||||
],
|
||||
outputs=[
|
||||
io.Boolean.Output(display_name="success"),
|
||||
io.String.Output(display_name="message"),
|
||||
io.String.Output(display_name="rel_path"),
|
||||
io.String.Output(display_name="abs_path"),
|
||||
io.Int.Output(display_name="total_files"),
|
||||
io.Int.Output(display_name="uploaded_successfully"),
|
||||
io.Int.Output(display_name="failed_uploads"),
|
||||
io.String.Output(display_name="errors"),
|
||||
],
|
||||
)
|
||||
|
||||
CATEGORY = "InoS3Helper"
|
||||
RETURN_TYPES = ("BOOLEAN", "STRING", "STRING", "STRING", "INT", "INT", "INT", "STRING", )
|
||||
RETURN_NAMES = ("success", "message", "rel_path", "abs_path", "total_files", "uploaded_successfully", "failed_uploads", "errors", )
|
||||
FUNCTION = "function"
|
||||
OUTPUT_NODE = True
|
||||
|
||||
async def function(self, execute, enabled, s3_key, parent_folder, folder, delete_local, s3_config=None, bucket_name=None, max_concurrent=5, verify_with_s3=False):
|
||||
@classmethod
|
||||
async def execute(cls, execute, enabled, s3_key, parent_folder, folder, delete_local, s3_config=None, bucket_name=None, max_concurrent=5, verify_with_s3=False) -> io.NodeOutput:
|
||||
if not enabled:
|
||||
return (False, "", "", "", 0, 0, 0, "", )
|
||||
|
||||
return io.NodeOutput(False, "", "", "", 0, 0, 0, "")
|
||||
if not execute:
|
||||
return (False, "", "", "", 0, 0, 0, "", )
|
||||
return io.NodeOutput(False, "", "", "", 0, 0, 0, "")
|
||||
|
||||
validate_s3_key = S3Helper.validate_s3_key(s3_key)
|
||||
if not validate_s3_key["success"]:
|
||||
return (False, validate_s3_key["msg"], "", "", 0, 0, 0, "", )
|
||||
return io.NodeOutput(False, validate_s3_key["msg"], "", "", 0, 0, 0, "")
|
||||
|
||||
rel_path, abs_path = resolve_comfy_path(parent_folder, folder)
|
||||
|
||||
local_upload_path = Path(abs_path)
|
||||
validate_local_path = S3Helper.validate_local_path(local_upload_path)
|
||||
validate_local_path = S3Helper.validate_local_path(Path(abs_path))
|
||||
if not validate_local_path["success"]:
|
||||
return (False, validate_local_path["msg"], rel_path, abs_path, 0, 0, 0, "", )
|
||||
return io.NodeOutput(False, validate_local_path["msg"], rel_path, abs_path, 0, 0, 0, "")
|
||||
|
||||
s3_instance = S3Helper.get_instance(s3_config)
|
||||
if ino_is_err(s3_instance):
|
||||
return (False, s3_instance["msg"], rel_path, abs_path, 0, 0, 0, "", )
|
||||
return io.NodeOutput(False, s3_instance["msg"], rel_path, abs_path, 0, 0, 0, "")
|
||||
s3_instance = s3_instance["instance"]
|
||||
|
||||
s3_result = await s3_instance.upload_folder(
|
||||
s3_folder_key=s3_key,
|
||||
local_folder_path=abs_path,
|
||||
max_concurrent=max_concurrent,
|
||||
verify=verify_with_s3
|
||||
)
|
||||
s3_result = await s3_instance.upload_folder(s3_folder_key=s3_key, local_folder_path=abs_path, max_concurrent=max_concurrent, verify=verify_with_s3)
|
||||
if s3_result["success"] and delete_local:
|
||||
shutil.rmtree(local_upload_path)
|
||||
shutil.rmtree(Path(abs_path))
|
||||
|
||||
return (s3_result["success"], s3_result["msg"], rel_path, abs_path, s3_result["total_files"], s3_result["uploaded_successfully"], s3_result["failed_uploads"], str(s3_result["errors"]), )
|
||||
return io.NodeOutput(s3_result["success"], s3_result["msg"], rel_path, abs_path, s3_result["total_files"], s3_result["uploaded_successfully"], s3_result["failed_uploads"], str(s3_result["errors"]))
|
||||
|
||||
@@ -1,62 +1,69 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
from PIL import Image, ImageOps, ImageSequence
|
||||
from PIL import Image
|
||||
from PIL.PngImagePlugin import PngInfo
|
||||
import numpy as np
|
||||
from inopyutils import InoJsonHelper, ino_ok, ino_err, ino_is_err, InoUtilHelper
|
||||
from inopyutils import InoJsonHelper, ino_is_err, InoUtilHelper
|
||||
|
||||
import folder_paths
|
||||
from comfy.cli_args import args
|
||||
from comfy_api.latest import io
|
||||
|
||||
from .s3_helper import S3Helper, S3_EMPTY_CONFIG_STRING
|
||||
from ..node_helper import any_type, PARENT_FOLDER_OPTIONS, resolve_comfy_path
|
||||
from ..node_helper import PARENT_FOLDER_OPTIONS, resolve_comfy_path
|
||||
|
||||
class InoS3UploadImage:
|
||||
|
||||
class InoS3UploadImage(io.ComfyNode):
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"execute": (any_type,),
|
||||
"enabled": ("BOOLEAN", {"default": True, "label_off": "OFF", "label_on": "ON"}),
|
||||
"images": ("IMAGE",),
|
||||
"s3_key": ("STRING", {"default": ""}),
|
||||
"parent_folder": (PARENT_FOLDER_OPTIONS, {"default": "temp"}),
|
||||
"folder": ("STRING", {"default": ""}),
|
||||
"filename": ("STRING", {"default": ""})
|
||||
},
|
||||
"optional": {
|
||||
"s3_config": ("STRING", {"default": S3_EMPTY_CONFIG_STRING, "tooltip": "you can leave it empty and pass it with env vars"}),
|
||||
"compress_level": ("INT", {"default": 4, "min": 1, "max": 9}),
|
||||
"date_time_as_name": ("BOOLEAN", {"default": False}),
|
||||
},
|
||||
}
|
||||
def define_schema(cls):
|
||||
return io.Schema(
|
||||
node_id="InoS3UploadImage",
|
||||
display_name="Ino S3 Upload Image",
|
||||
category="InoS3Helper",
|
||||
description="Saves images locally as PNG then uploads them to S3.",
|
||||
is_output_node=True,
|
||||
inputs=[
|
||||
io.AnyType.Input("execute"),
|
||||
io.Boolean.Input("enabled", default=True, label_off="OFF", label_on="ON"),
|
||||
io.Image.Input("images"),
|
||||
io.String.Input("s3_key", default=""),
|
||||
io.Combo.Input("parent_folder", options=PARENT_FOLDER_OPTIONS, default="temp"),
|
||||
io.String.Input("folder", default=""),
|
||||
io.String.Input("filename", default=""),
|
||||
io.String.Input("s3_config", default=S3_EMPTY_CONFIG_STRING, optional=True, tooltip="you can leave it empty and pass it with env vars"),
|
||||
io.Int.Input("compress_level", default=4, min=1, max=9, optional=True),
|
||||
io.Boolean.Input("date_time_as_name", default=False, optional=True),
|
||||
],
|
||||
outputs=[
|
||||
io.Image.Output(display_name="images"),
|
||||
io.Boolean.Output(display_name="success"),
|
||||
io.String.Output(display_name="message"),
|
||||
io.String.Output(display_name="rel_path"),
|
||||
io.String.Output(display_name="abs_path"),
|
||||
io.String.Output(display_name="file_names"),
|
||||
io.String.Output(display_name="s3_image_paths"),
|
||||
],
|
||||
)
|
||||
|
||||
RETURN_TYPES = ("IMAGE", "BOOLEAN", "STRING", "STRING", "STRING", "STRING", "STRING",)
|
||||
RETURN_NAMES = ("images", "success", "message", "rel_path", "abs_path", "file_names", "s3_image_paths",)
|
||||
FUNCTION = "function"
|
||||
OUTPUT_NODE = True
|
||||
CATEGORY = "InoS3Helper"
|
||||
|
||||
async def function(self, execute, enabled, images, s3_key, parent_folder, folder, filename, s3_config=None, compress_level=4, date_time_as_name=False):
|
||||
@classmethod
|
||||
async def execute(cls, execute, enabled, images, s3_key, parent_folder, folder, filename, s3_config=None, compress_level=4, date_time_as_name=False) -> io.NodeOutput:
|
||||
if not enabled:
|
||||
return (images, False, "", "", "", "", "",)
|
||||
|
||||
return io.NodeOutput(images, False, "", "", "", "", "")
|
||||
if not execute:
|
||||
return (images, False, "", "", "", "", "",)
|
||||
return io.NodeOutput(images, False, "", "", "", "", "")
|
||||
|
||||
validate_s3_key = S3Helper.validate_s3_key(s3_key)
|
||||
if not validate_s3_key["success"]:
|
||||
return (images, False, validate_s3_key["msg"], "", "", "", "")
|
||||
return io.NodeOutput(images, False, validate_s3_key["msg"], "", "", "", "")
|
||||
|
||||
if date_time_as_name:
|
||||
filename = InoUtilHelper.get_date_time_utc_base64()
|
||||
|
||||
rel_path, abs_path = resolve_comfy_path(parent_folder, folder)
|
||||
|
||||
full_output_folder, file_prefix, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(filename, abs_path, images[0].shape[1], images[0].shape[0])
|
||||
results:dict = {}
|
||||
full_output_folder, file_prefix, counter, subfolder, _ = folder_paths.get_save_image_path(filename, abs_path, images[0].shape[1], images[0].shape[0])
|
||||
results = {}
|
||||
for (batch_number, image) in enumerate(images):
|
||||
i = 255. * image.cpu().numpy()
|
||||
img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8))
|
||||
@@ -69,31 +76,22 @@ class InoS3UploadImage:
|
||||
file_w_ext = file + ".png"
|
||||
full_path = os.path.join(full_output_folder, file_w_ext)
|
||||
img.save(full_path, pnginfo=metadata, compress_level=compress_level)
|
||||
results[batch_number] = {
|
||||
"filename": file,
|
||||
"filename_w_ext": file_w_ext,
|
||||
"full_path": full_path,
|
||||
}
|
||||
results[batch_number] = {"filename": file, "filename_w_ext": file_w_ext, "full_path": full_path}
|
||||
counter += 1
|
||||
img.close()
|
||||
|
||||
s3_instance = S3Helper.get_instance(s3_config)
|
||||
if ino_is_err(s3_instance):
|
||||
return (images, False, s3_instance["msg"], rel_path, abs_path, "", "")
|
||||
return io.NodeOutput(images, False, s3_instance["msg"], rel_path, abs_path, "", "")
|
||||
s3_instance = s3_instance["instance"]
|
||||
|
||||
for index in results:
|
||||
s3_full_key = s3_key + "/" + results[index]["filename_w_ext"]
|
||||
s3_result = await s3_instance.upload_file(
|
||||
s3_key=s3_full_key,
|
||||
local_file_path=results[index]["full_path"],
|
||||
)
|
||||
s3_result = await s3_instance.upload_file(s3_key=s3_full_key, local_file_path=results[index]["full_path"])
|
||||
results[index]["s3_success"] = s3_result["success"]
|
||||
results[index]["s3_msg"] = s3_result["msg"]
|
||||
results[index]["s3_key"] = s3_full_key
|
||||
|
||||
result_str = InoJsonHelper.dict_to_string(results)['data']
|
||||
|
||||
final_success = True
|
||||
final_message = ""
|
||||
for index in results:
|
||||
@@ -103,12 +101,9 @@ class InoS3UploadImage:
|
||||
break
|
||||
|
||||
if not final_success:
|
||||
return (images, final_success, final_message, rel_path, abs_path, "", "")
|
||||
return io.NodeOutput(images, final_success, final_message, rel_path, abs_path, "", "")
|
||||
|
||||
s3_paths = []
|
||||
file_names = []
|
||||
for index in results:
|
||||
s3_paths.append(results[index]["s3_key"])
|
||||
file_names.append(results[index]["filename"])
|
||||
s3_paths = [results[i]["s3_key"] for i in results]
|
||||
file_names = [results[i]["filename"] for i in results]
|
||||
|
||||
return (images, True, "Success", rel_path, abs_path, str(file_names), str(s3_paths), )
|
||||
return io.NodeOutput(images, True, "Success", rel_path, abs_path, str(file_names), str(s3_paths))
|
||||
|
||||
@@ -1,75 +1,76 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
from PIL import Image, ImageOps, ImageSequence
|
||||
from PIL.PngImagePlugin import PngInfo
|
||||
import numpy as np
|
||||
from inopyutils import InoJsonHelper, InoFileHelper, ino_ok, ino_err, ino_is_err, InoUtilHelper
|
||||
from inopyutils import InoJsonHelper, InoFileHelper, ino_is_err, InoUtilHelper
|
||||
|
||||
import folder_paths
|
||||
from comfy.cli_args import args
|
||||
from comfy_api.latest import io
|
||||
|
||||
from .s3_helper import S3Helper, S3_EMPTY_CONFIG_STRING
|
||||
from ..node_helper import any_type, PARENT_FOLDER_OPTIONS, resolve_comfy_path
|
||||
from ..node_helper import PARENT_FOLDER_OPTIONS, resolve_comfy_path
|
||||
|
||||
class InoS3UploadString:
|
||||
|
||||
class InoS3UploadString(io.ComfyNode):
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"execute": (any_type,),
|
||||
"enabled": ("BOOLEAN", {"default": True, "label_off": "OFF", "label_on": "ON"}),
|
||||
"string": ("STRING",),
|
||||
"save_as": (["txt", "json", "ini"], ),
|
||||
"s3_key": ("STRING", {"default": ""}),
|
||||
"parent_folder": (PARENT_FOLDER_OPTIONS, {"default": "temp"}),
|
||||
"folder": ("STRING", {"default": ""}),
|
||||
"filename": ("STRING", {"default": ""})
|
||||
},
|
||||
"optional": {
|
||||
"s3_config": ("STRING", {"default": S3_EMPTY_CONFIG_STRING, "tooltip": "you can leave it empty and pass it with env vars"}),
|
||||
"date_time_as_name": ("BOOLEAN", {"default": False}),
|
||||
},
|
||||
}
|
||||
def define_schema(cls):
|
||||
return io.Schema(
|
||||
node_id="InoS3UploadString",
|
||||
display_name="Ino S3 Upload String",
|
||||
category="InoS3Helper",
|
||||
description="Saves a string as a file (txt/json/ini) locally then uploads it to S3.",
|
||||
is_output_node=True,
|
||||
inputs=[
|
||||
io.AnyType.Input("execute"),
|
||||
io.Boolean.Input("enabled", default=True, label_off="OFF", label_on="ON"),
|
||||
io.String.Input("string"),
|
||||
io.Combo.Input("save_as", options=["txt", "json", "ini"]),
|
||||
io.String.Input("s3_key", default=""),
|
||||
io.Combo.Input("parent_folder", options=PARENT_FOLDER_OPTIONS, default="temp"),
|
||||
io.String.Input("folder", default=""),
|
||||
io.String.Input("filename", default=""),
|
||||
io.String.Input("s3_config", default=S3_EMPTY_CONFIG_STRING, optional=True, tooltip="you can leave it empty and pass it with env vars"),
|
||||
io.Boolean.Input("date_time_as_name", default=False, optional=True),
|
||||
],
|
||||
outputs=[
|
||||
io.String.Output(display_name="string"),
|
||||
io.Boolean.Output(display_name="success"),
|
||||
io.String.Output(display_name="message"),
|
||||
io.String.Output(display_name="rel_path"),
|
||||
io.String.Output(display_name="abs_path"),
|
||||
io.String.Output(display_name="file_name"),
|
||||
io.String.Output(display_name="s3_string_path"),
|
||||
],
|
||||
)
|
||||
|
||||
RETURN_TYPES = ("STRING", "BOOLEAN", "STRING", "STRING", "STRING", "STRING", "STRING",)
|
||||
RETURN_NAMES = ("STRING", "success", "message", "rel_path", "abs_path", "file_name", "s3_string_path",)
|
||||
FUNCTION = "function"
|
||||
OUTPUT_NODE = True
|
||||
CATEGORY = "InoS3Helper"
|
||||
|
||||
async def function(self, execute, enabled, string, save_as, s3_key, parent_folder, folder, filename, s3_config=None, date_time_as_name=False):
|
||||
@classmethod
|
||||
async def execute(cls, execute, enabled, string, save_as, s3_key, parent_folder, folder, filename, s3_config=None, date_time_as_name=False) -> io.NodeOutput:
|
||||
if not enabled:
|
||||
return (string, False, "", "", "", "", "",)
|
||||
|
||||
return io.NodeOutput(string, False, "", "", "", "", "")
|
||||
if not execute:
|
||||
return (string, False, "", "", "", "", "",)
|
||||
return io.NodeOutput(string, False, "", "", "", "", "")
|
||||
|
||||
if isinstance(filename, list):
|
||||
if len(filename) == 1:
|
||||
filename = str(filename[0])
|
||||
else:
|
||||
return (string, False, "file name is list", "", "", "", "",)
|
||||
elif isinstance(filename, str):
|
||||
pass
|
||||
else:
|
||||
return (string, False, "file name not string", "", "", "", "",)
|
||||
return io.NodeOutput(string, False, "file name is list", "", "", "", "")
|
||||
elif not isinstance(filename, str):
|
||||
return io.NodeOutput(string, False, "file name not string", "", "", "", "")
|
||||
|
||||
validate_s3_key = S3Helper.validate_s3_key(s3_key)
|
||||
if not validate_s3_key["success"]:
|
||||
return (string, False, validate_s3_key["msg"], "", "", "", "",)
|
||||
return io.NodeOutput(string, False, validate_s3_key["msg"], "", "", "", "")
|
||||
|
||||
if date_time_as_name:
|
||||
filename = InoUtilHelper.get_date_time_utc_base64()
|
||||
|
||||
rel_path, abs_path = resolve_comfy_path(parent_folder, folder)
|
||||
|
||||
full_output_folder, file_prefix, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(filename, abs_path, 0, 0)
|
||||
full_output_folder, file_prefix, counter, subfolder, _ = folder_paths.get_save_image_path(filename, abs_path, 0, 0)
|
||||
|
||||
filename_with_batch_num = file_prefix.replace("%batch_num%", "0")
|
||||
file = f"{filename_with_batch_num}_{counter:05}"
|
||||
file_w_ext= f"{file}.{save_as}"
|
||||
file_w_ext = f"{file}.{save_as}"
|
||||
full_path = os.path.join(full_output_folder, file_w_ext)
|
||||
|
||||
if save_as == "json":
|
||||
@@ -78,19 +79,16 @@ class InoS3UploadString:
|
||||
save_file = await InoFileHelper.save_string_as_file(string, full_path)
|
||||
|
||||
if not save_file["success"]:
|
||||
return (string, False, save_file["msg"], rel_path, abs_path, "", "", )
|
||||
return io.NodeOutput(string, False, save_file["msg"], rel_path, abs_path, "", "")
|
||||
|
||||
s3_instance = S3Helper.get_instance(s3_config)
|
||||
if ino_is_err(s3_instance):
|
||||
return (string, False, s3_instance["msg"], rel_path, abs_path, "", "", )
|
||||
return io.NodeOutput(string, False, s3_instance["msg"], rel_path, abs_path, "", "")
|
||||
s3_instance = s3_instance["instance"]
|
||||
|
||||
s3_full_key = f"{s3_key.rstrip('/')}/{filename}.{save_as}"
|
||||
s3_result = await s3_instance.upload_file(
|
||||
s3_key=s3_full_key,
|
||||
local_file_path=full_path,
|
||||
)
|
||||
s3_result = await s3_instance.upload_file(s3_key=s3_full_key, local_file_path=full_path)
|
||||
if not s3_result["success"]:
|
||||
return (string, False, s3_result["msg"], rel_path, abs_path, "", "",)
|
||||
return io.NodeOutput(string, False, s3_result["msg"], rel_path, abs_path, "", "")
|
||||
|
||||
return (string, True, "Success", rel_path, abs_path, file_w_ext, s3_full_key, )
|
||||
return io.NodeOutput(string, True, "Success", rel_path, abs_path, file_w_ext, s3_full_key)
|
||||
|
||||
@@ -1,56 +1,58 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
from PIL import Image, ImageOps, ImageSequence
|
||||
from PIL.PngImagePlugin import PngInfo
|
||||
import numpy as np
|
||||
from inopyutils import InoJsonHelper, ino_ok, ino_err, ino_is_err, InoUtilHelper
|
||||
from inopyutils import ino_is_err, InoUtilHelper
|
||||
|
||||
import folder_paths
|
||||
from comfy.cli_args import args
|
||||
from comfy_api.latest import ComfyExtension, io, ui, Input, InputImpl, Types
|
||||
from comfy_api.latest import io, Input, Types
|
||||
|
||||
from .s3_helper import S3Helper, S3_EMPTY_CONFIG_STRING
|
||||
from ..node_helper import any_type, PARENT_FOLDER_OPTIONS, resolve_comfy_path
|
||||
from ..node_helper import PARENT_FOLDER_OPTIONS, resolve_comfy_path
|
||||
|
||||
class InoS3UploadVideo:
|
||||
|
||||
class InoS3UploadVideo(io.ComfyNode):
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"execute": (any_type,),
|
||||
"enabled": ("BOOLEAN", {"default": True, "label_off": "OFF", "label_on": "ON"}),
|
||||
"video": ("VIDEO",),
|
||||
"s3_key": ("STRING", {"default": ""}),
|
||||
"parent_folder": (PARENT_FOLDER_OPTIONS, {"default": "temp"}),
|
||||
"folder": ("STRING", {"default": ""}),
|
||||
"filename": ("STRING", {"default": ""})
|
||||
},
|
||||
"optional": {
|
||||
"s3_config": ("STRING", {"default": S3_EMPTY_CONFIG_STRING, "tooltip": "you can leave it empty and pass it with env vars"}),
|
||||
"date_time_as_name": ("BOOLEAN", {"default": False}),
|
||||
"video_format": (("mp4", "auto"), {}),
|
||||
"video_codec": (("h264", "auto"), {}),
|
||||
},
|
||||
}
|
||||
def define_schema(cls):
|
||||
return io.Schema(
|
||||
node_id="InoS3UploadVideo",
|
||||
display_name="Ino S3 Upload Video",
|
||||
category="InoS3Helper",
|
||||
description="Saves video locally then uploads it to S3.",
|
||||
is_output_node=True,
|
||||
inputs=[
|
||||
io.AnyType.Input("execute"),
|
||||
io.Boolean.Input("enabled", default=True, label_off="OFF", label_on="ON"),
|
||||
io.Video.Input("video"),
|
||||
io.String.Input("s3_key", default=""),
|
||||
io.Combo.Input("parent_folder", options=PARENT_FOLDER_OPTIONS, default="temp"),
|
||||
io.String.Input("folder", default=""),
|
||||
io.String.Input("filename", default=""),
|
||||
io.String.Input("s3_config", default=S3_EMPTY_CONFIG_STRING, optional=True, tooltip="you can leave it empty and pass it with env vars"),
|
||||
io.Boolean.Input("date_time_as_name", default=False, optional=True),
|
||||
io.Combo.Input("video_format", options=["mp4", "auto"], optional=True),
|
||||
io.Combo.Input("video_codec", options=["h264", "auto"], optional=True),
|
||||
],
|
||||
outputs=[
|
||||
io.Video.Output(display_name="video"),
|
||||
io.Boolean.Output(display_name="success"),
|
||||
io.String.Output(display_name="message"),
|
||||
io.String.Output(display_name="rel_path"),
|
||||
io.String.Output(display_name="abs_path"),
|
||||
io.String.Output(display_name="file_name"),
|
||||
io.String.Output(display_name="s3_video_path"),
|
||||
],
|
||||
)
|
||||
|
||||
RETURN_TYPES = ("Video", "BOOLEAN", "STRING", "STRING", "STRING", "STRING", "STRING",)
|
||||
RETURN_NAMES = ("video", "success", "message", "rel_path", "abs_path", "file_name", "s3_video_path",)
|
||||
FUNCTION = "function"
|
||||
OUTPUT_NODE = True
|
||||
CATEGORY = "InoS3Helper"
|
||||
|
||||
async def function(self, execute, enabled, video: Input.Video, s3_key, parent_folder, folder, filename, s3_config=None, date_time_as_name=False, video_format="mp4", video_codec="h264"):
|
||||
@classmethod
|
||||
async def execute(cls, execute, enabled, video: Input.Video, s3_key, parent_folder, folder, filename, s3_config=None, date_time_as_name=False, video_format="mp4", video_codec="h264") -> io.NodeOutput:
|
||||
if not enabled:
|
||||
return (video, False, "", "", "", "", "",)
|
||||
|
||||
return io.NodeOutput(video, False, "", "", "", "", "")
|
||||
if not execute:
|
||||
return (video, False, "", "", "", "", "",)
|
||||
return io.NodeOutput(video, False, "", "", "", "", "")
|
||||
|
||||
validate_s3_key = S3Helper.validate_s3_key(s3_key)
|
||||
if not validate_s3_key["success"]:
|
||||
return (video, False, validate_s3_key["msg"], "", "", "", "")
|
||||
return io.NodeOutput(video, False, validate_s3_key["msg"], "", "", "", "")
|
||||
|
||||
if date_time_as_name:
|
||||
filename = InoUtilHelper.get_date_time_utc_base64()
|
||||
@@ -58,36 +60,19 @@ class InoS3UploadVideo:
|
||||
rel_path, abs_path = resolve_comfy_path(parent_folder, folder)
|
||||
width, height = video.get_dimensions()
|
||||
|
||||
full_output_folder, file_prefix, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(
|
||||
filename,
|
||||
abs_path,
|
||||
width,
|
||||
height
|
||||
)
|
||||
|
||||
saved_metadata = None
|
||||
full_output_folder, file_prefix, counter, subfolder, _ = folder_paths.get_save_image_path(filename, abs_path, width, height)
|
||||
|
||||
filename_w_ext = f"{file_prefix}.{Types.VideoContainer.get_extension(video_format)}"
|
||||
file_path = os.path.join(full_output_folder, filename_w_ext)
|
||||
|
||||
video.save_to(
|
||||
file_path,
|
||||
format=Types.VideoContainer(video_format),
|
||||
codec=video_codec,
|
||||
metadata=saved_metadata
|
||||
)
|
||||
video.save_to(file_path, format=Types.VideoContainer(video_format), codec=Types.VideoCodec(video_codec))
|
||||
|
||||
s3_instance = S3Helper.get_instance(s3_config)
|
||||
if ino_is_err(s3_instance):
|
||||
return (video, False, s3_instance["msg"], rel_path, abs_path, "", "")
|
||||
return io.NodeOutput(video, False, s3_instance["msg"], rel_path, abs_path, "", "")
|
||||
s3_instance = s3_instance["instance"]
|
||||
|
||||
s3_full_key = s3_key + "/" + filename_w_ext
|
||||
s3_result = await s3_instance.upload_file(
|
||||
s3_key=s3_full_key,
|
||||
local_file_path=file_path,
|
||||
)
|
||||
if not s3_result["success"]:
|
||||
return (video, s3_result["success"], s3_result["msg"], rel_path, abs_path, filename, s3_full_key,)
|
||||
s3_result = await s3_instance.upload_file(s3_key=s3_full_key, local_file_path=file_path)
|
||||
|
||||
return (video, s3_result["success"], s3_result["msg"], rel_path, abs_path, filename, s3_full_key,)
|
||||
return io.NodeOutput(video, s3_result["success"], s3_result["msg"], rel_path, abs_path, filename, s3_full_key)
|
||||
|
||||
@@ -2,70 +2,66 @@ from pathlib import Path
|
||||
|
||||
from inopyutils import ino_is_err
|
||||
|
||||
import folder_paths
|
||||
from comfy_api.latest import io
|
||||
|
||||
from .s3_helper import S3Helper, S3_EMPTY_CONFIG_STRING
|
||||
from ..node_helper import any_type, PARENT_FOLDER_OPTIONS, resolve_comfy_path
|
||||
from ..node_helper import PARENT_FOLDER_OPTIONS, resolve_comfy_path
|
||||
|
||||
class InoS3VerifyFile:
|
||||
|
||||
class InoS3VerifyFile(io.ComfyNode):
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"execute": (any_type,),
|
||||
"enabled": ("BOOLEAN", {"default": True, "label_off": "OFF", "label_on": "ON"}),
|
||||
"s3_key": ("STRING", {"default": ""}),
|
||||
"parent_folder": (PARENT_FOLDER_OPTIONS,),
|
||||
"folder": ("STRING", {"default": ""}),
|
||||
"filename": ("STRING", {"default": ""}),
|
||||
},
|
||||
"optional": {
|
||||
"s3_config": ("STRING", {"default": S3_EMPTY_CONFIG_STRING, "tooltip": "you can leave it empty and pass it with env vars"}),
|
||||
"use_md5": ("BOOLEAN", {"default": False}),
|
||||
"use_sha256": ("BOOLEAN", {"default": False}),
|
||||
}
|
||||
}
|
||||
def define_schema(cls):
|
||||
return io.Schema(
|
||||
node_id="InoS3VerifyFile",
|
||||
display_name="Ino S3 Verify File",
|
||||
category="InoS3Helper",
|
||||
description="Verifies a local file exists in S3 and optionally checks integrity via hash.",
|
||||
inputs=[
|
||||
io.AnyType.Input("execute"),
|
||||
io.Boolean.Input("enabled", default=True, label_off="OFF", label_on="ON"),
|
||||
io.String.Input("s3_key", default=""),
|
||||
io.Combo.Input("parent_folder", options=PARENT_FOLDER_OPTIONS),
|
||||
io.String.Input("folder", default=""),
|
||||
io.String.Input("filename", default=""),
|
||||
io.String.Input("s3_config", default=S3_EMPTY_CONFIG_STRING, optional=True, tooltip="you can leave it empty and pass it with env vars"),
|
||||
io.Boolean.Input("use_md5", default=False, optional=True),
|
||||
io.Boolean.Input("use_sha256", default=False, optional=True),
|
||||
],
|
||||
outputs=[
|
||||
io.Boolean.Output(display_name="success"),
|
||||
io.String.Output(display_name="message"),
|
||||
io.String.Output(display_name="rel_path"),
|
||||
io.String.Output(display_name="abs_path"),
|
||||
io.Boolean.Output(display_name="exists_remote"),
|
||||
io.Boolean.Output(display_name="sizes_match"),
|
||||
],
|
||||
)
|
||||
|
||||
CATEGORY = "InoS3Helper"
|
||||
RETURN_TYPES = ("BOOLEAN", "STRING", "STRING", "STRING", "BOOLEAN", "BOOLEAN",)
|
||||
RETURN_NAMES = ("success", "message", "rel_path", "abs_path", "exists_remote", "sizes_match",)
|
||||
FUNCTION = "function"
|
||||
|
||||
async def function(self, execute, enabled, s3_key, parent_folder, folder, filename, s3_config=None, use_md5=False, use_sha256=False):
|
||||
@classmethod
|
||||
async def execute(cls, execute, enabled, s3_key, parent_folder, folder, filename, s3_config=None, use_md5=False, use_sha256=False) -> io.NodeOutput:
|
||||
if not enabled:
|
||||
return (False, "not enabled", "", "", False, False,)
|
||||
|
||||
return io.NodeOutput(False, "not enabled", "", "", False, False)
|
||||
if not execute:
|
||||
return (False, "execute empty", "", "", False, False,)
|
||||
return io.NodeOutput(False, "execute empty", "", "", False, False)
|
||||
|
||||
validate_s3_key = S3Helper.validate_s3_key(s3_key)
|
||||
if not validate_s3_key["success"]:
|
||||
return (False, validate_s3_key["msg"], "", "", False, False,)
|
||||
return io.NodeOutput(False, validate_s3_key["msg"], "", "", False, False)
|
||||
|
||||
rel_path, abs_path = resolve_comfy_path(parent_folder, folder, filename)
|
||||
|
||||
local_file_path = Path(abs_path)
|
||||
validate_local_path = S3Helper.validate_local_path(local_file_path)
|
||||
validate_local_path = S3Helper.validate_local_path(Path(abs_path))
|
||||
if not validate_local_path["success"]:
|
||||
return (False, validate_local_path["msg"], rel_path, abs_path, False, False,)
|
||||
return io.NodeOutput(False, validate_local_path["msg"], rel_path, abs_path, False, False)
|
||||
|
||||
s3_instance = S3Helper.get_instance(s3_config)
|
||||
if ino_is_err(s3_instance):
|
||||
return (False, s3_instance["msg"], rel_path, abs_path, False, False,)
|
||||
return io.NodeOutput(False, s3_instance["msg"], rel_path, abs_path, False, False)
|
||||
s3_instance = s3_instance["instance"]
|
||||
|
||||
s3_result = await s3_instance.verify_file(
|
||||
local_file_path=abs_path,
|
||||
s3_key=s3_key,
|
||||
use_md5=use_md5,
|
||||
use_sha256=use_sha256,
|
||||
)
|
||||
s3_result = await s3_instance.verify_file(local_file_path=abs_path, s3_key=s3_key, use_md5=use_md5, use_sha256=use_sha256)
|
||||
|
||||
return (
|
||||
s3_result["success"],
|
||||
s3_result["msg"],
|
||||
rel_path,
|
||||
abs_path,
|
||||
s3_result.get("exists_remote", False),
|
||||
s3_result.get("sizes_match", False),
|
||||
return io.NodeOutput(
|
||||
s3_result["success"], s3_result["msg"], rel_path, abs_path,
|
||||
s3_result.get("exists_remote", False), s3_result.get("sizes_match", False),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user