92 lines
3.1 KiB
Python
92 lines
3.1 KiB
Python
import os
|
|
import re
|
|
|
|
import folder_paths
|
|
|
|
|
|
class SaveTextFile:
|
|
def __init__(self):
|
|
self.output_dir = folder_paths.get_output_directory()
|
|
self.type = "output"
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"text": (
|
|
"STRING",
|
|
{
|
|
"default": "",
|
|
"multiline": True,
|
|
"dynamicPrompts": True,
|
|
"tooltip": "Text content to write to the output file.",
|
|
},
|
|
),
|
|
"filename_prefix": (
|
|
"STRING",
|
|
{
|
|
"default": "text/ComfyUI",
|
|
"tooltip": "Prefix for the saved file. Supports ComfyUI date/time tokens and subfolders, like the Save Image node.",
|
|
},
|
|
),
|
|
"file_extension": (
|
|
"STRING",
|
|
{
|
|
"default": "txt",
|
|
"tooltip": "File extension to use, without path separators. Examples: txt, json, md, csv.",
|
|
},
|
|
),
|
|
}
|
|
}
|
|
|
|
RETURN_TYPES = ()
|
|
FUNCTION = "save_text"
|
|
OUTPUT_NODE = True
|
|
|
|
CATEGORY = "save"
|
|
DESCRIPTION = "Saves text to a file in the ComfyUI output directory and reports it in API history like Save Image."
|
|
SEARCH_ALIASES = ["save", "save text", "save file", "export text", "write text"]
|
|
|
|
def save_text(self, text, filename_prefix="text/ComfyUI", file_extension="txt"):
|
|
extension = self._normalize_extension(file_extension)
|
|
full_output_folder, filename, counter, subfolder, _ = folder_paths.get_save_image_path(
|
|
filename_prefix,
|
|
self.output_dir,
|
|
)
|
|
|
|
file = f"{filename}_{counter:05}_.{extension}" if extension else f"{filename}_{counter:05}_"
|
|
path = os.path.join(full_output_folder, file)
|
|
|
|
with open(path, "w", encoding="utf-8", newline="") as output_file:
|
|
output_file.write(text)
|
|
|
|
result = {
|
|
"filename": file,
|
|
"subfolder": subfolder,
|
|
"type": self.type,
|
|
}
|
|
|
|
# The key is intentionally "images" to match SaveImage/SaveSVG API history output:
|
|
# history[prompt_id]["outputs"][node_id]["images"] -> [{filename, subfolder, type}]
|
|
return {"ui": {"images": [result]}}
|
|
|
|
@staticmethod
|
|
def _normalize_extension(file_extension):
|
|
extension = str(file_extension or "").strip().lstrip(".")
|
|
if not extension:
|
|
return ""
|
|
if os.path.basename(extension) != extension or "\\" in extension:
|
|
raise ValueError("file_extension must not contain path separators")
|
|
if not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._-]*", extension):
|
|
raise ValueError("file_extension may only contain letters, numbers, dots, underscores, and hyphens")
|
|
return extension
|
|
|
|
|
|
NODE_CLASS_MAPPINGS = {
|
|
"SaveTextFile": SaveTextFile,
|
|
}
|
|
|
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
|
"SaveTextFile": "Save Text/File",
|
|
}
|