feat: add CivitAI API key setting for authenticated image downloads (#52)

Most civitai example images (especially NSFW) require authentication.
Adds API key field to the Integrations settings panel, passed as
Bearer token when downloading example images.
This commit is contained in:
Vito Sansevero
2026-04-03 08:41:41 -07:00
parent 451596fab3
commit 0e41e6d65c
5 changed files with 30 additions and 5 deletions
+8 -1
View File
@@ -76,6 +76,7 @@ class LoraIntegrationMixin:
"enabled": config["enabled"],
"path": config["path"],
"trigger_words_enabled": config["trigger_words_enabled"],
"civitai_api_key": config.get("civitai_api_key", ""),
"detected": detected_path is not None,
"detected_path": detected_path or "",
"trigger_cache_loaded": cache.is_loaded,
@@ -94,6 +95,7 @@ class LoraIntegrationMixin:
enabled = data.get("enabled", False)
path = data.get("path", "")
trigger_words = data.get("trigger_words_enabled", False)
civitai_key = data.get("civitai_api_key", "")
from ..config import IntegrationConfig, PromptManagerConfig
from ..lora_utils import detect_lora_manager, get_trigger_cache
@@ -115,6 +117,7 @@ class LoraIntegrationMixin:
IntegrationConfig.LORA_MANAGER_ENABLED = enabled
IntegrationConfig.LORA_MANAGER_PATH = path
IntegrationConfig.LORA_TRIGGER_WORDS_ENABLED = trigger_words
IntegrationConfig.CIVITAI_API_KEY = civitai_key
# Persist to config.json
config_dir = os.path.dirname(
@@ -248,7 +251,11 @@ class LoraIntegrationMixin:
get_preview_images_from_metadata, metadata, meta_file
)
civitai_paths = await self._run_in_executor(
download_civitai_images, metadata, meta_file, cache_dir
download_civitai_images,
metadata,
meta_file,
cache_dir,
IntegrationConfig.CIVITAI_API_KEY,
)
# Merge, local first, dedup
+4
View File
@@ -212,6 +212,7 @@ class IntegrationConfig:
LORA_MANAGER_ENABLED = False
LORA_MANAGER_PATH = "" # Auto-detected if empty
LORA_TRIGGER_WORDS_ENABLED = False # Auto-inject trigger words into prompts
CIVITAI_API_KEY = "" # Required to download NSFW example images
@classmethod
def get_config(cls) -> Dict[str, Any]:
@@ -220,6 +221,7 @@ class IntegrationConfig:
"enabled": cls.LORA_MANAGER_ENABLED,
"path": cls.LORA_MANAGER_PATH,
"trigger_words_enabled": cls.LORA_TRIGGER_WORDS_ENABLED,
"civitai_api_key": cls.CIVITAI_API_KEY,
},
}
@@ -232,6 +234,8 @@ class IntegrationConfig:
cls.LORA_MANAGER_PATH = lora["path"]
if "trigger_words_enabled" in lora:
cls.LORA_TRIGGER_WORDS_ENABLED = lora["trigger_words_enabled"]
if "civitai_api_key" in lora:
cls.CIVITAI_API_KEY = lora["civitai_api_key"]
class PromptManagerConfig:
+8 -4
View File
@@ -297,13 +297,16 @@ def get_preview_image_from_metadata(
def download_civitai_images(
metadata: Dict, metadata_path: Path, cache_dir: Path
metadata: Dict, metadata_path: Path, cache_dir: Path, api_key: str = ""
) -> List[str]:
"""Download civitai example images to a local cache directory.
Images are stored as ``<cache_dir>/<lora_stem>/<hash>.jpg``.
Already-downloaded files are skipped.
Args:
api_key: CivitAI API key for authenticated downloads (NSFW content).
Returns:
List of absolute paths to downloaded image files.
"""
@@ -340,9 +343,10 @@ def download_civitai_images(
continue
try:
req = urllib.request.Request(
url, headers={"User-Agent": "ComfyUI-PromptManager/1.0"}
)
headers = {"User-Agent": "ComfyUI-PromptManager/1.0"}
if api_key:
headers["Authorization"] = f"Bearer {api_key}"
req = urllib.request.Request(url, headers=headers)
with urllib.request.urlopen(req, timeout=15) as resp:
local_path.write_bytes(resp.read())
downloaded.append(str(local_path.resolve()))
+7
View File
@@ -439,6 +439,13 @@
</div>
<p class="text-xs text-pm-muted">Automatically append trigger words when <code class="text-pm-accent">&lt;lora:name:weight&gt;</code> is detected in prompts.</p>
<div class="mt-2">
<label class="block text-xs font-medium text-pm-secondary mb-1">CivitAI API Key</label>
<input type="password" id="civitaiApiKey" placeholder="Optional — required for NSFW example images"
class="w-full px-2.5 py-1.5 bg-pm-input border border-pm rounded-pm-sm text-pm text-[13px] placeholder:text-pm-muted focus:outline-none">
<p class="text-xs text-pm-muted mt-1">Get your key from <a href="https://civitai.com/user/account" target="_blank" class="text-pm-accent hover:underline">civitai.com/user/account</a></p>
</div>
<button id="loraImportBtn" type="button"
class="mt-1 flex items-center gap-1 text-xs text-pm-accent hover:text-pm-accent-hover">
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
+3
View File
@@ -796,6 +796,7 @@
toggle.disabled = false;
document.getElementById("loraManagerPath").value = status.path;
document.getElementById("loraTriggerWords").checked = status.trigger_words_enabled;
document.getElementById("civitaiApiKey").value = status.civitai_api_key || "";
settings.classList.remove("hidden");
this._loraPath = status.path;
this._bindLoraEvents();
@@ -850,6 +851,7 @@
async saveLoraSettings() {
const enabled = document.getElementById("loraEnabled").checked;
const triggerWords = document.getElementById("loraTriggerWords").checked;
const civitaiKey = document.getElementById("civitaiApiKey").value.trim();
const path = this._loraPath || "";
try {
@@ -860,6 +862,7 @@
enabled,
path,
trigger_words_enabled: triggerWords,
civitai_api_key: civitaiKey,
}),
});
const data = await res.json();