Files
2026-06-11 19:05:56 -04:00

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",
}