@@ -28,13 +28,15 @@ def load_image_helper(path: str):
2828 pass
2929
3030 if not os .path .exists (path ):
31- raise FileNotFoundError ( f"Basic data handling: Image file not found: { path } " )
31+ return None
3232
3333 # Open and process the image
34- img = Image .open (path )
35- img = ImageOps .exif_transpose (img )
36-
37- return img
34+ try :
35+ img = Image .open (path )
36+ img = ImageOps .exif_transpose (img )
37+ return img
38+ except Exception :
39+ return None
3840
3941
4042def extract_mask_from_alpha (img ):
@@ -675,19 +677,33 @@ def INPUT_TYPES(cls):
675677 },
676678 }
677679
678- RETURN_TYPES = (IO .STRING ,)
679- RETURN_NAMES = ("text" ,)
680+ RETURN_TYPES = (IO .STRING , IO . BOOLEAN )
681+ RETURN_NAMES = ("text" , "exists" )
680682 CATEGORY = "Basic/Path"
681683 DESCRIPTION = cleandoc (__doc__ or "" )
682684 FUNCTION = "load_text"
683685
686+ @classmethod
687+ def IS_CHANGED (cls , path ):
688+ try :
689+ if os .path .exists (path ):
690+ return os .path .getmtime (path )
691+ except Exception :
692+ pass
693+ return float ("NaN" ) # Return NaN if file doesn't exist or can't access modification time
694+
684695 def load_text (self , path : str ):
685- if not os .path .exists (path ):
686- raise FileNotFoundError (f"Basic data handling: String file not found: { path } " )
696+ exists = os .path .exists (path )
687697
688- with open (path , "r" , encoding = "utf-8" ) as f :
689- text = f .read ()
690- return (text ,)
698+ if not exists :
699+ return ("" , False )
700+
701+ try :
702+ with open (path , "r" , encoding = "utf-8" ) as f :
703+ text = f .read ()
704+ return (text , True )
705+ except Exception :
706+ return ("" , False )
691707
692708
693709class PathLoadImageRGB (ComfyNodeABC ):
@@ -705,26 +721,40 @@ def INPUT_TYPES(cls):
705721 },
706722 }
707723
708- RETURN_TYPES = (IO .IMAGE ,)
709- RETURN_NAMES = ("image" ,)
724+ RETURN_TYPES = (IO .IMAGE , IO . BOOLEAN )
725+ RETURN_NAMES = ("image" , "exists" )
710726 CATEGORY = "Basic/Path"
711727 DESCRIPTION = cleandoc (__doc__ or "" )
712728 FUNCTION = "load_image_rgb"
713729
730+ @classmethod
731+ def IS_CHANGED (cls , path ):
732+ try :
733+ if os .path .exists (path ):
734+ return os .path .getmtime (path )
735+ except Exception :
736+ pass
737+ return float ("NaN" ) # Return NaN if file doesn't exist or can't access modification time
738+
714739 def load_image_rgb (self , path : str ):
715740 import numpy as np
716741 import torch
717742
718743 img = load_image_helper (path )
719744
745+ if img is None :
746+ # Create an empty 1x1 image
747+ empty_tensor = torch .zeros ((1 , 1 , 1 , 3 ), dtype = torch .float32 )
748+ return (empty_tensor , False )
749+
720750 # Convert to RGB (removing alpha if present)
721751 img_rgb = img .convert ("RGB" )
722752
723753 # Convert to tensor format expected by ComfyUI
724754 image_tensor = np .array (img_rgb ).astype (np .float32 ) / 255.0
725755 image_tensor = torch .from_numpy (image_tensor )[None ,]
726756
727- return (image_tensor ,)
757+ return (image_tensor , True )
728758
729759
730760class PathLoadImageRGBA (ComfyNodeABC ):
@@ -743,18 +773,33 @@ def INPUT_TYPES(cls):
743773 },
744774 }
745775
746- RETURN_TYPES = (IO .IMAGE , IO .MASK )
747- RETURN_NAMES = ("image" , "mask" )
776+ RETURN_TYPES = (IO .IMAGE , IO .MASK , IO . BOOLEAN )
777+ RETURN_NAMES = ("image" , "mask" , "exists" )
748778 CATEGORY = "Basic/Path"
749779 DESCRIPTION = cleandoc (__doc__ or "" )
750780 FUNCTION = "load_image_rgba"
751781
782+ @classmethod
783+ def IS_CHANGED (cls , path ):
784+ try :
785+ if os .path .exists (path ):
786+ return os .path .getmtime (path )
787+ except Exception :
788+ pass
789+ return float ("NaN" ) # Return NaN if file doesn't exist or can't access modification time
790+
752791 def load_image_rgba (self , path : str ):
753792 import numpy as np
754793 import torch
755794
756795 img = load_image_helper (path )
757796
797+ if img is None :
798+ # Create empty 1x1 image and mask
799+ empty_image = torch .zeros ((1 , 1 , 1 , 3 ), dtype = torch .float32 )
800+ empty_mask = torch .zeros ((1 , 1 , 1 ), dtype = torch .float32 )
801+ return (empty_image , empty_mask , False )
802+
758803 # Convert to RGB for the image
759804 img_rgb = img .convert ("RGB" )
760805
@@ -765,7 +810,7 @@ def load_image_rgba(self, path: str):
765810 # Extract alpha channel as mask
766811 mask_tensor = extract_mask_from_alpha (img )
767812
768- return (image_tensor , mask_tensor )
813+ return (image_tensor , mask_tensor , True )
769814
770815
771816class PathLoadMaskFromAlpha (ComfyNodeABC ):
@@ -784,16 +829,33 @@ def INPUT_TYPES(cls):
784829 },
785830 }
786831
787- RETURN_TYPES = (IO .MASK ,)
788- RETURN_NAMES = ("mask" ,)
832+ RETURN_TYPES = (IO .MASK , IO . BOOLEAN )
833+ RETURN_NAMES = ("mask" , "exists" )
789834 CATEGORY = "Basic/Path"
790835 DESCRIPTION = cleandoc (__doc__ or "" )
791836 FUNCTION = "load_mask_from_alpha"
792837
838+ @classmethod
839+ def IS_CHANGED (cls , path ):
840+ try :
841+ if os .path .exists (path ):
842+ return os .path .getmtime (path )
843+ except Exception :
844+ pass
845+ return float ("NaN" ) # Return NaN if file doesn't exist or can't access modification time
846+
793847 def load_mask_from_alpha (self , path : str ):
848+ import torch
849+
794850 img = load_image_helper (path )
851+
852+ if img is None :
853+ # Return empty 1x1 mask
854+ empty_mask = torch .zeros ((1 , 1 , 1 ), dtype = torch .float32 )
855+ return (empty_mask , False )
856+
795857 mask_tensor = extract_mask_from_alpha (img )
796- return (mask_tensor ,)
858+ return (mask_tensor , True )
797859
798860
799861class PathLoadMaskFromGreyscale (ComfyNodeABC ):
@@ -815,21 +877,38 @@ def INPUT_TYPES(cls):
815877 },
816878 }
817879
818- RETURN_TYPES = (IO .MASK ,)
819- RETURN_NAMES = ("mask" ,)
880+ RETURN_TYPES = (IO .MASK , IO . BOOLEAN )
881+ RETURN_NAMES = ("mask" , "exists" )
820882 CATEGORY = "Basic/Path"
821883 DESCRIPTION = cleandoc (__doc__ or "" )
822884 FUNCTION = "load_mask_from_greyscale"
823885
886+ @classmethod
887+ def IS_CHANGED (cls , path ):
888+ try :
889+ if os .path .exists (path ):
890+ return os .path .getmtime (path )
891+ except Exception :
892+ pass
893+ return float ("NaN" ) # Return NaN if file doesn't exist or can't access modification time
894+
824895 def load_mask_from_greyscale (self , path : str , invert : bool = False ):
896+ import torch
897+
825898 img = load_image_helper (path )
899+
900+ if img is None :
901+ # Return empty 1x1 mask
902+ empty_mask = torch .zeros ((1 , 1 , 1 ), dtype = torch .float32 )
903+ return (empty_mask , False )
904+
826905 mask_tensor = extract_mask_from_greyscale (img )
827906
828907 # Optionally invert the mask (1.0 - mask)
829908 if invert :
830909 mask_tensor = 1.0 - mask_tensor
831910
832- return (mask_tensor ,)
911+ return (mask_tensor , True )
833912
834913
835914class PathSaveStringFile (ComfyNodeABC ):
@@ -852,15 +931,16 @@ def INPUT_TYPES(cls):
852931 }
853932 }
854933
855- RETURN_TYPES = (IO .BOOLEAN )
856- RETURN_NAMES = ("success" )
934+ RETURN_TYPES = (IO .BOOLEAN , )
935+ RETURN_NAMES = ("success" , )
857936 CATEGORY = "Basic/Path"
858937 DESCRIPTION = cleandoc (__doc__ or "" )
859938 FUNCTION = "save_text"
860939 OUTPUT_NODE = True
861940
862941 def save_text (self , text : str , path : str , create_dirs : bool = True , encoding : str = "utf-8" ):
863942 if not path :
943+ print ("Basic data handling: Save failed - no path specified" )
864944 return (False ,)
865945
866946 try :
@@ -872,6 +952,7 @@ def save_text(self, text: str, path: str, create_dirs: bool = True, encoding: st
872952 with open (path , "w" , encoding = encoding ) as f :
873953 f .write (text )
874954
955+ print (f"Basic data handling: Successfully saved text to { path } " )
875956 return (True ,)
876957 except Exception as e :
877958 print (f"Basic data handling: Error saving text file: { e } " )
@@ -899,15 +980,16 @@ def INPUT_TYPES(cls):
899980 }
900981 }
901982
902- RETURN_TYPES = (IO .BOOLEAN )
903- RETURN_NAMES = ("success" )
983+ RETURN_TYPES = (IO .BOOLEAN , )
984+ RETURN_NAMES = ("success" , )
904985 CATEGORY = "Basic/Path"
905986 DESCRIPTION = cleandoc (__doc__ or "" )
906987 FUNCTION = "save_image"
907988 OUTPUT_NODE = True
908989
909990 def save_image (self , images , path : str , format : str = "png" , quality : int = 95 , create_dirs : bool = True ):
910991 if not path :
992+ print ("Basic data handling: Save failed - no path specified" )
911993 return (False ,)
912994
913995 # If the path doesn't have an extension or it doesn't match the format, add it
@@ -957,6 +1039,7 @@ def save_image(self, images, path: str, format: str = "png", quality: int = 95,
9571039 else :
9581040 pil_img .save (path , format = format .upper ())
9591041
1042+ print (f"Basic data handling: Successfully saved image to { path } " )
9601043 return (True ,)
9611044 except Exception as e :
9621045 print (f"Basic data handling: Error saving image: { e } " )
@@ -987,8 +1070,8 @@ def INPUT_TYPES(cls):
9871070 }
9881071 }
9891072
990- RETURN_TYPES = (IO .BOOLEAN )
991- RETURN_NAMES = ("success" )
1073+ RETURN_TYPES = (IO .BOOLEAN , )
1074+ RETURN_NAMES = ("success" , )
9921075 CATEGORY = "Basic/Path"
9931076 DESCRIPTION = cleandoc (__doc__ or "" )
9941077 FUNCTION = "save_image_with_mask"
@@ -998,6 +1081,7 @@ def save_image_with_mask(self, images, mask, path: str, format: str = "png",
9981081 quality : int = 95 , invert_mask : bool = False ,
9991082 create_dirs : bool = True ):
10001083 if not path :
1084+ print ("Basic data handling: Save failed - no path specified" )
10011085 return (False ,)
10021086
10031087 # Check format compatibility - needs to support alpha channel
@@ -1065,6 +1149,7 @@ def save_image_with_mask(self, images, mask, path: str, format: str = "png",
10651149 else :
10661150 pil_img_rgba .save (path , format = format .upper ())
10671151
1152+ print (f"Basic data handling: Successfully saved image with mask to { path } " )
10681153 return (True ,)
10691154 except Exception as e :
10701155 print (f"Basic data handling: Error saving image with mask: { e } " )
0 commit comments