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:
@@ -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
|
||||
|
||||
@@ -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
@@ -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()))
|
||||
|
||||
@@ -439,6 +439,13 @@
|
||||
</div>
|
||||
<p class="text-xs text-pm-muted">Automatically append trigger words when <code class="text-pm-accent"><lora:name:weight></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">
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user