Clean Commit Overwrite Old Files

This commit is contained in:
machinepainting
2025-04-23 03:06:50 -07:00
commit b741c156a4
11 changed files with 383 additions and 0 deletions
Vendored
BIN
View File
Binary file not shown.
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 machinepainting
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+65
View File
@@ -0,0 +1,65 @@
# ComfyUI DropSend Node
This custom node package adds automatic Dropbox upload capabilities to your ComfyUI workflows.
## 🚀 Features
- **📤 Dropbox AutoUploader Node**
- Monitors a specified folder (e.g., ComfyUI's `output/`) in real time
- Automatically uploads new images to your Dropbox account
- **🛠️ Dropbox Setup Node**
- Accepts your App Key, App Secret, and Authorization Code
- Automatically generates a refresh token and saves credentials to a `.env` file
- **🔐 Helpers**
- `generate_base64.py`: Easily generate your Base64 App credentials
- `get_refresh_token.py`: Standalone CLI for manually retrieving a refresh token
---
## 🧩 Installation
Place this folder inside your `ComfyUI/custom_nodes/` directory:
```bash
ComfyUI/custom_nodes/ComfyUI_DropSendNode/
```
Install dependencies:
```bash
pip install -r requirements.txt
```
---
## 🔧 Setup Instructions
1. Go to the [Dropbox App Console](https://www.dropbox.com/developers/apps) and create a new app.
2. Enable `App Folder` access and note your **App Key** and **App Secret**.
3. Generate an **Authorization Code** via:
```
https://www.dropbox.com/oauth2/authorize?client_id=APPKEY&response_type=code&token_access_type=offline
```
4. Paste these into the **🛠️ Dropbox Setup** node and run it once.
5. Then use the **📤 Dropbox AutoUploader** node in your workflow to monitor and upload.
---
## ✅ Example Workflow
- Connect your image-saving pipeline to the default output folder
- Add the **📤 AutoUploader** node pointing to that folder
- Files will upload as soon as they’re saved
---
## 🧪 Tested On
- Python 3.10 / 3.11
- ComfyUI latest release (2024–2025)
- Dropbox API with refresh token support
---
## License
MIT
+13
View File
@@ -0,0 +1,13 @@
# ComfyUI_DropSendNode/__init__.py
# Register Dropbox Setup + Run-only nodes
from .run_dropbox_node import NODE_CLASS_MAPPINGS as RUN_CLASS, NODE_DISPLAY_NAME_MAPPINGS as RUN_DISPLAY
from .setup_dropbox_node import NODE_CLASS_MAPPINGS as SETUP_CLASS, NODE_DISPLAY_NAME_MAPPINGS as SETUP_DISPLAY
NODE_CLASS_MAPPINGS = {}
NODE_CLASS_MAPPINGS.update(RUN_CLASS)
NODE_CLASS_MAPPINGS.update(SETUP_CLASS)
NODE_DISPLAY_NAME_MAPPINGS = {}
NODE_DISPLAY_NAME_MAPPINGS.update(RUN_DISPLAY)
NODE_DISPLAY_NAME_MAPPINGS.update(SETUP_DISPLAY)
+60
View File
@@ -0,0 +1,60 @@
# ComfyUI_DropSendNode/dropbox_upload.py
import os
import dropbox
import requests
from dotenv import load_dotenv
def get_token():
# ✅ 1. Prioritize direct access_token from general environment variables
access_token = os.getenv("DROPBOX_ACCESS_TOKEN")
if access_token:
return access_token
# ✅ 2. Try refresh_token flow from general environment variables
refresh_token = os.getenv("DROPBOX_REFRESH_TOKEN")
client_id = os.getenv("DROPBOX_APP_KEY")
client_secret = os.getenv("DROPBOX_APP_SECRET")
# ✅ 3. If missing, check for RunPod-style prefixed secrets
refresh_token = refresh_token or os.getenv("RUNPOD_SECRET_DROPBOX_REFRESH_TOKEN")
access_token = os.getenv("RUNPOD_SECRET_DROPBOX_ACCESS_TOKEN")
if access_token:
return access_token
# 🔁 4. Final fallback: load from .env for local users
if not all([refresh_token, client_id, client_secret]):
env_path = os.path.join(os.path.dirname(__file__), ".env")
if os.path.exists(env_path):
load_dotenv(env_path, override=True)
refresh_token = refresh_token or os.getenv("DROPBOX_REFRESH_TOKEN") or os.getenv("RUNPOD_SECRET_DROPBOX_REFRESH_TOKEN")
client_id = client_id or os.getenv("DROPBOX_APP_KEY")
client_secret = client_secret or os.getenv("DROPBOX_APP_SECRET")
if not all([refresh_token, client_id, client_secret]):
raise Exception("❌ Missing Dropbox credentials. Define them as environment variables or in a .env file.")
response = requests.post(
"https://api.dropbox.com/oauth2/token",
headers={"Content-Type": "application/x-www-form-urlencoded"},
data={
"grant_type": "refresh_token",
"refresh_token": refresh_token,
"client_id": client_id,
"client_secret": client_secret,
},
)
response.raise_for_status()
return response.json()["access_token"]
def upload_to_dropbox(local_file_path, dropbox_folder="/Apps/ComfyUI_Output_Files"):
access_token = get_token()
dbx = dropbox.Dropbox(access_token)
file_name = os.path.basename(local_file_path)
dropbox_path = f"{dropbox_folder}/{file_name}"
with open(local_file_path, "rb") as f:
dbx.files_upload(f.read(), dropbox_path, mode=dropbox.files.WriteMode.overwrite)
print(f"📦 Uploaded to Dropbox: {dropbox_path}")
+10
View File
@@ -0,0 +1,10 @@
# ComfyUI_DropSendNode/generate_base64.py
import base64
app_key = input("Enter your App Key: ")
app_secret = input("Enter your App Secret: ")
credentials = f"{app_key}:{app_secret}"
encoded = base64.b64encode(credentials.encode()).decode()
print(f"\nBasic {encoded}")
+27
View File
@@ -0,0 +1,27 @@
# ComfyUI_DropSendNode/get_refresh_token.py
import base64
import requests
APP_KEY = input("Enter your APP KEY: ")
APP_SECRET = input("Enter your APP SECRET: ")
AUTH_CODE = input("Paste the Dropbox authorization code: ")
auth_header = base64.b64encode(f"{APP_KEY}:{APP_SECRET}".encode()).decode()
res = requests.post(
"https://api.dropbox.com/oauth2/token",
data={
"code": AUTH_CODE,
"grant_type": "authorization_code",
"redirect_uri": "https://localhost"
},
headers={
"Authorization": f"Basic {auth_header}",
"Content-Type": "application/x-www-form-urlencoded"
}
)
if res.ok:
print("\nYour refresh token is:\n", res.json().get("refresh_token"))
else:
print("Error:", res.text)
+48
View File
@@ -0,0 +1,48 @@
# ComfyUI_DropSendNode/monitor_output.py
import time
import os
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
from .dropbox_upload import upload_to_dropbox
uploaded_files = set()
def start_monitoring(folder, dropbox_dest_folder):
class Handler(FileSystemEventHandler):
def on_created(self, event):
if event.is_directory:
return
file_path = event.src_path
# Wait for file to finish writing
while True:
try:
with open(file_path, 'rb') as f:
f.read()
break
except Exception:
time.sleep(0.5)
if file_path not in uploaded_files:
try:
upload_to_dropbox(file_path, dropbox_dest_folder)
uploaded_files.add(file_path)
print(f"📦✅ Uploaded: {file_path}")
except Exception as e:
print(f"📦❌ Upload failed: {e}")
# Ensure the folder exists
if not os.path.exists(folder):
os.makedirs(folder)
observer = Observer()
observer.schedule(Handler(), folder, recursive=False)
observer.start()
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
observer.stop()
observer.join()
+3
View File
@@ -0,0 +1,3 @@
requests
python-dotenv
watchdog
+64
View File
@@ -0,0 +1,64 @@
# ComfyUI_DropSendNode/run_dropbox_node.py
import os
import threading
from dotenv import load_dotenv, dotenv_values
from .monitor_output import start_monitoring
class DropSendRunNode:
@classmethod
def INPUT_TYPES(cls):
node_dir = os.path.dirname(__file__)
env_path = os.path.join(node_dir, ".env")
default_watch = os.path.join(os.getcwd(), "output")
default_dest = "/Apps/ComfyUI_Output_Files"
# if .env exists, pull last-used folder
if os.path.exists(env_path):
cfg = dotenv_values(env_path)
default_dest = cfg.get("DROPBOX_FOLDER", default_dest)
return {
"required": {
"watch_folder": ("STRING", {"default": default_watch}),
"dropbox_dest_folder": ("STRING", {"default": default_dest}),
}
}
RETURN_TYPES = ("STRING",)
OUTPUT_NODE = True
FUNCTION = "start"
def start(self, watch_folder, dropbox_dest_folder):
# persist new destination into .env alongside secrets
node_dir = os.path.dirname(__file__)
env_path = os.path.join(node_dir, ".env")
existing = {}
if os.path.exists(env_path):
existing = dotenv_values(env_path)
existing["DROPBOX_FOLDER"] = dropbox_dest_folder
with open(env_path, "w") as f:
for k, v in existing.items():
f.write(f"{k}={v}\n")
load_dotenv(env_path, override=True)
# start (or restart) the folder watcher
thread = threading.Thread(
target=start_monitoring,
args=(watch_folder, dropbox_dest_folder),
daemon=True
)
thread.start()
return (
f"📦✅ .env updated: DROPBOX_FOLDER={dropbox_dest_folder}",
f"🔎📁 Monitoring Folder: {watch_folder} → {dropbox_dest_folder}"
)
# Required mappings for ComfyUI
NODE_CLASS_MAPPINGS = {"DropSendRunNode": DropSendRunNode}
NODE_DISPLAY_NAME_MAPPINGS = {"DropSendRunNode": "📦📤 Dropbox AutoUploader Run"}
+72
View File
@@ -0,0 +1,72 @@
# ComfyUI_DropSendNode/setup_dropbox_node.py
import os
import requests
from dotenv import load_dotenv, dotenv_values
class DropboxSetupNode:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"app_key": ("STRING", {"default": ""}),
"app_secret": ("STRING", {"default": ""}),
"auth_code": ("STRING", {"default": ""}),
"dropbox_dest_folder": ("STRING", {"default": "/Apps/ComfyUI_Output_Files"}),
}
}
RETURN_TYPES = ("STRING",)
OUTPUT_NODE = True
FUNCTION = "setup"
def setup(self, app_key, app_secret, auth_code, dropbox_dest_folder):
node_dir = os.path.dirname(__file__)
env_path = os.path.join(node_dir, ".env")
# 1. Skip setup if generic environment variables already exist
general_env_set = all([
os.getenv("DROPBOX_APP_KEY"),
os.getenv("DROPBOX_APP_SECRET"),
os.getenv("DROPBOX_REFRESH_TOKEN")
])
if general_env_set:
return ("⚠️ Dropbox credentials already set in system environment variables. Skipping .env creation.",)
# 2. Skip setup if RunPod secrets are defined
runpod_env_set = all([
os.getenv("RUNPOD_SECRET_DROPBOX_ACCESS_TOKEN"),
os.getenv("RUNPOD_SECRET_DROPBOX_REFRESH_TOKEN")
])
if runpod_env_set:
return ("⚠️ Detected RunPod secrets (RUNPOD_SECRET_DROPBOX_ACCESS_TOKEN, RUNPOD_SECRET_DROPBOX_REFRESH_TOKEN). Skipping .env creation.",)
# 3. Run auth_code flow if no env config is present
if not all([app_key, app_secret, auth_code]):
return ("❌ Missing App Key, Secret, or Authorization Code.",)
data = {
"code": auth_code,
"grant_type": "authorization_code",
"client_id": app_key,
"client_secret": app_secret
}
headers = {"Content-Type": "application/x-www-form-urlencoded"}
resp = requests.post("https://api.dropbox.com/oauth2/token", headers=headers, data=data)
resp.raise_for_status()
refresh_token = resp.json().get("refresh_token")
# Write .env with fallback credentials
with open(env_path, "w") as f:
f.write(f"DROPBOX_APP_KEY={app_key}\n")
f.write(f"DROPBOX_APP_SECRET={app_secret}\n")
f.write(f"DROPBOX_REFRESH_TOKEN={refresh_token}\n")
f.write(f"DROPBOX_FOLDER={dropbox_dest_folder}\n")
load_dotenv(env_path, override=True)
return ("✅ .env file created with Dropbox credentials.",)
# Required mappings for ComfyUI
NODE_CLASS_MAPPINGS = {"DropboxSetupNode": DropboxSetupNode}
NODE_DISPLAY_NAME_MAPPINGS = {"DropboxSetupNode": "📦⚙️ Dropbox AutoUploader Setup"}