176 lines
8.3 KiB
Python
176 lines
8.3 KiB
Python
import uuid
|
|
from pathlib import Path
|
|
from aiohttp import web
|
|
import folder_paths
|
|
from server import PromptServer
|
|
from comfy_execution.graph import ExecutionBlocker
|
|
import json
|
|
import time
|
|
|
|
TEMP_SUBFOLDER = "string-editor-persistent-state"
|
|
|
|
def get_persistent_temp_file_path(node_id: str) -> Path:
|
|
temp_dir = Path(folder_paths.get_temp_directory())
|
|
state_dir = temp_dir / TEMP_SUBFOLDER
|
|
state_dir.mkdir(parents=True, exist_ok=True)
|
|
return state_dir / f"state_{node_id}.json"
|
|
|
|
if hasattr(PromptServer, 'instance') and PromptServer.instance is not None:
|
|
routes = PromptServer.instance.routes
|
|
@routes.post('/string_editor/signal_continue')
|
|
async def signal_continue_handler(request: web.Request) -> web.Response:
|
|
try:
|
|
if request.content_type != 'application/json':
|
|
return web.json_response({"status": "error", "message": "Invalid content type"}, status=400)
|
|
data = await request.json()
|
|
node_id = data.get('node_id')
|
|
edited_string = data.get('edited_string')
|
|
if not node_id:
|
|
return web.json_response({"status": "error", "message": "node_id missing"}, status=400)
|
|
if edited_string is None:
|
|
edited_string = ""
|
|
temp_file = get_persistent_temp_file_path(node_id)
|
|
try:
|
|
state_data = {"edited_string": edited_string, "last_modified": time.time()}
|
|
with open(temp_file, 'w', encoding='utf-8') as f:
|
|
json.dump(state_data, f)
|
|
return web.json_response({"status": "ok", "message": "State saved, ready for re-queue."})
|
|
except IOError as e:
|
|
return web.json_response({"status": "error", "message": f"Failed to write state file: {e}"}, status=500)
|
|
except json.JSONDecodeError:
|
|
return web.json_response({"status": "error", "message": "Invalid JSON format"}, status=400)
|
|
except Exception:
|
|
return web.json_response({"status": "error", "message": "Internal server error"}, status=500)
|
|
|
|
class StringEditorPersistentTempFileNode:
|
|
@classmethod
|
|
def IS_CHANGED(cls, *args, **kwargs):
|
|
return str(uuid.uuid4())
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"input_string": ("STRING", {"forceInput": True}),
|
|
"split_tag": ("STRING", {"default": "", "tooltip": "Split a string into two segments using a tag."}),
|
|
"pause": ("BOOLEAN", {"default": False}),
|
|
},
|
|
"hidden": {
|
|
"prompt": "PROMPT",
|
|
"extra_pnginfo": "EXTRA_PNGINFO",
|
|
"unique_id": "UNIQUE_ID",
|
|
},
|
|
}
|
|
|
|
RETURN_TYPES = ("STRING", "STRING", "STRING")
|
|
RETURN_NAMES = ("edited_string", "split_1", "split_2")
|
|
FUNCTION = "edit_or_check_and_output"
|
|
CATEGORY = "🎤MW/MW-Audio-Tools"
|
|
OUTPUT_NODE = True
|
|
|
|
def edit_or_check_and_output(self,
|
|
input_string: str,
|
|
split_tag: str,
|
|
pause: bool,
|
|
unique_id: str,
|
|
prompt=None,
|
|
extra_pnginfo=None,
|
|
**kwargs
|
|
):
|
|
|
|
node_id = unique_id
|
|
temp_file = get_persistent_temp_file_path(node_id)
|
|
# --- REMOVE class variable self.current_input_string - it's not needed ---
|
|
# if self.current_input_string is None:
|
|
# self.current_input_string = input_string
|
|
# --------------------------------------------------------------------
|
|
|
|
# Initialize string_to_display with the CURRENT input
|
|
current_input_string = str(input_string)
|
|
string_to_display = current_input_string # <<< Start with current input
|
|
|
|
should_block = True
|
|
output_string = None # This will hold the value read from file if successful
|
|
|
|
try:
|
|
if temp_file.exists():
|
|
with open(temp_file, 'r', encoding='utf-8') as f:
|
|
state_data = json.load(f)
|
|
# Read the value from the file
|
|
value_from_file = state_data.get("edited_string", current_input_string) # Fallback needed?
|
|
# *** THIS IS THE KEY CHANGE ***
|
|
string_to_display = value_from_file # <<< Update string_to_display with file content
|
|
output_string = value_from_file # <<< Store the value intended for output
|
|
# *****************************
|
|
try:
|
|
temp_file.unlink()
|
|
should_block = False
|
|
except OSError as e:
|
|
# If delete fails, we must block to avoid losing the edit
|
|
print(f"[ERROR] Node {node_id}: Failed to delete state file {temp_file}: {e}. Forcing block.") # Added print/log
|
|
should_block = True
|
|
output_string = None # Cannot guarantee output if delete failed
|
|
string_to_display = value_from_file # Still display what we read before delete failed
|
|
else:
|
|
# File doesn't exist (first run or after successful continue)
|
|
string_to_display = current_input_string # Use current input
|
|
state_data = {"edited_string": string_to_display, "last_modified": time.time()}
|
|
try:
|
|
# Create the file to prepare for blocking
|
|
with open(temp_file, 'w', encoding='utf-8') as f:
|
|
json.dump(state_data, f)
|
|
except IOError as e:
|
|
print(f"[ERROR] Node {node_id}: Failed to create initial state file {temp_file}: {e}") # Added print/log
|
|
pass # Proceed to block anyway
|
|
should_block = True
|
|
except (IOError, json.JSONDecodeError) as e:
|
|
print(f"[ERROR] Node {node_id}: Error processing state file {temp_file}: {e}. Blocking.") # Added print/log
|
|
string_to_display = current_input_string # Fallback display
|
|
should_block = True
|
|
output_string = None
|
|
try:
|
|
temp_file.unlink() # Clean up potentially corrupt file
|
|
except OSError:
|
|
pass
|
|
|
|
# Determine if UI should be enabled
|
|
ui_should_be_enabled = pause and should_block
|
|
|
|
# Always send the UI update with the determined string_to_display
|
|
if hasattr(PromptServer, 'instance') and PromptServer.instance is not None:
|
|
PromptServer.instance.send_sync("string_editor_persistent_tempfile_display", {
|
|
"node_id": node_id,
|
|
"string_to_edit": string_to_display, # <<< Send the correct value
|
|
"enable_ui": ui_should_be_enabled
|
|
})
|
|
# Apply delay only if pause=True and we are *not* blocking (i.e., file processed)
|
|
if pause and not should_block:
|
|
time.sleep(0.02)
|
|
|
|
# Final return logic
|
|
parts = ["", ""]
|
|
if split_tag.strip() != "" and split_tag in input_string:
|
|
parts = input_string.split(split_tag, 1)
|
|
if len(parts) > 1:
|
|
print("There are multiple tags in the string, only split by the first one.")
|
|
|
|
split_1 = parts[0].strip()
|
|
split_2 = parts[1].strip()
|
|
|
|
if not pause:
|
|
PromptServer.instance.send_sync("string_editor_persistent_tempfile_display", {
|
|
"node_id": node_id,
|
|
"string_to_edit": input_string, # <<< Send the correct value
|
|
"enable_ui": ui_should_be_enabled
|
|
})
|
|
time.sleep(0.02)
|
|
# If pause is off, return the current input string directly
|
|
return {"result": (input_string, split_1, split_2)}
|
|
else:
|
|
# If pause is on, return Blocker or the value read from the file
|
|
if should_block:
|
|
return {"result": (ExecutionBlocker(None), ExecutionBlocker(None), ExecutionBlocker(None))}
|
|
else:
|
|
# Ensure we have a valid output string (should be from file)
|
|
final_output = output_string if output_string is not None else current_input_string # Fallback just in case
|
|
return {"result": (final_output, split_1, split_2)} |