From 80909269c8017ea77cb628ac3a435c41fc14889a Mon Sep 17 00:00:00 2001 From: "Fred N. Garvin, Esq." <184324400+FNGarvin@users.noreply.github.com> Date: Sun, 11 Jan 2026 22:32:27 -0600 Subject: [PATCH] Rebranding --- .gitignore | 8 ++++ README.md | 98 ++++++++++++++++++------------------------- __init__.py | 15 +++---- pyproject.toml | 7 ++-- web/serverDownload.js | 80 +++++++++++++++++------------------ 5 files changed, 99 insertions(+), 109 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..deee265 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +__pycache__/ +*.pyc +.vscode/ +.devcontainer/ +.gemini/ +*.egg-info/ +dist/ +build/ diff --git a/README.md b/README.md index 6bb1136..4c91ef5 100644 --- a/README.md +++ b/README.md @@ -1,73 +1,59 @@ -# ComfyUI RunpodDirect +# ComfyUI-AutoModelDownloader -[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) -[![GitHub](https://img.shields.io/github/stars/MadiatorLabs/ComfyUI-RunpodDirect?style=social)](https://github.com/MadiatorLabs/ComfyUI-RunpodDirect) - -Direct model downloads to your Runpod pod with blazing-fast multi-connection support. No more downloading models to your local machine and re-uploading! +A pretty straightforward rebranding of [ComfyUI-RunpodDirect](https://github.com/MadiatorLabs/ComfyUI-RunpodDirect) with the Runpod bits removed in order to make the addon ideally suited for "offline" use. ## Features -- **Multi-Connection Downloads**: Up to 4x faster with parallel chunk downloading (4 connections per file) -- **Pause/Resume/Cancel**: Full control over active downloads -- **Download All Models**: Queue and download all missing models sequentially with one click -- **Real-Time Progress**: Live progress tracking with download speed, file size, and completion percentage - -## Installation - -1. Clone or download this repository into your ComfyUI `custom_nodes` directory: - -```bash -cd ComfyUI/custom_nodes -git clone https://github.com/MadiatorLabs/ComfyUI-RunpodDirect.git -``` - -Or manually: -- Create a folder: `ComfyUI/custom_nodes/ComfyUI-RunpodDirect` -- Copy all files from this repository into that folder - -2. Restart ComfyUI - -3. **Hard refresh your browser** to load the new extension: - - Windows/Linux: `Ctrl + Shift + R` or `Ctrl + F5` - - Mac: `Cmd + Shift + R` - - Or: Open DevTools (F12) → Right-click refresh → "Empty Cache and Hard Reload" - -4. Verify the version loaded by checking the browser console (F12) for: - ``` - [RunpodDirect] v1.0.0 - ``` +- **Direct Downloads**: Download models directly to your ComfyUI instance. +- **Background Downloads**: Downloads happen in the background with progress tracking. +- **Multi-Connection Support**: Accelerated downloads using multiple connections. +- **Cross-Platform**: Works on Linux, Windows, and macOS. +- **Secure**: Validates filenames and paths to prevent security issues. +- **Privacy Focused**: No external tracking or analytics. ## Usage ### Via Missing Models Dialog -1. Load a workflow that references missing models +1. Load a workflow that references missing models. 2. When the "Missing Models" dialog appears, you'll see: - - **Download All Models to Pod** button (downloads all models sequentially) + - **Download All Models** button (downloads all models sequentially) - Individual buttons for each model: - - **Download** (browser download - original functionality) - - **Copy URL** (copy model URL - original functionality) - - **Download to Pod** (NEW - downloads directly to server) + - **Download** (browser download) + - **Copy URL** (copy model URL) + - **Download to Comfy** (NEW - downloads directly to the appropriate Comfy directory) 3. **Option A: Download All Models** - - Click "Download All Models to Pod (X)" button + - Click "Download All Models" button. - Watch the progress area showing: - - Current file being downloaded - - Overall progress (X/Y completed) - - Download speed and file size - - Pause/Resume/Cancel controls + - Current file being downloaded. + - Overall progress. + - Download speed and file size. + - Pause/Resume/Cancel controls. 4. **Option B: Download Individual Model** - - Click "Download to Pod" button for a specific model - - Button shows status with icons (spinner → checkmark/error) - - Download happens in the background with multi-connection support + - Click "Download to Comfy" button for a specific model. + - Button shows status with icons (spinner → checkmark/error). + - Download happens in the background. -## Security +## Installation -- Only allows downloads from whitelisted sources (as defined in ComfyUI's frontend) -- Downloads are saved to ComfyUI's model directories only -- Prevents overwriting existing files -- Validates all input parameters +### ComfyUI Manager (Recommended) +1. Search for "ComfyUI-AutoModelDownloader" in ComfyUI Manager. +2. Install the node. +3. Restart ComfyUI. + +### Manual Installation +1. Navigate to your ComfyUI `custom_nodes` directory. +2. Clone this repository: + ```bash + git clone https://github.com/FNGarvin/ComfyUI-AutoModelDownloader.git + ``` +3. Install dependencies: + ```bash + pip install -r requirements.txt + ``` +4. Restart ComfyUI. ## License @@ -77,12 +63,8 @@ This program is free software: you can redistribute it and/or modify it under th ## Contributing -Contributions are welcome! Please feel free to submit a Pull Request or open an Issue on [GitHub](https://github.com/MadiatorLabs/ComfyUI-RunpodDirect). +Contributions are welcome! Please feel free to submit a Pull Request or open an Issue. ## Credits -Developed by [MadiatorLabs](https://github.com/MadiatorLabs) - -## Support - -If you find this useful, please ⭐ star the repo on [GitHub](https://github.com/MadiatorLabs/ComfyUI-RunpodDirect)! +Since this is basically a Rebranded ComfyUI-RunpodDirect, all credit goes to MadiatorLabs. diff --git a/__init__.py b/__init__.py index 484a287..f0d6552 100644 --- a/__init__.py +++ b/__init__.py @@ -1,5 +1,5 @@ """ -ComfyUI RunpodDirect - Direct Model Downloads for RunPod +ComfyUI AutoModelDownloader - Direct Model Downloads for RunPod Download models directly to your RunPod instance with multi-connection support """ @@ -133,11 +133,11 @@ async def process_download_queue(): # Check if already downloading if current_download_task is not None and not current_download_task.done(): - logging.info("[RunpodDirect] Download already in progress, waiting...") + logging.info("[AutoModelDownloader] Download already in progress, waiting...") return # Already downloading if len(download_queue) == 0: - logging.info("[RunpodDirect] Queue is empty") + logging.info("[AutoModelDownloader] Queue is empty") return # Nothing to process # Get next download from queue @@ -151,7 +151,7 @@ async def process_download_queue(): active_downloads[download_id]["progress"] = 0 active_downloads[download_id]["downloaded"] = 0 - logging.info(f"[RunpodDirect] Starting download {download_id} with {NUM_CONNECTIONS} connections (full speed)") + logging.info(f"[AutoModelDownloader] Starting download {download_id} with {NUM_CONNECTIONS} connections (full speed)") # Notify frontend that download is starting await PromptServer.instance.send("server_download_progress", { @@ -173,7 +173,7 @@ def on_download_complete(download_id): global current_download_task current_download_task = None - logging.info(f"[RunpodDirect] Download completed: {download_id}, processing next in queue...") + logging.info(f"[AutoModelDownloader] Download completed: {download_id}, processing next in queue...") # Process next in queue asyncio.create_task(process_download_queue()) @@ -205,7 +205,7 @@ async def download_file(url, output_path, download_id): """Download file with multi-connection support and progress tracking""" import aiohttp - logging.info(f"[RunpodDirect] Download {download_id} using {NUM_CONNECTIONS} connections (full speed)") + logging.info(f"[AutoModelDownloader] Download {download_id} using {NUM_CONNECTIONS} connections (full speed)") try: # Initialize control for this download @@ -540,7 +540,7 @@ async def cancel_download(request): return web.json_response({"error": str(e)}, status=500) -@PromptServer.instance.routes.get("/extensions/ComfyUI-RunpodDirect/serverDownload.js") +@PromptServer.instance.routes.get("/extensions/ComfyUI-AutoModelDownloader/serverDownload.js") async def serve_js_with_version(request): """Serve JS file with cache-busting headers""" js_path = os.path.join(os.path.dirname(__file__), "web", "serverDownload.js") @@ -565,3 +565,4 @@ NODE_CLASS_MAPPINGS = {} NODE_DISPLAY_NAME_MAPPINGS = {} __all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS", "WEB_DIRECTORY"] +print("[AutoModelDownloader] Linked and Loaded!", flush=True) diff --git a/pyproject.toml b/pyproject.toml index 76bef85..cf820fc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,12 +24,11 @@ classifiers = [ ] [project.urls] -Repository = "https://github.com/MadiatorLabs/ComfyUI-RunpodDirect" -Documentation = "https://github.com/MadiatorLabs/ComfyUI-RunpodDirect#readme" -"Bug Tracker" = "https://github.com/MadiatorLabs/ComfyUI-RunpodDirect/issues" +Repository = "https://github.com/MadiatorLabs/ComfyUI-AutoModelDownloader" +Documentation = "https://github.com/MadiatorLabs/ComfyUI-AutoModelDownloader#readme" +"Bug Tracker" = "https://github.com/MadiatorLabs/ComfyUI-AutoModelDownloader/issues" [tool.comfy] PublisherId = "madiator2011" DisplayName = "Madiator2011" -Icon = "https://peer.madiator.cloud/lazy-static/avatars/769f014a-9bc4-45e4-8fb5-57512aea2e97.png" includes = [] \ No newline at end of file diff --git a/web/serverDownload.js b/web/serverDownload.js index ba22573..446530d 100644 --- a/web/serverDownload.js +++ b/web/serverDownload.js @@ -1,9 +1,9 @@ import { app } from "../../scripts/app.js"; import { api } from "../../scripts/api.js"; -// ComfyUI RunpodDirect Extension +// ComfyUI.AutoModelDownloader Extension // Version: 1.0.6 -console.log('[RunpodDirect] v1.0.6'); +console.log('[AutoModelDownloader] v1.0.6'); // Track download states const downloadStates = new Map(); @@ -64,7 +64,7 @@ api.addEventListener("server_download_complete", ({ detail }) => { // Increment counter BEFORE updating UI if (isDownloadingAll) { completedDownloads++; - console.log(`[RunpodDirect] Progress: ${completedDownloads}/${totalDownloads} completed`); + console.log(`[AutoModelDownloader] Progress: ${completedDownloads}/${totalDownloads} completed`); } downloadStates.set(download_id, { @@ -82,7 +82,7 @@ api.addEventListener("server_download_complete", ({ detail }) => { // Check if all downloads are done if (isDownloadingAll && completedDownloads >= totalDownloads) { - console.log('[RunpodDirect] All downloads completed!'); + console.log('[AutoModelDownloader] All downloads completed!'); isDownloadingAll = false; showRefreshPrompt(); } @@ -94,7 +94,7 @@ api.addEventListener("server_download_error", ({ detail }) => { // Increment counter BEFORE updating UI if (isDownloadingAll) { completedDownloads++; - console.log(`[RunpodDirect] Progress: ${completedDownloads}/${totalDownloads} completed (1 error)`); + console.log(`[AutoModelDownloader] Progress: ${completedDownloads}/${totalDownloads} completed (1 error)`); } downloadStates.set(download_id, { @@ -110,7 +110,7 @@ api.addEventListener("server_download_error", ({ detail }) => { // Check if all downloads are done (including failed ones) if (isDownloadingAll && completedDownloads >= totalDownloads) { - console.log('[RunpodDirect] All downloads completed!'); + console.log('[AutoModelDownloader] All downloads completed!'); isDownloadingAll = false; showRefreshPrompt(); } @@ -229,12 +229,12 @@ async function cancelDownload(downloadId) { // Process download queue - Sends all downloads to backend which handles queue management async function processDownloadQueue() { if (downloadQueue.length === 0) { - console.log('[RunpodDirect] No downloads in queue'); + console.log('[AutoModelDownloader] No downloads in queue'); return; } // Send all downloads to the backend (backend handles queue and priorities) - console.log(`[RunpodDirect] Starting ${downloadQueue.length} downloads (backend will queue and prioritize)`); + console.log(`[AutoModelDownloader] Starting ${downloadQueue.length} downloads (backend will queue and prioritize)`); const downloadsToStart = [...downloadQueue]; downloadQueue = []; // Clear queue as we're sending all to backend @@ -242,11 +242,11 @@ async function processDownloadQueue() { // Start all downloads - backend will queue and manage priorities // Pass markAsQueued=true so buttons show "Queued" status immediately for (const download of downloadsToStart) { - console.log(`[RunpodDirect] Queuing download ${download.filename}`); + console.log(`[AutoModelDownloader] Queuing download ${download.filename}`); await startServerDownload(download.url, download.directory, download.filename, true); } - console.log(`[RunpodDirect] All ${downloadsToStart.length} downloads queued on backend`); + console.log(`[AutoModelDownloader] All ${downloadsToStart.length} downloads queued on backend`); } // Show refresh prompt @@ -260,7 +260,7 @@ function showRefreshPrompt() { // Check if prompt already exists if (document.querySelector('.server-download-refresh-prompt')) { - console.log('[RunpodDirect] Refresh prompt already shown'); + console.log('[AutoModelDownloader] Refresh prompt already shown'); return; } @@ -371,7 +371,7 @@ function updateDownloadProgressItem(download_id, status, progress, downloaded, t try { if (item && item.parentNode) item.remove(); } catch (e) { - console.error('[RunpodDirect] Error removing progress item:', e); + console.error('[AutoModelDownloader] Error removing progress item:', e); } }, 2000); } @@ -390,7 +390,7 @@ function updateDownloadProgressItem(download_id, status, progress, downloaded, t container.appendChild(item); } } catch (e) { - console.error('[RunpodDirect] Error in updateDownloadProgressItem:', e); + console.error('[AutoModelDownloader] Error in updateDownloadProgressItem:', e); return; } @@ -436,7 +436,7 @@ function updateDownloadProgressItem(download_id, status, progress, downloaded, t `; } } catch (e) { - console.error('[RunpodDirect] Error updating progress item HTML:', e); + console.error('[AutoModelDownloader] Error updating progress item HTML:', e); } } @@ -449,7 +449,7 @@ window.serverDownload = { // Helper to inject buttons using MutationObserver function setupDialogObserver() { - console.log('[RunpodDirect] Setting up dialog observer'); + console.log('[AutoModelDownloader] Setting up dialog observer'); // Watch for the missing models dialog to appear const observer = new MutationObserver((mutations) => { @@ -464,7 +464,7 @@ function setupDialogObserver() { ); if (hasDialog) { - console.log('[RunpodDirect] Detected missing models dialog, injecting buttons...'); + console.log('[AutoModelDownloader] Detected missing models dialog, injecting buttons...'); setTimeout(() => { injectServerDownloadButtons(); }, 500); @@ -481,32 +481,32 @@ function setupDialogObserver() { subtree: true }); - console.log('[RunpodDirect] Observer active'); + console.log('[AutoModelDownloader] Observer active'); } function injectServerDownloadButtons() { - console.log('[RunpodDirect] injectServerDownloadButtons called'); + console.log('[AutoModelDownloader] injectServerDownloadButtons called'); // Find the missing models listbox const listbox = document.querySelector('.comfy-missing-models'); if (!listbox) { - console.log('[RunpodDirect] Missing models listbox not found'); + console.log('[AutoModelDownloader] Missing models listbox not found'); return; } - console.log('[RunpodDirect] Found .comfy-missing-models listbox'); + console.log('[AutoModelDownloader] Found .comfy-missing-models listbox'); // Check if we already added our UI if (document.querySelector('.server-download-all-btn')) { - console.log('[RunpodDirect] Buttons already injected'); + console.log('[AutoModelDownloader] Buttons already injected'); return; } const listItems = listbox.querySelectorAll('.p-listbox-option'); - console.log(`[RunpodDirect] Found ${listItems.length} list items`); + console.log(`[AutoModelDownloader] Found ${listItems.length} list items`); if (listItems.length === 0) { - console.log('[RunpodDirect] No list items found'); + console.log('[AutoModelDownloader] No list items found'); return; } @@ -521,7 +521,7 @@ function injectServerDownloadButtons() { const downloadAllLabel = document.createElement('span'); downloadAllLabel.className = 'p-button-label'; - downloadAllLabel.textContent = `Download All Models to Pod (${listItems.length})`; + downloadAllLabel.textContent = `Download All Models (${listItems.length})`; downloadAllBtn.appendChild(downloadAllLabel); downloadAllBtn.onclick = async (e) => { @@ -566,11 +566,11 @@ function injectServerDownloadButtons() { listbox.parentElement.insertBefore(downloadAllContainer, listbox); listItems.forEach((item, index) => { - console.log(`[RunpodDirect] Processing item ${index + 1}`); + console.log(`[AutoModelDownloader] Processing item ${index + 1}`); // Check if we already added the button if (item.querySelector('.server-download-btn')) { - console.log(`[RunpodDirect] Item ${index + 1} already has server download button, skipping`); + console.log(`[AutoModelDownloader] Item ${index + 1} already has server download button, skipping`); return; } @@ -586,11 +586,11 @@ function injectServerDownloadButtons() { // Find the main flex container const mainContainer = item.querySelector('.flex.flex-row.items-center.gap-2'); if (!mainContainer) { - console.log('[RunpodDirect] Main flex container not found'); + console.log('[AutoModelDownloader] Main flex container not found'); return; } - console.log('[RunpodDirect] Found main container'); + console.log('[AutoModelDownloader] Found main container'); // We'll create a new div for our button (following the same pattern) const buttonWrapper = document.createElement('div'); @@ -598,25 +598,25 @@ function injectServerDownloadButtons() { // Get model info from the item const labelElement = item.querySelector('[title]'); if (!labelElement) { - console.log('[RunpodDirect] No title element found'); + console.log('[AutoModelDownloader] No title element found'); return; } const label = labelElement.textContent.trim(); const url = labelElement.getAttribute('title'); - console.log(`[RunpodDirect] Model: ${label}, URL: ${url}`); + console.log(`[AutoModelDownloader] Model: ${label}, URL: ${url}`); // Parse "checkpoints / model.safetensors" format const parts = label.split('/').map(p => p.trim()); if (parts.length !== 2) { - console.log(`[RunpodDirect] Could not parse label format: ${label}`); + console.log(`[AutoModelDownloader] Could not parse label format: ${label}`); return; } const directory = parts[0]; const filename = parts[1]; const download_id = `${directory}/${filename}`; - console.log(`[RunpodDirect] Creating button for ${download_id}`); + console.log(`[AutoModelDownloader] Creating button for ${download_id}`); // Create server download button const serverDownloadBtn = document.createElement('button'); @@ -626,7 +626,7 @@ function injectServerDownloadButtons() { // Create button content const btnLabel = document.createElement('span'); btnLabel.className = 'p-button-label'; - btnLabel.textContent = 'Download to Pod'; + btnLabel.textContent = 'Download to Host'; serverDownloadBtn.appendChild(btnLabel); // Status indicator (icon) @@ -696,34 +696,34 @@ function injectServerDownloadButtons() { // Add wrapper to main container (alongside Download and Copy URL) mainContainer.appendChild(buttonWrapper); - console.log(`[RunpodDirect] Button added to main container for ${download_id}`); + console.log(`[AutoModelDownloader] Button added to main container for ${download_id}`); }); - console.log('[RunpodDirect] Button injection complete'); + console.log('[AutoModelDownloader] Button injection complete'); } // Register the extension app.registerExtension({ - name: "ComfyUI.RunpodDirect", + name: "ComfyUI.AutoModelDownloader", async setup() { - console.log("[RunpodDirect] Extension setup starting"); + console.log("[AutoModelDownloader] Extension setup starting"); // Set up observer to watch for missing models dialog setupDialogObserver(); // Also try to inject immediately if dialog already exists setTimeout(() => { - console.log('[RunpodDirect] Checking for existing dialog...'); + console.log('[AutoModelDownloader] Checking for existing dialog...'); injectServerDownloadButtons(); }, 1000); // Try again after a longer delay setTimeout(() => { - console.log('[RunpodDirect] Second check for dialog...'); + console.log('[AutoModelDownloader] Second check for dialog...'); injectServerDownloadButtons(); }, 3000); - console.log("[RunpodDirect] Extension setup complete"); + console.log("[AutoModelDownloader] Extension setup complete"); } });