commit 6ada0bfb18c9d2337951d209994d13c3f57831b2 Author: Karl Hudgell Date: Fri Mar 28 18:37:58 2025 +0000 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..10f528a --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +venv/* +script.log +**/*.pyc +*.rtf +build/ +dist/ +user_config.cfg +Dockerfile +output/**.* \ No newline at end of file diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..2419ad5 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.11.9 diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..be5e826 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,24 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python Debugger: Current File", + "type": "debugpy", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal", + "justMyCode": false, + // "args": [ + // "--num_inference_steps", + // "6", + // "--rtf_file", + // "./AllInDB.rtf", + // "--player_uuid", + // "2000252303" + // ] + } + ] +} \ No newline at end of file diff --git a/ai_frame_image_server.py b/ai_frame_image_server.py new file mode 100644 index 0000000..c267f83 --- /dev/null +++ b/ai_frame_image_server.py @@ -0,0 +1,35 @@ +from flask import Flask, render_template, send_from_directory, redirect, url_for +import os +from lib import create_image + +app = Flask(__name__) + +image_folder = "./output" + +def get_latest_image(): + """Get the latest image file from the directory.""" + files = [f for f in os.listdir(image_folder) if f.endswith(('.png', '.jpg', '.jpeg'))] + if not files: + return None + latest_file = max(files, key=lambda f: os.path.getmtime(os.path.join(image_folder, f))) + return latest_file + + +@app.route('/') +def index(): + latest_image = get_latest_image() + return render_template("index.html", image=latest_image) + +@app.route('/images/') +def images(filename): + return send_from_directory(image_folder, filename) + +@app.route('/create') +def create(): + """Endpoint to create a new image.""" + create_image() + return redirect(url_for("index")) + +if __name__ == '__main__': + os.makedirs(image_folder, exist_ok=True) # Ensure the folder exists + app.run(debug=True) diff --git a/lib.py b/lib.py new file mode 100644 index 0000000..75653dc --- /dev/null +++ b/lib.py @@ -0,0 +1,76 @@ +import random +import configparser +import logging +import sys +import litellm +import time + +from datetime import datetime + +from comfy_api_simplified import ComfyApiWrapper, ComfyWorkflowWrapper + +user_config = configparser.ConfigParser() +try: + user_config.read("./user_config.cfg") + output_folder = user_config["comfyui"]["output_dir"] + logging.debug("Configuration loaded successfully.") +except KeyError as e: + logging.error(f"Missing configuration key: {e}") + sys.exit(1) + + +def send_prompt_to_openwebui(prompt): + response = litellm.completion( + api_base=user_config["openwebui"]["base_url"], + model="openai/" + user_config["openwebui"]["model"], + messages=[ + { + "role": "user", + "content": prompt, + } + ], + api_key=user_config["openwebui"]["api_key"], + ) + + return response["choices"][0]["message"]["content"].strip('"') + + +def generate_image(file_name, comfy_prompt): + """Generate an image using the Comfy API.""" + try: + # Initialize API and workflow + api = ComfyApiWrapper(user_config["comfyui"]["comfyui_url"]) + wf = ComfyWorkflowWrapper("./workflow_api.json") + + # Set workflow parameters + wf.set_node_param("KSampler", "seed", random.getrandbits(32)) + # wf.set_node_param("KSampler", "steps", steps) + wf.set_node_param("CLIP Text Encode (Prompt)", "text", comfy_prompt) + wf.set_node_param("Save Image", "filename_prefix", file_name) + wf.set_node_param("Empty Latent Image", "width", user_config["comfyui"]["width"]) + wf.set_node_param("Empty Latent Image", "height", user_config["comfyui"]["height"]) + wf.set_node_param( + "Load Checkpoint", "ckpt_name", user_config["comfyui"]["model"] + ) + # Queue your workflow for completion + logging.debug(f"Generating image: {file_name}") + results = api.queue_and_wait_images(wf, "Save Image") + for filename, image_data in results.items(): + with open( + user_config["comfyui"]["output_dir"] + file_name + ".png", "wb+" + ) as f: + f.write(image_data) + logging.debug(f"Image generated successfully for UID: {file_name}") + except Exception as e: + logging.error(f"Failed to generate image for UID: {file_name}. Error: {e}") + + +def create_image(): + """Main function for generating images.""" + prompt = send_prompt_to_openwebui(user_config["comfyui"]["prompt"]) + print(f"Generated prompt: {prompt}") + generate_image(str(time.time()), prompt) + + +# if __name__ == "__main__": +# main() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..1a989a1 Binary files /dev/null and b/requirements.txt differ diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..4c8173c --- /dev/null +++ b/templates/index.html @@ -0,0 +1,39 @@ + + + + + + Latest Image + + + + + {% if image %} + Latest Image + {% else %} +

No images found

+ {% endif %} + + diff --git a/user_config.cfg.sample b/user_config.cfg.sample new file mode 100644 index 0000000..f9dc393 --- /dev/null +++ b/user_config.cfg.sample @@ -0,0 +1,11 @@ +[comfyui] +comfyui_url = http://comfyui +model = zavychromaxl_v100.safetensors +output_dir = ./output/ +prompt = "Be explicit, only return the prompt and no other text, Generate a random detailed prompt for stable diffusion." +width = 1568 +height = 672 + +[openwebui] +base_url = https://openwebui +api_key = sk- \ No newline at end of file diff --git a/workflow_api.json b/workflow_api.json new file mode 100644 index 0000000..13eab21 --- /dev/null +++ b/workflow_api.json @@ -0,0 +1,107 @@ +{ + "3": { + "inputs": { + "seed": 676047523401976, + "steps": 30, + "cfg": 6, + "sampler_name": "dpmpp_3m_sde", + "scheduler": "karras", + "denoise": 1, + "model": [ + "4", + 0 + ], + "positive": [ + "6", + 0 + ], + "negative": [ + "7", + 0 + ], + "latent_image": [ + "5", + 0 + ] + }, + "class_type": "KSampler", + "_meta": { + "title": "KSampler" + } + }, + "4": { + "inputs": { + "ckpt_name": "zavychromaxl_v80.safetensors" + }, + "class_type": "CheckpointLoaderSimple", + "_meta": { + "title": "Load Checkpoint" + } + }, + "5": { + "inputs": { + "width": 1568, + "height": 672, + "batch_size": 1 + }, + "class_type": "EmptyLatentImage", + "_meta": { + "title": "Empty Latent Image" + } + }, + "6": { + "inputs": { + "text": "A bustling cyberpunk street at night, filled with neon signs, rain-soaked pavement, and futuristic street vendors. High detail, vivid neon colors, and realistic reflections.", + "clip": [ + "4", + 1 + ] + }, + "class_type": "CLIPTextEncode", + "_meta": { + "title": "CLIP Text Encode (Prompt)" + } + }, + "7": { + "inputs": { + "text": "text, watermark, deformed Avoid flat colors, poor lighting, and artificial elements. No unrealistic elements, low resolution, or flat colors. Avoid generic objects, poor lighting, and inconsistent styles.", + "clip": [ + "4", + 1 + ] + }, + "class_type": "CLIPTextEncode", + "_meta": { + "title": "CLIP Text Encode (Prompt)" + } + }, + "8": { + "inputs": { + "samples": [ + "3", + 0 + ], + "vae": [ + "4", + 2 + ] + }, + "class_type": "VAEDecode", + "_meta": { + "title": "VAE Decode" + } + }, + "9": { + "inputs": { + "filename_prefix": "ComfyUI", + "images": [ + "8", + 0 + ] + }, + "class_type": "SaveImage", + "_meta": { + "title": "Save Image" + } + } +} \ No newline at end of file