Files
T
2025-03-06 21:05:59 -05:00

890 lines
40 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import random
import re
import os
from datetime import datetime, timedelta
from .weather_utils import generate_weather_description
from .color_data import COLORS, color_families
# Palette generation module imports:
from .make_palette_analogous import generate_analogous_palette_4, generate_analogous_palette_5
from .make_palette_art_house import generate_art_house_palette_4, generate_art_house_palette_5
from .make_palette_chaotic import generate_chaotic_palette
from .make_palette_complementary import generate_complementary_palette, generate_complementary_palette_5
from .make_palette_double_complementary import generate_double_complementary_palette_4, generate_double_complementary_palette_5
from .make_palette_modified_compound import generate_modified_compound_palette_five, generate_modified_compound_palette_four_from_five
from .make_palette_split_complementary import generate_split_complementary_palette_4, generate_split_complementary_palette_5
from .make_palette_triadic import generate_four_color_palette, generate_triadic_palette_5
class EbuPromptHelperRandomColorPalette:
"""
EBU Random Color Palette Generator Node
Inputs:
- include_analogous_palettes (BOOLEAN): Include analogous palettes (default True)
- include_art_house_palettes (BOOLEAN): Include art house palettes (default True)
- include_chaotic_palettes (BOOLEAN): Include chaotic palettes (default True)
- include_complementary_palettes (BOOLEAN): Include complementary palettes (default True)
- include_compound_palettes (BOOLEAN): Include compound palettes (default True)
- include_split_complementary_palettes (BOOLEAN): Include split complementary palettes (default True)
- include_tetradic_palettes (BOOLEAN): Include tetradic palettes (default True)
- include_triadic_palettes (BOOLEAN): Include triadic palettes (default True)
- palette_size (STRING): Drop‑down selection for palette size. Options:
"3 colors", "4 colors", or "5 colors".
(If "3 colors" is selected, a standard 4‑color palette is generated and its first 3 colors are returned.)
- prefer_color_family (STRING): Drop‑down for a preferred color family.
Options: "None", "Reds", "Pinks", "Oranges", "Yellows", "Greens", "Blues", "Purples",
"Browns", "Greys", "Neutrals", "Metallics", "Pastels", "Peaches", "Warm Colors", "Cool Colors".
(If not "None", the palette is regenerated up to 50 times until the condition is met.)
- avoid_color_family (STRING): Drop‑down for a color family to avoid.
Options: "None", "Reds", "Pinks", "Oranges", "Yellows", "Greens", "Blues", "Purples",
"Browns", "Greys", "Neutrals", "Metallics", "Pastels", "Peaches", "Warm Colors", "Cool Colors".
(If not "None", the palette is regenerated up to 50 times until the condition is met.)
- seed (INT): Seed for random generation (0 means non‑deterministic).
Returns:
- palette (STRING): A comma‑separated list of color names (always at least 3), in lower-case.
- color1 (STRING): The first color in the palette (lower-case).
- color2 (STRING): The second color in the palette (lower-case).
- color3 (STRING): The third color in the palette (lower-case).
- color4 (STRING): The fourth color if available, or "" if not.
- color5 (STRING): The fifth color if available, or "" if not.
- palette_type (STRING): The chosen palette type.
- hex_values (STRING): A comma‑separated list of hex values corresponding to each color.
"""
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"include_analogous_palettes": ("BOOLEAN", {"default": True}),
"include_art_house_palettes": ("BOOLEAN", {"default": True}),
"include_chaotic_palettes": ("BOOLEAN", {"default": True}),
"include_complementary_palettes": ("BOOLEAN", {"default": True}),
"include_compound_palettes": ("BOOLEAN", {"default": True}),
"include_split_complementary_palettes": ("BOOLEAN", {"default": True}),
"include_tetradic_palettes": ("BOOLEAN", {"default": True}),
"include_triadic_palettes": ("BOOLEAN", {"default": True}),
"palette_size": (["3 colors", "4 colors", "5 colors"], {"default": "4 colors"}),
"prefer_color_family": (
["None", "Reds", "Pinks", "Oranges", "Yellows", "Greens", "Blues", "Purples",
"Browns", "Greys", "Neutrals", "Metallics", "Pastels", "Peaches", "Warm Colors", "Cool Colors"],
{"default": "None"}
),
"avoid_color_family": (
["None", "Reds", "Pinks", "Oranges", "Yellows", "Greens", "Blues", "Purples",
"Browns", "Greys", "Neutrals", "Metallics", "Pastels", "Peaches", "Warm Colors", "Cool Colors"],
{"default": "None"}
),
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
}
}
RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING", "STRING", "STRING", "STRING", "STRING")
RETURN_NAMES = ("palette_string", "color1", "color2", "color3", "color4", "color5", "palette_type", "hex_values")
FUNCTION = "generate_palette"
CATEGORY = "Prompts"
def generate_palette(
self,
include_analogous_palettes,
include_art_house_palettes,
include_chaotic_palettes,
include_complementary_palettes,
include_compound_palettes,
include_split_complementary_palettes,
include_tetradic_palettes,
include_triadic_palettes,
palette_size,
prefer_color_family,
avoid_color_family,
seed):
# Set the random seed.
random.seed(seed)
# If the same color family is specified for both prefer and avoid, cancel them out.
if (prefer_color_family != "None" and
avoid_color_family != "None" and
prefer_color_family == avoid_color_family):
print("Warning: The same color family was selected for both prefer and avoid. Cancelling these conditions.")
prefer_color_family = "None"
avoid_color_family = "None"
# Build a list of enabled palette types.
enabled_types = []
if include_analogous_palettes:
enabled_types.append("analogous")
if include_art_house_palettes:
enabled_types.append("art house")
if include_chaotic_palettes:
enabled_types.append("chaotic")
if include_complementary_palettes:
enabled_types.append("complementary")
if include_compound_palettes:
enabled_types.append("compound")
if include_split_complementary_palettes:
enabled_types.append("split complementary")
if include_tetradic_palettes:
enabled_types.append("tetradic")
if include_triadic_palettes:
enabled_types.append("triadic")
# If none are enabled, log a warning and default to all palette types.
if not enabled_types:
print("Warning: No palette types were enabled. Defaulting to all palette types.")
enabled_types = [
"analogous", "art house", "chaotic", "complementary",
"compound", "split complementary", "tetradic", "triadic"
]
# Determine target palette size (numeric).
if palette_size == "3 colors":
target_size = 3
elif palette_size == "4 colors":
target_size = 4
elif palette_size == "5 colors":
target_size = 5
else:
raise Exception("Invalid palette size: " + palette_size)
max_attempts = 50
chosen_palette = None
chosen_type = None # To record which palette type was chosen.
for attempt in range(max_attempts):
chosen_type = random.choice(enabled_types)
if chosen_type == "analogous":
if target_size == 5:
palette = generate_analogous_palette_5()
else:
palette = generate_analogous_palette_4()
if target_size == 3:
palette = palette[:3]
elif chosen_type == "art house":
if target_size == 5:
palette = generate_art_house_palette_5()
else:
palette = generate_art_house_palette_4()
if target_size == 3:
palette = palette[:3]
elif chosen_type == "chaotic":
if target_size == 5:
palette = generate_chaotic_palette(num_colors=5)
elif target_size == 4:
palette = generate_chaotic_palette(num_colors=4)
elif target_size == 3:
palette = generate_chaotic_palette(num_colors=4)[:3]
elif chosen_type == "complementary":
if target_size == 5:
palette = generate_complementary_palette_5()
else:
palette = generate_complementary_palette()
if target_size == 3:
palette = palette[:3]
elif chosen_type == "compound":
if target_size == 5:
palette = generate_modified_compound_palette_five()
else:
palette = generate_modified_compound_palette_four_from_five()
if target_size == 3:
palette = palette[:3]
elif chosen_type == "split complementary":
if target_size == 5:
palette = generate_split_complementary_palette_5()
else:
palette = generate_split_complementary_palette_4()
if target_size == 3:
palette = palette[:3]
elif chosen_type == "tetradic":
if target_size == 5:
palette = generate_double_complementary_palette_5()
else:
palette = generate_double_complementary_palette_4()
if target_size == 3:
palette = palette[:3]
elif chosen_type == "triadic":
if target_size == 5:
palette = generate_triadic_palette_5()
else:
palette = generate_four_color_palette()
if target_size == 3:
palette = palette[:3]
else:
raise Exception("Unknown palette type chosen: " + chosen_type)
# --- Check the "prefer" condition ---
condition_preferred = True
if prefer_color_family != "None":
if prefer_color_family in ["Warm Colors", "Cool Colors"]:
warm_list = color_families.get("Warm Colors", [])
cool_list = color_families.get("Cool Colors", [])
warm_count = sum(1 for color in palette if color in warm_list)
cool_count = sum(1 for color in palette if color in cool_list)
if prefer_color_family == "Warm Colors":
condition_preferred = (warm_count >= cool_count + 1)
else:
condition_preferred = (cool_count >= warm_count + 1)
else:
preferred_list = color_families.get(prefer_color_family, [])
condition_preferred = any(color in preferred_list for color in palette)
# --- Check the "avoid" condition ---
condition_avoid = True
if avoid_color_family != "None":
if avoid_color_family in ["Warm Colors", "Cool Colors"]:
warm_list = color_families.get("Warm Colors", [])
cool_list = color_families.get("Cool Colors", [])
warm_count = sum(1 for color in palette if color in warm_list)
cool_count = sum(1 for color in palette if color in cool_list)
if avoid_color_family == "Warm Colors":
condition_avoid = (warm_count <= cool_count - 1)
else:
condition_avoid = (cool_count <= warm_count - 1)
else:
avoid_list = color_families.get(avoid_color_family, [])
condition_avoid = not any(color in avoid_list for color in palette)
if condition_preferred and condition_avoid:
chosen_palette = palette
break
if chosen_palette is None:
print(f"Warning: Attempted {max_attempts} times to meet color preferences but wasn't able to.")
chosen_palette = palette
display_palette = [color.lower() for color in chosen_palette]
palette_str = ", ".join(display_palette)
padded_display = display_palette.copy()
while len(padded_display) < 5:
padded_display.append("")
color1, color2, color3, color4, color5 = padded_display[:5]
hex_list = [COLORS.get(color, "") for color in chosen_palette]
hex_values_str = ", ".join(hex_list)
return (palette_str, color1, color2, color3, color4, color5, chosen_type, hex_values_str)
class EbuPromptHelperReplace:
"""
EBU Prompt Helper Replace Node
This node replaces occurrences of one or more target words within an input prompt with a specified replacement string.
The target words should be provided as a single string, where multiple words are separated by the '|' character.
For example, if the `word_to_replace` input is "person|man|woman", then any occurrence of "person", "man", or "woman"
in the prompt will be replaced with the value provided in `replace_with`.
Inputs:
- prompt_text (STRING): A multiline string representing the input prompt.
- word_to_replace (STRING): A single-line string containing one or more words separated by '|'.
- replace_with (STRING): A multiline string that will replace each occurrence of the target words.
- case_sensitive (BOOLEAN): A toggle to control if the replacement is case‑sensitive (default True).
"""
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"prompt_text": ("STRING", {"multiline": True, "default": ""}),
"word_to_replace": ("STRING", {"multiline": False, "default": ""}),
"replace_with": ("STRING", {"multiline": True, "default": ""}),
"case_sensitive": ("BOOLEAN", {"default": True}),
},
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("updated_prompt_text",)
FUNCTION = "replace_text"
CATEGORY = "Example"
def replace_text(self, prompt_text, word_to_replace, replace_with, case_sensitive):
if not word_to_replace.strip():
return (prompt_text,)
words = word_to_replace.split("|")
revised = prompt_text
if case_sensitive:
for word in words:
revised = revised.replace(word, replace_with)
else:
import re
for word in words:
pattern = re.compile(re.escape(word), re.IGNORECASE)
revised = pattern.sub(replace_with, revised)
return (revised,)
class EbuPromptHelperRandomize:
"""
EBU PromptHelper Randomize Node
This node takes an input prompt and replaces occurrences of a specified target substring
(word_to_replace) with a randomly selected option from a provided string of options.
The options string is split based on the chosen delimiter, which can be newlines, commas, or semi‑colons.
The seed input controls the random selection, allowing for deterministic output if desired.
Inputs:
- prompt_text (STRING): A multiline string representing the input prompt.
- word_to_replace (STRING): A single-line string indicating the substring to replace.
- replacement_options (STRING): A string containing replacement options.
- seed (INT): Ensures that node runs every time
- case_sensitive (BOOLEAN): A toggle to control if the replacement is case‑sensitive (default True).
- delimit_options_with (STRING): Dropdown with options ["newlines", "commas", "semi-colons"]
that determines how to split the options string.
Returns:
- updated_prompt_text (STRING): The prompt after replacement.
- word_selected (STRING): The randomly selected replacement option.
"""
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"prompt_text": ("STRING", {"multiline": True, "default": ""}),
"word_to_replace": ("STRING", {"multiline": False, "default": ""}),
"replacement_options": ("STRING", {"multiline": True, "default": ""}),
"seed": ("INT", {"default": 0, "step": 1, "display": "number"}),
"case_sensitive": ("BOOLEAN", {"default": True}),
"delimit_options_with": (["newlines", "commas", "semi-colons"], {"default": "newlines"})
}
}
RETURN_TYPES = ("STRING", "STRING")
RETURN_NAMES = ("updated_prompt_text", "word_selected")
FUNCTION = "randomize_text"
CATEGORY = "Prompts"
def randomize_text(self, prompt_text, word_to_replace, replacement_options, seed, case_sensitive, delimit_options_with):
random.seed(seed)
# Split the options string based on the selected delimiter.
if delimit_options_with == "newlines":
options = [opt.strip() for opt in replacement_options.splitlines() if opt.strip()]
elif delimit_options_with == "commas":
options = [opt.strip() for opt in replacement_options.split(',') if opt.strip()]
elif delimit_options_with == "semi-colons":
options = [opt.strip() for opt in replacement_options.split(';') if opt.strip()]
else:
options = [opt.strip() for opt in replacement_options.splitlines() if opt.strip()]
if not options:
return (prompt_text, "")
selected = random.choice(options)
if case_sensitive:
new_prompt = prompt_text.replace(word_to_replace, selected)
else:
import re
pattern = re.compile(re.escape(word_to_replace), re.IGNORECASE)
new_prompt = pattern.sub(selected, prompt_text)
return (new_prompt, selected)
class EbuPromptHelperCombineTwoStrings:
"""
EBU PromptHelper Combine Two Strings Node
This node takes two input strings and combines them into one string using a provided join string.
Only non-empty input strings are included in the final result. For example, if one of the inputs is
empty, only the non-empty string is returned.
Inputs:
- str1 (STRING): The first string to combine. (Multiline supported.)
- str2 (STRING): The second string to combine. (Multiline supported.)
- join_str (STRING): The string used to join the two input strings. Defaults to "\n\n".
Returns:
- combined (STRING): The resulting combined string.
"""
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"str1": ("STRING", {
"multiline": True,
"default": ""
}),
"str2": ("STRING", {
"multiline": True,
"default": ""
}),
"join_str": ("STRING", {
"multiline": True,
"default": "\n\n"
}),
},
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("combined",)
FUNCTION = "combine_two_strings"
CATEGORY = "Prompts"
def combine_two_strings(self, str1, str2, join_str):
strs = []
if str1:
strs.append(str1)
if str2:
strs.append(str2)
combined = join_str.join(strs)
return (combined,)
class EbuPromptHelperListSampler:
"""
EBU PromptHelper List Sampler Node
This node takes a multi-line string (representing a list) as input, processes each line
by removing any leading numbers (e.g., "1. ", "23. ") and trimming whitespace, and then
randomly shuffles the remaining non-empty lines. It returns a sample containing a specified
number of lines (or all lines if there are fewer than requested). If the toggle is enabled,
the sampled list will be numbered starting at 1.
Inputs:
- list (STRING): A multi-line string containing the list items. Each line may optionally
start with a number and a dot.
- seed (INT): A seed for the random number generator. Use 0 for non-deterministic output.
- number_of_elements (INT, optional): The maximum number of list elements to sample. Defaults to 10.
- number_sampled_list (BOOLEAN, optional): If True, the sampled list will be numbered (default False).
Returns:
- (STRING): A newline-separated string of the sampled list items.
"""
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"list": ("STRING", {"multiline": True}),
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
},
"optional": {
"number_of_elements": ("INT", {"default": 10, "min": 1, "max": 1000}),
"number_sampled_list": ("BOOLEAN", {"default": False}),
}
}
RETURN_TYPES = ("STRING",)
FUNCTION = "sample_list"
CATEGORY = "utils"
def sample_list(self, list, seed, number_of_elements=10, number_sampled_list=False):
# Split the input string into lines.
lines = list.split('\n')
# Process lines: remove leading numbers and ignore empty or whitespace-only lines.
processed_lines = []
for line in lines:
# Remove leading numbers followed by a dot (e.g., "1. ", "23. ").
processed_line = re.sub(r'^\d+\.\s*', '', line)
processed_line = processed_line.strip()
if processed_line: # Only add non-empty lines.
processed_lines.append(processed_line)
# Set the random seed.
random.seed(seed)
# Shuffle the processed lines.
random.shuffle(processed_lines)
# Select the specified number of elements, or all available if there are fewer.
selected_lines = processed_lines[:min(number_of_elements, len(processed_lines))]
# If numbering is enabled, number each line starting at 1.
if number_sampled_list:
numbered_lines = []
for idx, line in enumerate(selected_lines, start=1):
numbered_lines.append(f"{idx}. {line}")
selected_lines = numbered_lines
# Join the selected lines into a single string separated by newlines.
output = '\n'.join(selected_lines)
return (output,)
class EbuPromptHelperLoadFileAsString:
"""
EBU PromptHelper Load File as String Node
This node loads the contents of a file as a string. Provide the directory and file name, and the node
will attempt to read the file using UTF-8 encoding. If an error occurs (e.g., if the file is not found),
the node will print an error message and return an empty string.
Inputs:
- directory (STRING): The directory path where the file is located.
- file_name (STRING): The name of the file to load.
Returns:
- (STRING): The content of the file as a string, or an empty string if an error occurred.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"directory": ("STRING", {"default": ""}),
"file_name": ("STRING", {"default": ""}),
},
}
RETURN_TYPES = ("STRING",)
FUNCTION = "load_file"
CATEGORY = "File Operations"
def load_file(self, directory, file_name):
full_path = os.path.join(directory, file_name)
try:
with open(full_path, 'r', encoding='utf-8') as file:
content = file.read()
return (content,)
except Exception as e:
print(f"Error reading file: {e}")
return ("",)
class EbuPromptHelperCurrentDateTime:
"""
EBU PromptHelper Current DateTime Node
This node returns the current datetime (at the moment of execution) in several formats:
- date: "February 10, 2025"
- time: "1:15pm"
- datetime: "2025-02-10 1:15:33pm"
- datetime_for_filename: "2025-02-10_13-15-33"
No input parameters are required.
"""
@classmethod
def INPUT_TYPES(cls):
# No inputs required.
return {"required": {}}
RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING")
RETURN_NAMES = ("date", "time", "datetime", "datetime_for_filename")
FUNCTION = "generate_datetime"
CATEGORY = "Utility"
def generate_datetime(self):
# Get the current datetime.
dt = datetime.now()
# Format the date as "February 10, 2025" (with the day inserted without a leading zero).
date_str = dt.strftime("%B {day}, %Y").format(day=dt.day)
# Format the time as "1:15pm" (12-hour format with any leading zero removed and lowercase am/pm).
time_str = dt.strftime("%I:%M%p").lstrip("0").lower()
# Format the full datetime as "2025-02-10 1:15:33pm".
datetime_str = dt.strftime("%Y-%m-%d ") + dt.strftime("%I:%M:%S%p").lstrip("0").lower()
# Format the datetime for filenames as "2025-02-10_13-15-33".
datetime_filename_str = dt.strftime("%Y-%m-%d_%H-%M-%S")
return (date_str, time_str, datetime_str, datetime_filename_str)
class EbuPromptHelperConsumeListItem:
"""
EBU PromptHelper Consume List Item Node
This node takes an optional prompt, a target substring to replace, and a list of options provided
as a newline-separated string. It randomly selects one option from the list (using the provided seed
for reproducibility), removes that option from the list, and replaces all occurrences of the target
substring in the prompt with the selected option. The revised list (with the selected option removed)
is also returned.
Inputs:
- word_to_replace (STRING): The substring in the prompt to be replaced.
- list (STRING): A newline-separated list of options.
- seed (INT): A seed for the random number generator (0 means non-deterministic).
- prompt_text (STRING, optional): A multiline string representing the input prompt. Defaults to an empty string.
Returns:
- updated_prompt_text (STRING): The prompt with the target substring replaced by the selected option.
- word_selected (STRING): The randomly selected option from the list.
- revised_list (STRING): The remaining options (after the selected one is removed) joined by a single newline
between each option, with no extra newline at the beginning.
"""
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"word_to_replace": ("STRING", {}),
"list": ("STRING", {"multiline": True, "default": ""}),
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
},
"optional": {
"prompt_text": ("STRING", {"multiline": True, "default": ""})
}
}
RETURN_TYPES = ("STRING", "STRING", "STRING")
RETURN_NAMES = ("updated_prompt_text", "word_selected", "revised_list")
FUNCTION = "consume_list_item"
CATEGORY = "utils"
def consume_list_item(self, word_to_replace, list, seed, prompt_text=""):
# Split the input string into a list of options using newlines.
options = [opt.strip() for opt in list.splitlines() if opt.strip()]
# Set the random seed.
random.seed(seed)
if options:
# Randomly select an option.
word_selected = random.choice(options)
# Remove the selected option from the list.
options.remove(word_selected)
# Join the remaining options with a single newline between them.
revised_list = '\n'.join(options)
# Ensure there is no extra newline at the beginning.
revised_list = revised_list.lstrip('\n')
# Replace all occurrences of the target substring in the prompt with the selected option.
updated_prompt_text = prompt_text.replace(word_to_replace, word_selected)
return (updated_prompt_text, word_selected, revised_list)
else:
# If no options are provided, return the prompt (or empty string) and empty values for the other outputs.
return (prompt_text, "", "")
class EbuPromptHelperSeasonWeatherTimeOfDay:
"""
EBU PromptHelper Season Weather Time-Of-Day Node
This node generates a random abstract description of a datetime within a specified year and time range,
and then produces a weather description based on that abstract datetime. The abstract datetime is
expressed in terms of a time of day and a seasonal descriptor (for example, "early morning during early winter of 2020").
Two new parameters allow you to skew the randomness:
- year_skew: How to bias the year selection.
- time_of_day_skew: How to bias the time selection.
The available options for each are:
- "no skew" (default): Uniform random selection.
- "earlier of two": Generate two random values and pick the earlier (lower) one.
- "later of two": Generate two random values and pick the later (higher) one.
- "middle of three": Generate three random values and pick the median.
Inputs:
- year_from (INT): The earliest year for the random date (e.g., 1980).
- year_to (INT): The latest year for the random date (e.g., 2022).
- time_from (STRING): The start of the time range (e.g., "6:00am").
- time_to (STRING): The end of the time range (e.g., "7:00pm").
- seed (INT): to ensure it runs every time
- year_skew (STRING): Dropdown with options ["no skew", "earlier of two", "later of two", "middle of three"].
- time_of_day_skew (STRING): Dropdown with the same options as above.
Returns:
- when (STRING): An abstract datetime description (e.g., "early morning during early winter of 2020").
- when_no_year (STRING): The abstract datetime description with the year removed (e.g., "early morning during early winter").
- weather (STRING): A weather description (e.g., "cool and snowy") selected based on the abstract datetime.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"year_from": ("INT", {"default": 1980, "step": 1, "display": "number"}),
"year_to": ("INT", {"default": 2025, "step": 1, "display": "number"}),
"time_from": ("STRING", {"multiline": False, "default": "6:00am"}),
"time_to": ("STRING", {"multiline": False, "default": "7:00pm"}),
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
"year_skew": (["no skew", "earlier of two", "later of two", "middle of three"], {"default": "no skew"}),
"time_of_day_skew": (["no skew", "earlier of two", "later of two", "middle of three"], {"default": "no skew"}),
}
}
RETURN_TYPES = ("STRING", "STRING", "STRING")
RETURN_NAMES = ("when", "when_no_year", "weather")
FUNCTION = "generate_info"
CATEGORY = "Example"
def generate_info(self, year_from, year_to, time_from, time_to, seed, year_skew, time_of_day_skew):
random.seed(seed)
when = self.generate_random_datetime(year_from, year_to, time_from, time_to, year_skew, time_of_day_skew)
weather = generate_weather_description(when) # Use external weather function
# Remove the " of [year]" part from the abstract datetime.
when_no_year = when.rsplit(" of ", 1)[0]
return (when, when_no_year, weather)
def generate_random_datetime(self, year_from, year_to, time_from, time_to, year_skew, time_of_day_skew):
# Helper: Convert a time string (e.g., "6:00am") to a datetime object.
def time_str_to_dt(time_str):
return datetime.strptime(time_str, '%I:%M%p')
# Helper: Generate a skewed random integer between min_val and max_val (inclusive).
def generate_skewed_random(min_val, max_val, skew):
if skew == "no skew":
return random.randint(min_val, max_val)
elif skew == "earlier of two":
return min(random.randint(min_val, max_val), random.randint(min_val, max_val))
elif skew == "later of two":
return max(random.randint(min_val, max_val), random.randint(min_val, max_val))
elif skew == "middle of three":
vals = [random.randint(min_val, max_val) for _ in range(3)]
return sorted(vals)[1]
else:
return random.randint(min_val, max_val)
# Helper: Generate a random date between start_year and end_year using the specified skew.
def random_date(start_year, end_year, skew):
start_date = datetime(start_year, 1, 1)
end_date = datetime(end_year + 1, 1, 1)
delta_days = (end_date - start_date).days
day_offset = generate_skewed_random(0, delta_days - 1, skew)
return start_date + timedelta(days=day_offset)
# Helper: Determine the abstract time of day based on the hour.
def get_time_of_day(hour):
if 5 <= hour < 8:
return "early morning"
elif 8 <= hour < 10:
return "morning"
elif 10 <= hour < 12:
return "late morning"
elif 12 <= hour < 15:
return "early afternoon"
elif 15 <= hour < 18:
return "late afternoon"
elif 18 <= hour < 20:
return "early evening"
elif 20 <= hour < 22:
return "evening"
elif 22 <= hour or hour < 5:
return "night"
else:
return "unknown time"
# Helper: Determine the seasonal descriptor based on the month.
def get_season_part(month):
if month in [12, 1, 2]:
season = "winter"
elif month in [3, 4, 5]:
season = "spring"
elif month in [6, 7, 8]:
season = "summer"
elif month in [9, 10, 11]:
season = "fall"
else:
season = "unknown season"
if month in [12, 1, 2]:
return f"early {season}" if month == 12 else f"late {season}"
elif month in [3, 4, 5]:
if month == 3:
return f"early {season}"
elif month == 5:
return f"late {season}"
else:
return f"middle of the {season}"
elif month in [6, 7, 8]:
if month == 6:
return f"early {season}"
elif month == 8:
return f"late {season}"
else:
return f"middle of the {season}"
elif month in [9, 10, 11]:
if month == 9:
return f"early {season}"
elif month == 11:
return f"late {season}"
else:
return f"middle of the {season}"
# Convert time strings to datetime objects.
time_from_dt = time_str_to_dt(time_from)
time_to_dt = time_str_to_dt(time_to)
if time_from_dt > time_to_dt:
raise ValueError("time_from must be before time_to")
# Generate a random date using the year skew.
random_date_obj = random_date(year_from, year_to, year_skew)
# Calculate the total seconds in the time window and generate a skewed random second offset.
total_seconds = int((time_to_dt - time_from_dt).total_seconds())
sec_offset = generate_skewed_random(0, total_seconds, time_of_day_skew)
random_time = time_from_dt + timedelta(seconds=sec_offset)
# Determine the descriptive strings.
tod_str = get_time_of_day(random_time.hour)
season_str = get_season_part(random_date_obj.month)
return f"{tod_str} during {season_str} of {random_date_obj.year}"
class EbuPromptHelperTruncate:
"""
EBU PromptHelper Truncate Node
This node takes a prompt string and a target substring. It removes everything either before or after the first occurrence of the substring.
The deletion can be inclusive (removing the substring as well) or exclusive (keeping the substring).
Inputs:
- prompt (STRING): The input prompt.
- substring (STRING): The target substring to search for.
- delete_option (STRING): Dropdown with options "delete before" or "delete after".
- inclusive (BOOLEAN): If True, the substring is removed along with the preceding or following text.
Returns:
- modified_prompt (STRING): The prompt after truncation.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"prompt": ("STRING", {"multiline": True, "default": ""}),
"substring": ("STRING", {"multiline": False, "default": ""}),
"delete_option": (["delete before", "delete after"], {"default": "delete before"}),
"inclusive": ("BOOLEAN", {"default": True}),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("modified_prompt",)
FUNCTION = "truncate_prompt"
CATEGORY = "Prompts"
def truncate_prompt(self, prompt, substring, delete_option, inclusive):
index = prompt.find(substring)
if index == -1:
# If the substring is not found, return the original prompt.
return (prompt,)
if delete_option == "delete before":
if inclusive:
# Delete everything before and including the substring.
return (prompt[index + len(substring):],)
else:
# Delete everything before but keep the substring.
return (prompt[index:],)
elif delete_option == "delete after":
if inclusive:
# Delete everything after and including the substring.
return (prompt[:index],)
else:
# Delete everything after but keep the substring.
return (prompt[:index + len(substring)],)
else:
return (prompt,)
# Node registration mappings.
NODE_CLASS_MAPPINGS = {
"EbuPromptHelperCombineTwoStrings": EbuPromptHelperCombineTwoStrings,
"EbuPromptHelperConsumeListItem": EbuPromptHelperConsumeListItem,
"EbuPromptHelperCurrentDateTime": EbuPromptHelperCurrentDateTime,
"EbuPromptHelperListSampler": EbuPromptHelperListSampler,
"EbuPromptHelperLoadFileAsString": EbuPromptHelperLoadFileAsString,
"EbuPromptHelperRandomColorPalette": EbuPromptHelperRandomColorPalette,
"EbuPromptHelperRandomize": EbuPromptHelperRandomize,
"EbuPromptHelperReplace": EbuPromptHelperReplace,
"EbuPromptHelperSeasonWeatherTimeOfDay": EbuPromptHelperSeasonWeatherTimeOfDay,
"EbuPromptHelperTruncate": EbuPromptHelperTruncate,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"EbuPromptHelperCombineTwoStrings": "EBU PromptHelper Combine Two Strings",
"EbuPromptHelperConsumeListItem": "EBU PromptHelper Consume List Item",
"EbuPromptHelperCurrentDateTime": "EBU PromptHelper Current DateTime",
"EbuPromptHelperListSampler": "EBU PromptHelper List Sampler",
"EbuPromptHelperLoadFileAsString": "EBU PromptHelper Load File as String",
"EbuPromptHelperRandomColorPalette": "EBU PromptHelper Color Palette",
"EbuPromptHelperRandomize": "EBU PromptHelper Randomize",
"EbuPromptHelperReplace": "EBU PromptHelper Replace",
"EbuPromptHelperSeasonWeatherTimeOfDay": "EBU PromptHelper Season Weather Time-Of-Day",
"EbuPromptHelperTruncate": "EBU PromptHelper Truncate",
}