161 lines
5.8 KiB
Python
161 lines
5.8 KiB
Python
|
|
from urllib.parse import urlparse, parse_qs, unquote
|
|
import requests
|
|
import os
|
|
import yaml
|
|
from tqdm import tqdm
|
|
import folder_paths
|
|
from comfy.comfy_types import IO
|
|
import logging
|
|
logger = logging.getLogger(__name__)
|
|
|
|
config_dir = os.path.join(folder_paths.base_path, "config")
|
|
|
|
|
|
class ModelsDownloaderOfUtils:
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"url_0": ("STRING", {"default": "", "multiline": True}),
|
|
"url_1": ("STRING", {"default": "", "multiline": True}),
|
|
"url_2": ("STRING", {"default": "", "multiline": True}),
|
|
"url_3": ("STRING", {"default": "", "multiline": True}),
|
|
"subdirectory": ("STRING", {"default": "loras"}),
|
|
"proxy": ("STRING", {"default": ""}),
|
|
"timeout": ("INT", {"default": 300, "min": 1, "max": 3600}),
|
|
},
|
|
"optional": {
|
|
"civitai_api_key": ("STRING", {"default": ""}),
|
|
}
|
|
}
|
|
|
|
RETURN_TYPES = (IO.ANY, IO.ANY, IO.ANY, IO.ANY)
|
|
RETURN_NAMES = ("model_name_0", "model_name_1", "model_name_2", "model_name_3")
|
|
FUNCTION = "download_models"
|
|
CATEGORY = "utils/download"
|
|
|
|
def extract_filename_from_url(self, url):
|
|
if not url or not url.strip():
|
|
return None
|
|
|
|
parsed = urlparse(url)
|
|
params = parse_qs(parsed.query)
|
|
|
|
path_part = unquote(parsed.path)
|
|
filename = os.path.basename(path_part)
|
|
|
|
if "?" in filename:
|
|
filename = filename.split("?")[0]
|
|
|
|
if not filename:
|
|
raise ValueError(f"cannot extract filename from url: {url}")
|
|
|
|
if "." not in filename:
|
|
if "format" in params:
|
|
format_param = params["format"][0]
|
|
extension_map = {
|
|
"SafeTensor": "safetensors",
|
|
"PickleTensor": "ckpt",
|
|
}
|
|
ext = extension_map.get(format_param, format_param.lower())
|
|
filename = f"{filename}.{ext}"
|
|
else:
|
|
raise ValueError(f"cannot extract filename from url: {url}")
|
|
|
|
return filename
|
|
|
|
def load_civitai_config(self):
|
|
config_path = os.path.join(config_dir, 'civitai_config.yml')
|
|
try:
|
|
with open(config_path, 'r', encoding='utf-8') as f:
|
|
config = yaml.load(f, Loader=yaml.FullLoader)
|
|
return config.get("API_KEY", "")
|
|
except FileNotFoundError:
|
|
return ""
|
|
|
|
def save_civitai_config(self, api_key):
|
|
config_path = os.path.join(config_dir, 'civitai_config.yml')
|
|
os.makedirs(config_dir, exist_ok=True)
|
|
config = {"API_KEY": api_key}
|
|
with open(config_path, 'w', encoding='utf-8') as f:
|
|
yaml.dump(config, f, indent=4)
|
|
|
|
def download_file(self, url, filepath, proxy, timeout, civitai_api_key=None):
|
|
proxies = {"http": proxy, "https": proxy} if proxy and proxy.strip() else None
|
|
|
|
headers = {}
|
|
if "civitai" in url.lower():
|
|
api_key = civitai_api_key if civitai_api_key else self.load_civitai_config()
|
|
if api_key:
|
|
headers["Authorization"] = f"Bearer {api_key}"
|
|
|
|
response = requests.get(url, proxies=proxies, timeout=timeout, stream=True, headers=headers)
|
|
response.raise_for_status()
|
|
|
|
os.makedirs(os.path.dirname(filepath), exist_ok=True)
|
|
|
|
total_size = int(response.headers.get("content-length", 0))
|
|
with open(filepath, "wb") as f:
|
|
if total_size > 0:
|
|
for chunk in tqdm(response.iter_content(chunk_size=8192), total=total_size // 8192, desc=f"Downloading {os.path.basename(filepath)}"):
|
|
f.write(chunk)
|
|
else:
|
|
for chunk in response.iter_content(chunk_size=8192):
|
|
f.write(chunk)
|
|
|
|
def download_models(self, url_0, url_1, url_2, url_3, subdirectory, proxy, timeout, civitai_api_key=""):
|
|
if civitai_api_key:
|
|
self.save_civitai_config(civitai_api_key)
|
|
|
|
urls = [url_0, url_1, url_2, url_3]
|
|
model_names = []
|
|
|
|
try:
|
|
base_paths = folder_paths.get_folder_paths(subdirectory)
|
|
except Exception as e:
|
|
logger.error(f"Error getting folder paths for {subdirectory}: {e}")
|
|
raise ValueError(f"Subdirectory '{subdirectory}' not found in models directory")
|
|
|
|
if not base_paths:
|
|
raise ValueError(f"Subdirectory '{subdirectory}' not found in models directory")
|
|
|
|
base_path = base_paths[0]
|
|
|
|
for url in urls:
|
|
if not url or not url.strip():
|
|
model_names.append("")
|
|
continue
|
|
|
|
filename = self.extract_filename_from_url(url)
|
|
if not filename:
|
|
model_names.append("")
|
|
continue
|
|
|
|
filepath = os.path.join(base_path, filename)
|
|
|
|
if os.path.exists(filepath):
|
|
logger.info(f"Model {filename} already exists in {base_path}")
|
|
model_names.append(filename)
|
|
continue
|
|
|
|
try:
|
|
self.download_file(url, filepath, proxy, timeout, civitai_api_key if civitai_api_key else None)
|
|
except Exception as e:
|
|
logger.error(f"Error downloading {url} to {filepath}: {e}")
|
|
if os.path.exists(filepath):
|
|
os.remove(filepath)
|
|
raise ValueError(f"Error downloading {url} to {filepath}: {e}")
|
|
|
|
model_names.append(filename)
|
|
|
|
return tuple(model_names)
|
|
|
|
|
|
NODE_CLASS_MAPPINGS = {
|
|
"ModelsDownloaderOfUtils": ModelsDownloaderOfUtils,
|
|
}
|
|
|
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
|
"ModelsDownloaderOfUtils": "Models Downloader",
|
|
} |