BUG FIX: Civitai API key token added to a url with existing query string.
- Also updated README.
This commit is contained in:
78
README.md
78
README.md
@@ -15,9 +15,9 @@ I made this fork because the original repo was inactive and missing many things
|
|||||||
|
|
||||||
### Download Tab
|
### Download Tab
|
||||||
|
|
||||||
- View multiple models associated to a url.
|
- View multiple models associated with a url.
|
||||||
- Select download directory.
|
- Select a download directory.
|
||||||
- Optionally download default or custom preview image.
|
- 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`.
|
- Civitai and HuggingFace API token configurable in `server_settings.yaml`.
|
||||||
|
|
||||||
### Models Tab
|
### 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`.
|
- Include models listed in ComfyUI's `extra_model_paths.yaml` or added in `ComfyUI/models`.
|
||||||
- Sort for models (Date Created, Date Modified, Name).
|
- Sort for models (Date Created, Date Modified, Name).
|
||||||
|
|
||||||
### Model Info
|
### Model Info View
|
||||||
|
|
||||||
- View model metadata, including training tags and bucket resolutions.
|
- 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.
|
- 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
|
### ComfyUI Node Graph
|
||||||
|
|
||||||
@@ -47,62 +47,56 @@ I made this fork because the original repo was inactive and missing many things
|
|||||||
|
|
||||||
### Settings Tab
|
### Settings Tab
|
||||||
|
|
||||||
- Change colors using ComfyUI's theme colors.
|
|
||||||
- Settings tab saved in `ui_settings.yaml`.
|
- Settings tab saved in `ui_settings.yaml`.
|
||||||
- Hide/Show 'add' and 'copy-to-clipboard' buttons.
|
- Hide/Show 'add' and 'copy-to-clipboard' buttons.
|
||||||
- Text to always search.
|
- Text to always search.
|
||||||
- Show/Hide add embedding extension.
|
- Show/Hide add embedding extension.
|
||||||
|
- Colors follow ComfyUI's current theme.
|
||||||
|
|
||||||
## TODO
|
## 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
|
### Download Model Info
|
||||||
|
|
||||||
- Optional (re)download `📥︎` model info from the internet and cache the text file locally. (requires checksum?)
|
- 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.
|
- 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
|
### Settings
|
||||||
|
|
||||||
- Exclude hidden folders with a `.` prefix.
|
- Toggle exclusion of "hidden folders" with a `.` prefix.
|
||||||
- Enable optional checksum to detect if a model is already downloaded.
|
- Sidebar default width/height.
|
||||||
- Sidebar width/height.
|
- Toggle non-uniform preview sizes. (How to handle extreme aspect ratios?)
|
||||||
|
|
||||||
### Search filtering and sort
|
### Search filtering and sort
|
||||||
|
|
||||||
- Real-time search
|
- Real-time search
|
||||||
- Check search code is optimized to avoid recalculation on every minor input change
|
- Check search code is optimized to avoid recalculation on every minor input change
|
||||||
- Directory dropdown
|
- Filter directory dropdown
|
||||||
- Use always filter to filter directory content auto-suggest dropdown
|
- Filter directory content in auto-suggest dropdown (not clear how this should be implemented)
|
||||||
- Filters dropdown
|
- Filters dropdown
|
||||||
- Stable Diffusion model version/Clip/Upscale/?
|
- Stable Diffusion model version, if applicable
|
||||||
- Favorites
|
- Favorites
|
||||||
- Sort-by dropdown
|
- Swap between `and` and `or` keyword search? (currently `and`)
|
||||||
- Recently used (ascending/decending)
|
|
||||||
- Frequently used (ascending/decending)
|
|
||||||
- `or` vs `and` type of keyword search (currently `and`)
|
|
||||||
|
|
||||||
### Image preview
|
### Code
|
||||||
|
|
||||||
- Support multiple preview images. (swipe?)
|
- Javascript cleanup.
|
||||||
- Show preview images for videos.
|
- Better abstraction and objectification. (After codebase settles down)
|
||||||
- If ffmpeg or cv2 available, extract the first frame of the video and use as image preview.
|
- Separate into classes per tab?
|
||||||
- Play preview video?
|
- HTML generation all inside main class?
|
||||||
|
- More server driven, HTMX-like HTML generation? (Avoid x2 states)
|
||||||
### Accessibility
|
|
||||||
|
|
||||||
- Proper naming and labeling.
|
|
||||||
- Tool tips?
|
|
||||||
|
|
||||||
### Sidebar
|
|
||||||
|
|
||||||
- Drag sidebar width/height dynamically.
|
|
||||||
|
|||||||
12
__init__.py
12
__init__.py
@@ -409,14 +409,18 @@ def download_file(url, filename, overwrite):
|
|||||||
api_key = server_settings["civitai_api_key"]
|
api_key = server_settings["civitai_api_key"]
|
||||||
if (api_key != ""):
|
if (api_key != ""):
|
||||||
def_headers["Authorization"] = f"Bearer {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/"):
|
elif url.startswith("https://huggingface.co/"):
|
||||||
api_key = server_settings["huggingface_api_key"]
|
api_key = server_settings["huggingface_api_key"]
|
||||||
if api_key != "":
|
if api_key != "":
|
||||||
def_headers["Authorization"] = f"Bearer {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)
|
rh = requests.get(url=url, stream=True, verify=False, headers=def_headers, proxies=None, allow_redirects=False)
|
||||||
if not rh.ok:
|
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
|
downloaded_size = 0
|
||||||
if rh.status_code == 200 and os.path.exists(filename_temp):
|
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"):
|
if not redirect_url.startswith("http"):
|
||||||
# Civitai requires login (NSFW or user-required)
|
# Civitai requires login (NSFW or user-required)
|
||||||
# TODO: inform user WHY download failed
|
# 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)
|
download_file(redirect_url, filename, overwrite)
|
||||||
return
|
return
|
||||||
if rh.status_code == 302 and r.status_code == 302:
|
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 = r.content.decode("utf-8")
|
||||||
redirect_url_index = redirect_url.find("http")
|
redirect_url_index = redirect_url.find("http")
|
||||||
if redirect_url_index == -1:
|
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)
|
download_file(redirect_url[redirect_url_index:], filename, overwrite)
|
||||||
return
|
return
|
||||||
elif rh.status_code == 200 and r.status_code == 206:
|
elif rh.status_code == 200 and r.status_code == 206:
|
||||||
|
|||||||
@@ -121,11 +121,17 @@ function modelTypeToComfyUiDirectory(modelType, fileType) {
|
|||||||
* @param {string} id - Model ID.
|
* @param {string} id - Model ID.
|
||||||
* @param {string} apiPath - Civitai request subdirectory. "models" for 'model' urls. "model-version" for 'api' urls.
|
* @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<Object>} Dictionary containing recieved model info. Returns an empty if fails.
|
||||||
*/
|
*/
|
||||||
async function civitai_requestInfo(id, apiPath) {
|
async function civitai_requestInfo(id, apiPath) {
|
||||||
const url = "https://civitai.com/api/v1/" + apiPath + "/" + id;
|
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.
|
* @param {string} stringUrl - Model url.
|
||||||
*
|
*
|
||||||
* @returns {Object} - Download information for the given url.
|
* @returns {Promise<Object>} - Download information for the given url.
|
||||||
*/
|
*/
|
||||||
async function civitai_getFilteredInfo(stringUrl) {
|
async function civitai_getFilteredInfo(stringUrl) {
|
||||||
const url = new URL(stringUrl);
|
const url = new URL(stringUrl);
|
||||||
@@ -264,7 +270,13 @@ async function civitai_getFilteredInfo(stringUrl) {
|
|||||||
*/
|
*/
|
||||||
async function huggingFace_requestInfo(id, apiPath = "models") {
|
async function huggingFace_requestInfo(id, apiPath = "models") {
|
||||||
const url = "https://huggingface.co/api/" + apiPath + "/" + id;
|
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} */ modelInfoUrl: null,
|
||||||
/** @type {HTMLDivElement} */ modelInfoOverwrite: null,
|
/** @type {HTMLDivElement} */ modelInfoOverwrite: null,
|
||||||
/** @type {HTMLDivElement} */ modelInfos: null,
|
/** @type {HTMLDivElement} */ modelInfos: null,
|
||||||
modelInfoRadioGroup: null,
|
/** @type {HTMLDivElement} */ modelInfoRadioGroup: null,
|
||||||
modelInfoPreview: null,
|
/** @type {HTMLDivElement} */ modelInfoPreview: null,
|
||||||
modelInfoDefaultUri: null,
|
/** @type {HTMLDivElement} */ modelInfoDefaultUri: null,
|
||||||
setAsPreviewButton: null,
|
/** @type {HTMLDivElement} */ setAsPreviewButton: null,
|
||||||
|
|
||||||
/** @type {HTMLDivElement} */ modelGrid: null,
|
/** @type {HTMLDivElement} */ modelGrid: null,
|
||||||
/** @type {HTMLSelectElement} */ modelTypeSelect: null,
|
/** @type {HTMLSelectElement} */ modelTypeSelect: null,
|
||||||
/** @type {HTMLSelectElement} */ modelSortSelect: null,
|
/** @type {HTMLSelectElement} */ modelSortSelect: null,
|
||||||
/** @type {HTMLDivElement} */ //searchDirectoryDropdown: null,
|
|
||||||
/** @type {HTMLInputElement} */ modelContentFilter: null,
|
/** @type {HTMLInputElement} */ modelContentFilter: null,
|
||||||
|
|
||||||
/** @type {HTMLDivElement} */ sidebarButtons: null,
|
/** @type {HTMLDivElement} */ sidebarButtons: null,
|
||||||
@@ -1649,7 +1660,10 @@ class ModelManager extends ComfyDialog {
|
|||||||
/** @type {string} */
|
/** @type {string} */
|
||||||
#systemSeparator = null;
|
#systemSeparator = null;
|
||||||
|
|
||||||
|
/** @type {Function} */
|
||||||
#resetModelInfoPreview = () => {};
|
#resetModelInfoPreview = () => {};
|
||||||
|
|
||||||
|
/** @type {Function} */
|
||||||
#modelInfoDefaultIsChecked = () => { return false; };
|
#modelInfoDefaultIsChecked = () => { return false; };
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -2161,7 +2175,7 @@ class ModelManager extends ComfyDialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {boolean} reloadData
|
* @param {Promise<boolean>} reloadData
|
||||||
*/
|
*/
|
||||||
async #settingsTab_reload(reloadData) {
|
async #settingsTab_reload(reloadData) {
|
||||||
const data = await request("/model-manager/settings/load");
|
const data = await request("/model-manager/settings/load");
|
||||||
|
|||||||
Reference in New Issue
Block a user