commit 9f85bf3c091475ebdfa689f7814023cb36ea3db2 Author: PrimalFinance Date: Fri Dec 19 11:12:34 2025 -0800 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..87c59e3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +presets.db +\__pycache__ \ No newline at end of file diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..e4fba21 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.12 diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..bd4552f --- /dev/null +++ b/__init__.py @@ -0,0 +1,26 @@ +from .preset_nodes import ( + LoadAction, + LoadSubject, + SavePreset, + DeletePreset, + UpdatePreset, +) + +NODE_CLASS_MAPPINGS = { + "DeletePreset": DeletePreset, + "LoadAction": LoadAction, + "LoadSubject": LoadSubject, + "SavePreset": SavePreset, + "UpdatePreset": UpdatePreset, +} + +NODE_DISPLAY_NAME_MAPPINGS = { + "DeletePreset": "Delete Preset", + "LoadAction": "Load Action", + "LoadSubject": "Load Subject Node", + "SavePreset": "Save Preset Node", + "UpdatePreset": "Update Preset", +} + + +__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"] diff --git a/db.py b/db.py new file mode 100644 index 0000000..a598bcc --- /dev/null +++ b/db.py @@ -0,0 +1,137 @@ +from pathlib import Path +import sqlite3 +from typing import Self + + +class Database: + def __init__(self, db_path: str): + self.db_path = Path(db_path) + self.db_path.parent.mkdir(parents=True, exist_ok=True) + self.CREATE_QUERY = """CREATE TABLE IF NOT EXISTS {} (label TEXT, description TEXT, type TEXT, PRIMARY KEY (label));""" + self.INSERT_QUERY = ( + "INSERT OR IGNORE INTO {} (label, description, type) VALUES (?, ?, ?)" + ) + self.READ_LABEL_QUERY = "SELECT label FROM {} WHERE type = ?" + self.READ_DESCRIPTION_QUERY = ( + "SELECT description FROM {} WHERE label = ? AND type = ?" + ) + self.TABLES = {"presets": "presets"} + self.conn = self._connect() + + def _connect(self, timeout: int = 30) -> sqlite3.Connection: + conn = sqlite3.connect( + str(self.db_path), timeout=timeout, detect_types=sqlite3.PARSE_DECLTYPES + ) + conn.execute("PRAGMA synchronous = NORMAL;") + conn.execute("PRAGMA foreign_keys = ON;") + conn.execute("PRAGMA busy_timeout = 30000;") + return conn + + def _init_schema(self, create_table_query: str) -> None: + cur = self.conn.cursor() + cur.execute(create_table_query) + cur.close() + + def _drop_table(self, table_name: str): + with self.conn: + cursor = self.conn.cursor() + query = f"DROP TABLE {table_name}" + cursor.execute(query) + + def _insert_data(self, query: str, params: tuple): + with self.conn: + cursor = self.conn.cursor() + cursor.execute(query, params) + + # def _update_data(self, query: str, params: tuple): + # with self.conn: + # cursor = self.conn.cursor() + + def _execute_query(self, query: str, params: tuple, fallback_func: callable = None): + try: + with self.conn: + + cursor = self.conn.cursor() + data = cursor.execute(query, params) + return data.fetchall() + except sqlite3.OperationalError: + if fallback_func is not None: + fallback_func() + self._execute_query(query, params) + + def close(self) -> None: + try: + self.conn.commit() + except Exception: + pass + self.conn.close() + + # context manager support + def __enter__(self) -> Self: + return self + + def __exit__(self, exc_type, exc, tb): + self.close() + + def create_presets_table(self): + self._init_schema(self.CREATE_QUERY.format(self.TABLES["presets"])) + + ####################################### + # Action + ####################################### + + def insert_data(self, label: str, description: str, preset_type: str): + self._execute_query( + self.INSERT_QUERY.format(self.TABLES["presets"]), + params=(label, description, preset_type), + fallback_func=self.create_presets_table, + ) + + def delete_data(self, label: str): + query = f"DELETE FROM {self.TABLES['presets']} WHERE label = ?" + params = (label,) + self._execute_query(query, params, fallback_func=self.create_presets_table) + + def read_labels(self, preset_type: str, get_all: bool = False) -> list: + if get_all: + data = self._execute_query( + f"SELECT label FROM {self.TABLES['presets']}", + (), + fallback_func=self.create_presets_table, + ) + else: + data = self._execute_query( + self.READ_LABEL_QUERY.format(self.TABLES["presets"]), + (preset_type,), + self.create_presets_table, + ) + return data + + def read_description(self, label: str, preset_type: str) -> list: + data = self._execute_query( + self.READ_DESCRIPTION_QUERY.format(self.TABLES["presets"]), + (label, preset_type), + self.create_presets_table, + ) + return data + + def update_data(self, label: str, new_description: str): + self._execute_query( + f"""UPDATE presets SET description = ? WHERE label = ?""", + params=(new_description, label), + fallback_func=self.create_presets_table, + ) + + +def _get_presets() -> str: + presets = ["action", "subject", "location"] + return presets + + +if __name__ == "__main__": + + db = Database("presets.db") + + # db.insert_subject("Test", "This is a test") + labels = db.read_labels("subject") + print(labels) diff --git a/main.py b/main.py new file mode 100644 index 0000000..4bd0150 --- /dev/null +++ b/main.py @@ -0,0 +1,6 @@ +def main(): + print("Hello from comfyui-presethelper!") + + +if __name__ == "__main__": + main() diff --git a/preset_nodes.py b/preset_nodes.py new file mode 100644 index 0000000..aa03bfc --- /dev/null +++ b/preset_nodes.py @@ -0,0 +1,149 @@ +from pathlib import Path + +from .db import Database, _get_presets + + +def _get_db_path() -> str: + BASE_DIR = Path(__file__).parent + db_path = BASE_DIR / "presets.db" + return db_path + + +class LoadAction: + def __init__(self): + self.db = Database(_get_db_path()) + self.preset_type = "action" + + @classmethod + def INPUT_TYPES(cls): + db = Database(_get_db_path()) + labels = db.read_labels("action") + labels = [r[0] for r in labels] + return { + "required": { + "label": (labels,), + } + } + + FUNCTION = "run" + CATEGORY = "Presets/load-action-preset" + RETURN_TYPES = ("STRING",) + RETURN_NAMES = ("description",) + + def run(self, label: str): + description = self.db.read_description(label, self.preset_type) + description = description[0][0] + return (description,) + + +class LoadSubject: + def __init__(self): + self.db = Database(_get_db_path()) + self.preset_type = "subject" + + @classmethod + def INPUT_TYPES(cls): + db = Database(_get_db_path()) + labels = db.read_labels("subject") + labels = [r[0] for r in labels] + return { + "required": { + "label": (labels,), + } + } + + FUNCTION = "run" + CATEGORY = "Presets/load-subject-preset" + RETURN_TYPES = ("STRING",) + RETURN_NAMES = ("description",) + + def run(self, label: str): + print(f"LABEL: {label} ") + # label = label[0] + # + description = self.db.read_description(label, self.preset_type) + description = description[0][0] + return (description,) + + +class SavePreset: + def __init__(self): + self.db = Database(_get_db_path()) + + @classmethod + def INPUT_TYPES(cls): + + presets = _get_presets() + db = Database(_get_db_path()) + labels = db.read_labels("", get_all=True) + labels = [r[0] for r in labels] + return { + "required": { + "preset_type": (presets,), + "label": ("STRING", {"default": ""}), + "description": ("STRING", {"default": "", "multiline": True}), + } + } + + RETURN_TYPES = () + OUTPUT_NODE = True + FUNCTION = "run" + CATEGORY = "Presets/save-preset" + + def run(self, preset_type: str, label: str, description: str): + print( + f"PRESET_TYP: {preset_type} LABEL: {label} DESCRIPTION: {description}" + ) + self.db.insert_data(label, description, preset_type) + return () + + +class UpdatePreset: + def __init__(self): + self.db = Database(_get_db_path()) + + @classmethod + def INPUT_TYPES(cls): + db = Database(_get_db_path()) + labels = db.read_labels("", get_all=True) + labels = [r[0] for r in labels] + return { + "required": { + "label": (labels,), + "description": ("STRING", {"default": "", "multiline": True}), + } + } + + RETURN_TYPES = () + OUTPUT_NODE = True + FUNCTION = "run" + CATEGORY = "Presets/update-preset" + + def run(self, label: str, description: str): + self.db.update_data(label, description) + return () + + +class DeletePreset: + def __init__(self): + self.db = Database(_get_db_path()) + + @classmethod + def INPUT_TYPES(cls): + db = Database(_get_db_path()) + labels = db.read_labels("", get_all=True) + labels = [r[0] for r in labels] + return { + "required": { + "label": (labels,), + } + } + + RETURN_TYPES = () + OUTPUT_NODE = True + FUNCTION = "run" + CATEGORY = "Presets/delete-preset" + + def run(self, label: str): + self.db.delete_data(label) + return () diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..f95cea0 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,7 @@ +[project] +name = "comfyui-presethelper" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +requires-python = ">=3.9" +dependencies = []