mirror of
https://github.com/vladmandic/automatic
synced 2026-08-25 22:20:46 +02:00
392c969754
Signed-off-by: Vladimir Mandic <mandic00@live.com>
177 lines
9.0 KiB
Python
177 lines
9.0 KiB
Python
import os
|
|
import time
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
from modules import paths
|
|
from modules.logger import log
|
|
from modules.shared import opts, max_workers
|
|
from modules.modelstats import stat
|
|
|
|
|
|
class Location:
|
|
name: str
|
|
folders: list[str]
|
|
paths: list[Path]
|
|
nfiles: int = 0
|
|
nfolders: int = 0
|
|
nsymlinks: int = 0
|
|
nerrors: int = 0
|
|
type: str = ''
|
|
size: int = 0
|
|
time: float = 0.0
|
|
mtime: datetime = datetime.fromtimestamp(0)
|
|
|
|
def __init__(self, name: str | None, folders: str | list[str], what: str = ''):
|
|
self.type = what
|
|
if isinstance(folders, str):
|
|
folders = [folders]
|
|
self.name = name if name is not None else ', '.join(folders)
|
|
self.folders = folders
|
|
self.paths = [Path(f).resolve(strict=False) for f in self.folders if f is not None and f != '']
|
|
|
|
def __repr__(self):
|
|
return f'Location(type={self.type} name="{self.name}" folders={self.folders} size={self.size/1024/1024:.3f} mtime="{self.mtime}" files={self.nfiles} folders={self.nfolders} symlinks={self.nsymlinks} errors={self.nerrors} time={self.time:.3f})'
|
|
|
|
def dict(self):
|
|
return {
|
|
'name': self.name,
|
|
'type': self.type,
|
|
'folders': self.folders,
|
|
'paths': [str(p) for p in self.paths],
|
|
'size': self.size,
|
|
'mtime': self.mtime.timestamp(),
|
|
'nfiles': self.nfiles,
|
|
'nfolders': self.nfolders,
|
|
'nsymlinks': self.nsymlinks,
|
|
'nerrors': self.nerrors,
|
|
'time': self.time,
|
|
}
|
|
|
|
def get_all_locations(types: list[str] | None = []) -> list[Location]:
|
|
locations = []
|
|
if types is None or 'All' in types or 'Models' in types:
|
|
locations.append(Location('SD Models', opts.ckpt_dir, 'Models'))
|
|
locations.append(Location('Diffusers Models', opts.diffusers_dir, 'Models'))
|
|
locations.append(Location('Huggingface Modules', opts.hfcache_dir, 'Models'))
|
|
locations.append(Location('VAE', [opts.vae_dir, os.path.join(paths.models_path, "TAESD")], 'Models'))
|
|
locations.append(Location('UNet', opts.unet_dir, 'Models'))
|
|
locations.append(Location('TextEncoder', opts.te_dir, 'Models'))
|
|
locations.append(Location('LoRA', opts.lora_dir, 'Models'))
|
|
locations.append(Location('ControlNets', opts.control_dir, 'Models'))
|
|
locations.append(Location('Embeddings', opts.embeddings_dir, 'Models'))
|
|
locations.append(Location('Detailers', [opts.yolo_dir, os.path.join(paths.models_path, 'Ultralytics')], 'Models'))
|
|
locations.append(Location('Upscalers', [opts.esrgan_models_path, opts.bsrgan_models_path, opts.realesrgan_models_path, opts.scunet_models_path, opts.swinir_models_path, os.path.join(paths.models_path, 'chaiNNer'), os.path.join(paths.models_path, 'GFPGAN'), os.path.join(paths.models_path, 'Spandrel'), os.path.join(paths.models_path, 'SeedVR2')], 'Models')) # chainners extension has late opts init
|
|
locations.append(Location('CLiP', opts.clip_models_path, 'Models'))
|
|
locations.append(Location('Rembg', os.path.join(paths.models_path, 'Rembg'), 'Models'))
|
|
locations.append(Location('RIFE', os.path.join(paths.models_path, 'RIFE'), 'Models'))
|
|
if types is None or 'All' in types or 'Data' in types:
|
|
locations.append(Location('Configs', ['data', paths.sd_configs_path], 'Data'))
|
|
locations.append(Location('AutoComplete', opts.autocomplete_dir, 'Data'))
|
|
locations.append(Location('Styles', opts.styles_dir, 'Data'))
|
|
locations.append(Location('Wildcards', opts.wildcards_dir, 'Data'))
|
|
locations.append(Location('Reference', paths.reference_path, 'Data'))
|
|
locations.append(Location('LUTs', os.path.join(paths.models_path, 'LUTs'), 'Data'))
|
|
locations.append(Location('Wiki', 'wiki', 'Data'))
|
|
if types is None or 'All' in types or 'Cache' in types:
|
|
locations.append(Location('Temp', paths.temp_dir, 'Cache'))
|
|
locations.append(Location('XET', opts.xetcache_dir, 'Cache'))
|
|
locations.append(Location('OpenVINO', opts.openvino_cache_path, 'Cache'))
|
|
locations.append(Location('ONNX', opts.onnx_cached_models_path, 'Cache'))
|
|
locations.append(Location('VENV', 'venv', 'Cache'))
|
|
locations.append(Location('Torch', [opts.tunable_dir, os.getenv("TORCHINDUCTOR_CACHE_DIR", None), os.getenv("TRITON_CACHE_DIR", None)], 'Cache'))
|
|
if types is None or 'All' in types or 'Code' in types:
|
|
locations.append(Location('Modules', 'modules', 'Code'))
|
|
locations.append(Location('Pipelines', 'pipelines', 'Code'))
|
|
locations.append(Location('Scripts', 'scripts', 'Code'))
|
|
locations.append(Location('UI', 'ui', 'Code'))
|
|
locations.append(Location('Builtin', paths.extensions_builtin_dir, 'Code'))
|
|
locations.append(Location('Extensions', paths.extensions_dir, 'Code'))
|
|
if types is None or 'All' in types or 'Images' in types:
|
|
locations.append(Location('Text', [opts.outdir_txt2img_samples], 'Images'))
|
|
locations.append(Location('Image', [opts.outdir_img2img_samples], 'Images'))
|
|
locations.append(Location('Control', [opts.outdir_control_samples], 'Images'))
|
|
locations.append(Location('Extras', [opts.outdir_extras_samples], 'Images'))
|
|
locations.append(Location('Save', [opts.outdir_save], 'Images'))
|
|
locations.append(Location('Grids', [opts.outdir_txt2img_grids, opts.outdir_img2img_grids, opts.outdir_control_grids], 'Images'))
|
|
if types is None or 'All' in types or 'Videos' in types:
|
|
locations.append(Location('Video', [opts.outdir_video], 'Videos'))
|
|
return locations
|
|
|
|
|
|
def get_other_locations(locations: list[Location], name: str, folder: str, what: str = 'Other') -> list[Location]:
|
|
# get list of first level subfolders in `folder` check each if its already in `locations` by comparing resolved paths if not, add each to the list as a new Location with type `what`
|
|
existing_paths = set()
|
|
for location in locations:
|
|
for path in location.paths:
|
|
existing_paths.add(path.resolve(strict=False))
|
|
try:
|
|
with os.scandir(folder) as entries:
|
|
for entry in entries:
|
|
if entry.is_dir(follow_symlinks=False):
|
|
path = Path(entry.path).resolve(strict=False)
|
|
if path not in existing_paths:
|
|
locations.append(Location(name, entry.path, what))
|
|
except (FileNotFoundError, PermissionError):
|
|
pass
|
|
return locations
|
|
|
|
|
|
def print_summary(locations: list[Location]):
|
|
summary = {}
|
|
for location in locations:
|
|
if location.type not in summary:
|
|
summary[location.type] = {
|
|
'size': 0,
|
|
'mtime': datetime.fromtimestamp(0),
|
|
'nfiles': 0,
|
|
'nfolders': 0,
|
|
'nerrors': 0,
|
|
}
|
|
summary[location.type]['size'] += location.size
|
|
if location.mtime > summary[location.type]['mtime']:
|
|
summary[location.type]['mtime'] = location.mtime
|
|
summary[location.type]['nfiles'] += location.nfiles
|
|
summary[location.type]['nfolders'] += location.nfolders
|
|
summary[location.type]['nerrors'] += location.nerrors
|
|
for k, v in summary.items():
|
|
log.debug(f'Storage: type={k} size={v["size"]/1024/1024:.3f} files={v["nfiles"]} folders={v["nfolders"]}')
|
|
|
|
|
|
def check_storage(folders: str | list[str] | None = None, types: list[str] | None = None, silent: bool = False) -> list[Location]:
|
|
if isinstance(folders, str):
|
|
folders = [folders]
|
|
if folders is not None and len(folders) > 0:
|
|
locations = [Location(None, folder) for _i, folder in enumerate(folders)]
|
|
else:
|
|
locations = get_all_locations(types)
|
|
if types is None or 'Other' in types or 'All' in types:
|
|
locations = get_other_locations(locations, 'Other', paths.models_path)
|
|
log.debug(f'Storage: locations={len(locations)} workers={max_workers} types={types} folders={folders} start')
|
|
|
|
def update_stats(location: Location) -> Location:
|
|
t0 = time.time()
|
|
for f in location.paths:
|
|
size, mtime, files, folders, symlinks, errors = stat(f, extended=True, exclude=['__pycache__', '.'])
|
|
location.size += size
|
|
if mtime > location.mtime:
|
|
location.mtime = mtime
|
|
location.nfiles += files
|
|
location.nfolders += folders
|
|
location.nsymlinks += symlinks
|
|
location.nerrors += errors
|
|
location.time = time.time() - t0
|
|
return location
|
|
|
|
t0 = time.time()
|
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
|
future_items = {executor.submit(update_stats, location): location for location in locations}
|
|
for future in as_completed(future_items):
|
|
location = future.result()
|
|
if location.size > 0 and not silent:
|
|
log.debug(location)
|
|
t1 = time.time()
|
|
print_summary(locations)
|
|
log.debug(f'Storage: time={t1-t0:.3f} end')
|
|
return locations
|