cddb07a176
docs / deploy (push) Has been cancelled
docs / changes (push) Has been cancelled
docs / check-and-build (push) Has been cancelled
build container image / cpu (push) Has been cancelled
build container image / cuda (push) Has been cancelled
build container image / rocm (push) Has been cancelled
frontend checks / frontend-checks (push) Has been cancelled
frontend tests / frontend-tests (push) Has been cancelled
lfs checks / lfs-check (push) Has been cancelled
python checks / python-checks (push) Has been cancelled
python tests / py3.12: macos-default (push) Has been cancelled
python tests / py3.11: windows-cpu (push) Has been cancelled
python tests / py3.12: windows-cpu (push) Has been cancelled
python tests / py3.11: linux-cpu (push) Has been cancelled
typegen checks / typegen-checks (push) Has been cancelled
uv lock checks / uv-lock-checks (push) Has been cancelled
openapi checks / openapi-checks (push) Has been cancelled
python tests / py3.11: macos-default (push) Has been cancelled
python tests / py3.12: linux-cpu (push) Has been cancelled
59 lines
2.1 KiB
Python
59 lines
2.1 KiB
Python
from abc import ABC, abstractmethod
|
|
from datetime import datetime
|
|
|
|
from invokeai.app.services.image_records.image_records_common import ImageCategory
|
|
|
|
|
|
class ImageSubfolderStrategy(ABC):
|
|
"""Base class for image subfolder strategies."""
|
|
|
|
@abstractmethod
|
|
def get_subfolder(self, image_name: str, image_category: ImageCategory, is_intermediate: bool) -> str:
|
|
"""Returns relative subfolder prefix (e.g. '2026/03/17', 'general'), or empty string for flat."""
|
|
pass
|
|
|
|
|
|
class FlatStrategy(ImageSubfolderStrategy):
|
|
"""No subfolders - all images in one directory (default behavior)."""
|
|
|
|
def get_subfolder(self, image_name: str, image_category: ImageCategory, is_intermediate: bool) -> str:
|
|
return ""
|
|
|
|
|
|
class DateStrategy(ImageSubfolderStrategy):
|
|
"""Organize images by date: YYYY/MM/DD."""
|
|
|
|
def get_subfolder(self, image_name: str, image_category: ImageCategory, is_intermediate: bool) -> str:
|
|
now = datetime.now()
|
|
return f"{now.year}/{now.month:02d}/{now.day:02d}"
|
|
|
|
|
|
class TypeStrategy(ImageSubfolderStrategy):
|
|
"""Organize images by category/type: general, intermediate, mask, control, etc."""
|
|
|
|
def get_subfolder(self, image_name: str, image_category: ImageCategory, is_intermediate: bool) -> str:
|
|
if is_intermediate:
|
|
return "intermediate"
|
|
return image_category.value
|
|
|
|
|
|
class HashStrategy(ImageSubfolderStrategy):
|
|
"""Organize images by UUID prefix for filesystem performance (first 2 characters)."""
|
|
|
|
def get_subfolder(self, image_name: str, image_category: ImageCategory, is_intermediate: bool) -> str:
|
|
return image_name[:2]
|
|
|
|
|
|
def create_subfolder_strategy(strategy_name: str) -> ImageSubfolderStrategy:
|
|
"""Factory function to create a subfolder strategy by name."""
|
|
strategies: dict[str, type[ImageSubfolderStrategy]] = {
|
|
"flat": FlatStrategy,
|
|
"date": DateStrategy,
|
|
"type": TypeStrategy,
|
|
"hash": HashStrategy,
|
|
}
|
|
cls = strategies.get(strategy_name)
|
|
if cls is None:
|
|
raise ValueError(f"Unknown subfolder strategy: {strategy_name}. Valid options: {', '.join(strategies.keys())}")
|
|
return cls()
|