wan-loop-generator / core_anim /space_config.py
andrasnest's picture
Wan Loop Generator
7d03019
Raw
History Blame Contribute Delete
3.3 kB
from __future__ import annotations
import configparser
from dataclasses import dataclass
import os
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
def _bool(value: str) -> bool:
return value.strip().lower() in {"1", "true", "yes", "on"}
@dataclass(frozen=True, slots=True)
class SpaceConfig:
width: int
height: int
seconds: float
fps: int
steps: int
split_step: int
cfg: float
shift: float
sampler: str
scheduler: str
start_strength: float
end_strength: float
end_mask_strength: float
use_rife: bool
rife_frames: int
use_crossfade: bool
text_encoder_quantization: str
global_resident_models: bool
vae_tiling: bool
gpu_size: str
gpu_duration: int
concurrency_limit: int
@property
def frame_count(self) -> int:
requested = max(int(round(self.seconds * self.fps)), 1)
return requested + ((4 - ((requested - 1) % 4)) % 4)
def load_space_config(path: Path | None = None) -> SpaceConfig:
parser = configparser.ConfigParser()
config_path = path or ROOT / "config.ini"
if not parser.read(config_path):
raise FileNotFoundError(f"Configuration not found: {config_path}")
section = parser["SPACE"]
gpu_size = os.environ.get("ZERO_GPU_SIZE", section.get("zero_gpu_size", "large")).strip().lower()
if gpu_size not in {"large", "xlarge"}:
raise ValueError("ZERO_GPU_SIZE must be 'large' or 'xlarge'.")
width, height = section.getint("width"), section.getint("height")
if width % 16 or height % 16:
raise ValueError("width and height must be divisible by 16.")
steps, split = section.getint("steps"), section.getint("split_step")
if not 0 < split < steps:
raise ValueError("split_step must be between 1 and steps-1.")
text_encoder_quantization = section.get("text_encoder_quantization", "int8_weight_only").strip().lower()
if text_encoder_quantization not in {"int8_weight_only", "bf16"}:
raise ValueError("text_encoder_quantization must be 'int8_weight_only' or 'bf16'.")
return SpaceConfig(
width=width,
height=height,
seconds=section.getfloat("seconds"),
fps=section.getint("fps"),
steps=steps,
split_step=split,
cfg=section.getfloat("cfgscale"),
shift=section.getfloat("model_sampling_shift"),
sampler=section.get("sampler_name", "euler"),
scheduler=section.get("scheduler", "normal"),
start_strength=section.getfloat("start_latent_strength"),
end_strength=section.getfloat("end_latent_strength"),
end_mask_strength=section.getfloat("end_temporal_mask_strength"),
use_rife=_bool(section.get("apply_rife_seam", "true")),
rife_frames=section.getint("rife_seam_frames"),
use_crossfade=_bool(section.get("apply_crossfade", "true")),
text_encoder_quantization=text_encoder_quantization,
global_resident_models=_bool(section.get("global_resident_models", "false")),
vae_tiling=_bool(section.get("vae_tiling", "false")),
gpu_size=gpu_size,
gpu_duration=int(os.environ.get("ZERO_GPU_DURATION", section.get("zero_gpu_duration", "300"))),
concurrency_limit=section.getint("concurrency_limit", 1),
)