CoolFace
Apppublic

cs686/ardy-motion-api

sourceHugging Faceupdated 2mo agoView on Hugging Face
6likes
timeline_utils.py83 linesDownload Raw Back to root
1"""Validation helpers for ARDY's continuous action timeline."""2 3from __future__ import annotations4 5import math6from typing import Any7 8 9MIN_SEGMENT_SECONDS = 2.010MAX_SEGMENT_SECONDS = 4.011MAX_TOTAL_SECONDS = 8.012MAX_SEGMENTS = 813MAX_PROMPT_CHARS = 50014 15 16def _rows(value: Any) -> list:17    if value is None:18        return []19    if hasattr(value, "values"):20        return value.values.tolist()21    if hasattr(value, "tolist") and not isinstance(value, list):22        return value.tolist()23    return list(value)24 25 26def normalize_timeline(value: Any, *, fps: float) -> list[dict]:27    """Normalize Gradio table rows and calculate exact frame ranges."""28    if not math.isfinite(float(fps)) or float(fps) <= 0:29        raise ValueError("FPS must be greater than zero.")30 31    clean_rows = []32    for row in _rows(value):33        if row is None:34            continue35        row = list(row)36        prompt = " ".join(str(row[0] if row else "").split())37        duration_value = row[1] if len(row) > 1 else None38        if not prompt and duration_value in (None, ""):39            continue40        if not prompt:41            raise ValueError("Every timeline row must contain an action prompt.")42        if len(prompt) > MAX_PROMPT_CHARS:43            raise ValueError(44                f"Each action prompt must contain at most {MAX_PROMPT_CHARS} characters."45            )46        try:47            duration = float(duration_value)48        except (TypeError, ValueError) as exc:49            raise ValueError(50                f"Duration for '{prompt}' must be a number."51            ) from exc52        if not math.isfinite(duration) or not MIN_SEGMENT_SECONDS <= duration <= MAX_SEGMENT_SECONDS:53            raise ValueError(54                f"Each segment must be between {MIN_SEGMENT_SECONDS:g} "55                f"and {MAX_SEGMENT_SECONDS:g} seconds."56            )57        clean_rows.append((prompt, duration))58 59    if not clean_rows:60        raise ValueError("Add at least one action to the timeline.")61    if len(clean_rows) > MAX_SEGMENTS:62        raise ValueError(f"The timeline supports at most {MAX_SEGMENTS} segments.")63    if sum(duration for _, duration in clean_rows) > MAX_TOTAL_SECONDS + 1e-6:64        raise ValueError(65            f"The combined timeline may not exceed {MAX_TOTAL_SECONDS:g} seconds."66        )67 68    segments = []69    start_frame = 070    for prompt, duration in clean_rows:71        frame_count = max(1, int(round(duration * float(fps))))72        end_frame = start_frame + frame_count73        segments.append(74            {75                "prompt": prompt,76                "duration_seconds": frame_count / float(fps),77                "start_frame": start_frame,78                "end_frame": end_frame,79            }80        )81        start_frame = end_frame82    return segments83