| from __future__ import annotations |
|
|
| from collections.abc import Mapping, Sequence |
|
|
|
|
| def detect_zerogpu_env( |
| environ: Mapping[str, str], |
| *, |
| bool_keys: Sequence[str], |
| text_keys: Sequence[str], |
| true_values: set[str], |
| ) -> bool: |
| if any(str(environ.get(key, "")).strip().lower() in true_values for key in bool_keys): |
| return True |
| markers = " ".join(str(environ.get(key, "")) for key in text_keys).strip().lower() |
| return "zerogpu" in markers or ("zero" in markers and "gpu" in markers) |
|
|
|
|
| def parse_resolution_value(value: str, *, resolutions: Mapping[str, tuple[int, int]]) -> tuple[int, int]: |
| text = str(value or "").strip() |
| if text in resolutions: |
| return resolutions[text] |
| head = text.split("·", 1)[0].strip().lower().replace("×", "x") |
| parts = [part.strip() for part in head.split("x")] |
| if len(parts) != 2: |
| raise ValueError("resolution must look like WIDTH × HEIGHT") |
| width, height = int(parts[0]), int(parts[1]) |
| if width < 64 or height < 64 or width % 64 != 0 or height % 64 != 0: |
| raise ValueError("width and height must be positive multiples of 64") |
| return width, height |
|
|
|
|
| def safe_config_float( |
| name: str, |
| value, |
| default: float, |
| minimum: float, |
| maximum: float, |
| *, |
| warnings: list[str], |
| ) -> float: |
| try: |
| result = float(value) |
| except Exception: |
| warnings.append(f"Invalid {name}={value!r}; using {default}.") |
| return default |
| if result < minimum or result > maximum: |
| clamped = min(maximum, max(minimum, result)) |
| warnings.append(f"Out-of-range {name}={result}; clamped to {clamped}.") |
| return clamped |
| return result |
|
|
|
|
| def safe_config_int(name: str, value, default: int, *, warnings: list[str]) -> int: |
| try: |
| return int(value) |
| except Exception: |
| warnings.append(f"Invalid {name}={value!r}; using {default}.") |
| return default |
|
|
|
|
| def safe_config_bool(value, default: bool, *, warnings: list[str], true_values: set[str]) -> bool: |
| if isinstance(value, bool): |
| return value |
| if isinstance(value, str): |
| normalized = value.strip().lower() |
| if normalized in true_values: |
| return True |
| if normalized in {"0", "false", "f", "no", "n", "off"}: |
| return False |
| if isinstance(value, (int, float)): |
| return bool(value) |
| warnings.append(f"Invalid boolean default {value!r}; using {default}.") |
| return default |
|
|
|
|
| def frames_from_seconds( |
| seconds: float, |
| *, |
| frame_rate: float, |
| experimental_max_seconds: float, |
| max_frames: int, |
| ) -> int: |
| seconds = min(experimental_max_seconds, max(1.0, float(seconds))) |
| raw = round(seconds * frame_rate) |
| frames = round((raw - 1) / 8) * 8 + 1 |
| return min(max_frames, max(25, int(frames))) |
|
|
|
|
| def supports_experimental_long( |
| resolution_key: str, |
| *, |
| resolutions: Mapping[str, tuple[int, int]], |
| ) -> bool: |
| try: |
| return parse_resolution_value(resolution_key, resolutions=resolutions) == (512, 512) |
| except Exception: |
| return False |
|
|