From 839b93c9bef4910e3b7e06c0e6c6ac3dad1f9586 Mon Sep 17 00:00:00 2001 From: Christian Bastian Date: Fri, 23 Feb 2024 02:36:40 -0500 Subject: [PATCH] BUG FIX: Civitai API key token added to a url with existing query string. - Also updated README. --- README.md | 78 ++++++++++++++++++++------------------------ __init__.py | 12 ++++--- web/model-manager.js | 34 +++++++++++++------ 3 files changed, 68 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index 7bd938b..17360f8 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,9 @@ I made this fork because the original repo was inactive and missing many things ### Download Tab -- View multiple models associated to a url. -- Select download directory. -- Optionally download default or custom preview image. +- View multiple models associated with a url. +- Select a download directory. +- Optionally also download a model preview image (a default image along side the model, from another url or locally uploaded). - Civitai and HuggingFace API token configurable in `server_settings.yaml`. ### Models Tab @@ -29,12 +29,12 @@ I made this fork because the original repo was inactive and missing many things - Include models listed in ComfyUI's `extra_model_paths.yaml` or added in `ComfyUI/models`. - Sort for models (Date Created, Date Modified, Name). -### Model Info +### Model Info View - View model metadata, including training tags and bucket resolutions. -- Delete or move a model. - Read, edit and save notes in a `.txt` file beside the model. -- Change or remove preview image with a file upload or a url. +- Change or remove a model's preview image (add a different one using a url or local upload). +- Move or **permanently** remove models. ### ComfyUI Node Graph @@ -47,62 +47,56 @@ I made this fork because the original repo was inactive and missing many things ### Settings Tab -- Change colors using ComfyUI's theme colors. - Settings tab saved in `ui_settings.yaml`. - Hide/Show 'add' and 'copy-to-clipboard' buttons. - Text to always search. - Show/Hide add embedding extension. +- Colors follow ComfyUI's current theme. ## TODO -### Code - -- Javascript cleanup. - - Seperate into classes per tab? - - HTML generation all inside main class? - - More server driven, HTMX-like HTML generation? (Avoid x2 states) - -### Model Copying - -- Copy model path? - ### Download Model Info - Optional (re)download `📥︎` model info from the internet and cache the text file locally. (requires checksum?) - Radio buttons to swap between downloaded and server view. +### Sidebar + +- Drag sidebar width/height dynamically. + +### Accessibility + +- Proper naming, labeling, alt text, etc. for html elements. +- Tool tips. +- Better error messages. + +### Image preview + +- Better placeholder preview. (with proper spelling!) +- Show preview images for videos. + - If ffmpeg or cv2 available, extract the first frame of the video and use as image preview. + ### Settings -- Exclude hidden folders with a `.` prefix. -- Enable optional checksum to detect if a model is already downloaded. -- Sidebar width/height. +- Toggle exclusion of "hidden folders" with a `.` prefix. +- Sidebar default width/height. +- Toggle non-uniform preview sizes. (How to handle extreme aspect ratios?) ### Search filtering and sort - Real-time search - Check search code is optimized to avoid recalculation on every minor input change -- Directory dropdown - - Use always filter to filter directory content auto-suggest dropdown +- Filter directory dropdown + - Filter directory content in auto-suggest dropdown (not clear how this should be implemented) - Filters dropdown - - Stable Diffusion model version/Clip/Upscale/? + - Stable Diffusion model version, if applicable - Favorites -- Sort-by dropdown - - Recently used (ascending/decending) - - Frequently used (ascending/decending) -- `or` vs `and` type of keyword search (currently `and`) +- Swap between `and` and `or` keyword search? (currently `and`) -### Image preview +### Code -- Support multiple preview images. (swipe?) -- Show preview images for videos. - - If ffmpeg or cv2 available, extract the first frame of the video and use as image preview. - - Play preview video? - -### Accessibility - -- Proper naming and labeling. -- Tool tips? - -### Sidebar - -- Drag sidebar width/height dynamically. +- Javascript cleanup. + - Better abstraction and objectification. (After codebase settles down) + - Separate into classes per tab? + - HTML generation all inside main class? + - More server driven, HTMX-like HTML generation? (Avoid x2 states) diff --git a/__init__.py b/__init__.py index b192e6e..45d7e6e 100644 --- a/__init__.py +++ b/__init__.py @@ -409,14 +409,18 @@ def download_file(url, filename, overwrite): api_key = server_settings["civitai_api_key"] if (api_key != ""): def_headers["Authorization"] = f"Bearer {api_key}" - url = url + f"?token={api_key}" # TODO: Authorization didn't work in the header + url += "&" if "?" in url else "?" # not the most robust solution + url += f"token={api_key}" # TODO: Authorization didn't work in the header elif url.startswith("https://huggingface.co/"): api_key = server_settings["huggingface_api_key"] if api_key != "": def_headers["Authorization"] = f"Bearer {api_key}" rh = requests.get(url=url, stream=True, verify=False, headers=def_headers, proxies=None, allow_redirects=False) if not rh.ok: - raise ValueError("Unable to download") + raise ValueError( + "Unable to download! Request header status code: " + + str(rh.status_code) + ) downloaded_size = 0 if rh.status_code == 200 and os.path.exists(filename_temp): @@ -432,7 +436,7 @@ def download_file(url, filename, overwrite): if not redirect_url.startswith("http"): # Civitai requires login (NSFW or user-required) # TODO: inform user WHY download failed - raise ValueError("Unable to download!") + raise ValueError("Unable to download from Civitai! Redirect url: " + str(redirect_url)) download_file(redirect_url, filename, overwrite) return if rh.status_code == 302 and r.status_code == 302: @@ -440,7 +444,7 @@ def download_file(url, filename, overwrite): redirect_url = r.content.decode("utf-8") redirect_url_index = redirect_url.find("http") if redirect_url_index == -1: - raise ValueError("Unable to download!") + raise ValueError("Unable to download from HuggingFace! Redirect url: " + str(redirect_url)) download_file(redirect_url[redirect_url_index:], filename, overwrite) return elif rh.status_code == 200 and r.status_code == 206: diff --git a/web/model-manager.js b/web/model-manager.js index fb9f8c8..b90f5b1 100644 --- a/web/model-manager.js +++ b/web/model-manager.js @@ -121,11 +121,17 @@ function modelTypeToComfyUiDirectory(modelType, fileType) { * @param {string} id - Model ID. * @param {string} apiPath - Civitai request subdirectory. "models" for 'model' urls. "model-version" for 'api' urls. * - * @returns {Object} Dictionary containing recieved model info. Returns an empty if fails. + * @returns {Promise} Dictionary containing recieved model info. Returns an empty if fails. */ async function civitai_requestInfo(id, apiPath) { const url = "https://civitai.com/api/v1/" + apiPath + "/" + id; - return await request(url); + try { + return await request(url); + } + catch (error) { + console.error("Failed to get model info from Civitai!", error); + return {}; + } } /** @@ -186,7 +192,7 @@ function civitai_getModelFilesInfo(modelVersionInfo, type = null, fp = null, siz * * @param {string} stringUrl - Model url. * - * @returns {Object} - Download information for the given url. + * @returns {Promise} - Download information for the given url. */ async function civitai_getFilteredInfo(stringUrl) { const url = new URL(stringUrl); @@ -264,7 +270,13 @@ async function civitai_getFilteredInfo(stringUrl) { */ async function huggingFace_requestInfo(id, apiPath = "models") { const url = "https://huggingface.co/api/" + apiPath + "/" + id; - return await request(url); + try { + return await request(url); + } + catch (error) { + console.error("Failed to get model info from HuggingFace!", error); + return {}; + } } /** @@ -1606,15 +1618,14 @@ class ModelManager extends ComfyDialog { /** @type {HTMLDivElement} */ modelInfoUrl: null, /** @type {HTMLDivElement} */ modelInfoOverwrite: null, /** @type {HTMLDivElement} */ modelInfos: null, - modelInfoRadioGroup: null, - modelInfoPreview: null, - modelInfoDefaultUri: null, - setAsPreviewButton: null, + /** @type {HTMLDivElement} */ modelInfoRadioGroup: null, + /** @type {HTMLDivElement} */ modelInfoPreview: null, + /** @type {HTMLDivElement} */ modelInfoDefaultUri: null, + /** @type {HTMLDivElement} */ setAsPreviewButton: null, /** @type {HTMLDivElement} */ modelGrid: null, /** @type {HTMLSelectElement} */ modelTypeSelect: null, /** @type {HTMLSelectElement} */ modelSortSelect: null, - /** @type {HTMLDivElement} */ //searchDirectoryDropdown: null, /** @type {HTMLInputElement} */ modelContentFilter: null, /** @type {HTMLDivElement} */ sidebarButtons: null, @@ -1649,7 +1660,10 @@ class ModelManager extends ComfyDialog { /** @type {string} */ #systemSeparator = null; + /** @type {Function} */ #resetModelInfoPreview = () => {}; + + /** @type {Function} */ #modelInfoDefaultIsChecked = () => { return false; }; constructor() { @@ -2161,7 +2175,7 @@ class ModelManager extends ComfyDialog { } /** - * @param {boolean} reloadData + * @param {Promise} reloadData */ async #settingsTab_reload(reloadData) { const data = await request("/model-manager/settings/load");