Files
huchukato-ComfyUI-HuggingFace/web/js/api/huggingface.js
T
huchukato 190950ab68 Add repo file browser and snapshot download to Search tab
- New backend endpoint /api/huggingface/repo_tree lists files/folders
  of a HF repo via HfApi.list_repo_tree (with list_repo_files fallback).
- SearchModels now returns real creator (from org/repo), metrics,
  publishedAt and pipeline_tag-derived type, so cards show useful info.
- New repoTreeRenderer modal: breadcrumb navigation, per-file Download
  (prefills Download tab with resolve URL) and Download repo button
  (snapshot_download, auto-selects diffusers folder).
- searchRenderer rewritten: removed CivitAI-style version buttons;
  each card now has View / Browse / Repo actions.
- eventListeners, UI, api client updated for the new buttons.
- DownloadModel no longer blocks snapshot downloads when the target
  directory already exists (allows resume).
- CSS: added repo tree modal styles, removed dead all-versions CSS.
2026-09-01 23:22:32 +02:00

173 lines
5.2 KiB
JavaScript

// API client for HuggingFace UI
// Wraps ComfyUI's fetchApi with consistent error handling
import { api } from "../../../../scripts/api.js";
export class HuggingFaceDownloaderAPI {
static async _request(endpoint, options = {}) {
try {
const url = endpoint.startsWith("/") ? endpoint : `/${endpoint}`;
const response = await api.fetchApi(url, options);
if (!response.ok) {
let errorData;
const status = response.status;
const statusText = response.statusText;
try {
errorData = await response.json();
if (typeof errorData !== "object" || errorData === null) {
errorData = { detail: String(errorData) };
}
} catch (_) {
const detailText = await response.text().catch(() => `Status ${status} - Could not read error text`);
errorData = {
error: `HTTP error ${status}`,
details: String(detailText).substring(0, 500),
};
}
const err = new Error(errorData.error || errorData.reason || `HTTP Error: ${status} ${statusText}`);
err.details = errorData.details || errorData.detail || errorData.error || "No details provided.";
err.status = status;
throw err;
}
if (response.status === 204 || response.headers.get("Content-Length") === "0") {
return null;
}
return await response.json();
} catch (error) {
if (!error.details) error.details = error.message;
throw error;
}
}
static async downloadModel(params) {
return await this._request("/huggingface/download", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(params),
});
}
static async getModelDetails(params) {
return await this._request("/huggingface/get_model_details", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(params),
});
}
static async getRepoTree(params) {
return await this._request("/huggingface/repo_tree", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(params),
});
}
static async getStatus() {
return await this._request("/huggingface/status");
}
static async cancelDownload(downloadId) {
return await this._request("/huggingface/cancel", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ download_id: downloadId }),
});
}
static async searchModels(params) {
return await this._request("/huggingface/search", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(params),
});
}
static async getBaseModels() {
return await this._request("/huggingface/base_models");
}
static async getModelTypes() {
return await this._request("/huggingface/model_types");
}
static async getModelDirs(modelType) {
const q = encodeURIComponent(modelType || 'checkpoints');
return await this._request(`/huggingface/model_dirs?type=${q}`);
}
static async createModelDir(modelType, newDir, root = "") {
return await this._request("/huggingface/create_dir", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ model_type: modelType, new_dir: newDir, root }),
});
}
static async createModelType(name) {
return await this._request("/huggingface/create_model_type", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name }),
});
}
static async getModelRoots(modelType) {
const q = encodeURIComponent(modelType || 'checkpoints');
return await this._request(`/huggingface/model_roots?type=${q}`);
}
static async createModelRoot(modelType, absPath) {
return await this._request("/huggingface/create_root", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ model_type: modelType, path: absPath }),
});
}
static async getGlobalRoot() {
return await this._request("/huggingface/global_root");
}
static async setGlobalRoot(path) {
return await this._request("/huggingface/global_root", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ path }),
});
}
static async clearGlobalRoot() {
return await this._request("/huggingface/global_root/clear", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({}),
});
}
static async retryDownload(downloadId) {
return await this._request("/huggingface/retry", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ download_id: downloadId }),
});
}
static async openPath(downloadId) {
return await this._request("/huggingface/open_path", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ download_id: downloadId }),
});
}
static async clearHistory() {
return await this._request("/huggingface/clear_history", {
method: "POST",
headers: { "Content-Type": "application/json" },
});
}
}