From 02fd0f0919f829f27e4d5a9328922a34d5e10d3f Mon Sep 17 00:00:00 2001 From: Ethanfel Date: Thu, 18 Jun 2026 14:58:44 +0200 Subject: [PATCH] feat: LTX-2 legal-frame helpers (core/ltx2.py) Co-Authored-By: Claude Fable 5 --- core/ltx2.py | 26 ++++++++++++++++++++++++++ tests/test_utils.py | 23 +++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 core/ltx2.py diff --git a/core/ltx2.py b/core/ltx2.py new file mode 100644 index 0000000..543c027 --- /dev/null +++ b/core/ltx2.py @@ -0,0 +1,26 @@ +"""LTX-2 frame-count math. Legal F satisfy F % 8 == 1 (8x temporal + 1).""" + + +def is_legal_frames(f: int) -> bool: + return f >= 9 and f % 8 == 1 + + +def legal_frames(min_f: int = 9, max_f: int = 1000) -> list[int]: + start = max(9, min_f + ((1 - min_f) % 8)) # first 8k+1 >= min_f + return list(range(start, max_f + 1, 8)) + + +def nearest_legal_frames(f: int) -> int: + if f <= 9: + return 9 + low = ((f - 1) // 8) * 8 + 1 + high = low + 8 + return low if (f - low) <= (high - f) else high + + +def duration_for_frames(frames: int, fps: float) -> float: + return frames / fps + + +def frames_for_duration(duration: float, fps: float) -> int: + return nearest_legal_frames(round(duration * fps)) diff --git a/tests/test_utils.py b/tests/test_utils.py index f0a9ac0..35f48a8 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -439,3 +439,26 @@ def test_apply_keyframes_before_first_uses_base(): result = apply_keyframes_to_jobs(jobs, kfs, base_center=0.5, base_ratio="4:5", base_rand_p=True, base_rand_s=False) assert result == [(1.0, "/out/a", "4:5", 0.5, True, False)] + + +# --- LTX-2 legal-frame math (core/ltx2.py) --- + +from core.ltx2 import is_legal_frames, nearest_legal_frames, frames_for_duration, duration_for_frames, legal_frames + +def test_ltx2_is_legal(): + assert is_legal_frames(201) and is_legal_frames(9) and is_legal_frames(25) + assert not is_legal_frames(200) and not is_legal_frames(8) + +def test_ltx2_nearest(): + assert nearest_legal_frames(200) == 201 # 200 -> nearest 8k+1 + assert nearest_legal_frames(196) == 193 + assert nearest_legal_frames(5) == 9 # floor at 9 + +def test_ltx2_duration_roundtrip(): + assert duration_for_frames(201, 25) == 201 / 25 + assert frames_for_duration(8.0, 25) == 201 # 200 -> 201 + +def test_ltx2_legal_series(): + s = legal_frames(min_f=9, max_f=33) + assert s == [9, 17, 25, 33] +