feat: add resolve_keyframe helper to extract sorted-keyframe lookup
Adds a pure function that returns the latest keyframe at or before a given time (with tolerance), replacing the inline lookup pattern that appears multiple times in main.py. Includes 6 tests covering empty list, before-first, exact match, between, after-last, and tolerance. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -53,6 +53,21 @@ def format_time(seconds: float) -> str:
|
|||||||
return f"{m}:{s:04.1f}"
|
return f"{m}:{s:04.1f}"
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_keyframe(
|
||||||
|
keyframes: list[tuple[float, float, str | None, bool, bool]],
|
||||||
|
t: float,
|
||||||
|
tolerance: float = 0.05,
|
||||||
|
) -> tuple[float, float, str | None, bool, bool] | None:
|
||||||
|
"""Return the latest keyframe at or before *t*, or None."""
|
||||||
|
result = None
|
||||||
|
for kf in keyframes:
|
||||||
|
if kf[0] <= t + tolerance:
|
||||||
|
result = kf
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
def build_ffmpeg_command(
|
def build_ffmpeg_command(
|
||||||
input_path: str, start: float, output_path: str,
|
input_path: str, start: float, output_path: str,
|
||||||
short_side: int | None = None,
|
short_side: int | None = None,
|
||||||
|
|||||||
+33
-1
@@ -1,5 +1,5 @@
|
|||||||
import tempfile, os, json
|
import tempfile, os, json
|
||||||
from main import build_export_path, format_time, build_ffmpeg_command, build_sequence_dir, build_audio_extract_command, build_annotation_json_path, upsert_clip_annotation
|
from main import build_export_path, format_time, resolve_keyframe, build_ffmpeg_command, build_sequence_dir, build_audio_extract_command, build_annotation_json_path, upsert_clip_annotation
|
||||||
from main import ProcessedDB
|
from main import ProcessedDB
|
||||||
|
|
||||||
|
|
||||||
@@ -369,3 +369,35 @@ def test_db_default_profile_backward_compat():
|
|||||||
assert db.get_profiles() == ["default"]
|
assert db.get_profiles() == ["default"]
|
||||||
finally:
|
finally:
|
||||||
os.unlink(path)
|
os.unlink(path)
|
||||||
|
|
||||||
|
|
||||||
|
# --- resolve_keyframe ---
|
||||||
|
|
||||||
|
def test_resolve_keyframe_empty():
|
||||||
|
assert resolve_keyframe([], 5.0) is None
|
||||||
|
|
||||||
|
def test_resolve_keyframe_before_first():
|
||||||
|
kfs = [(3.0, 0.5, None, False, False)]
|
||||||
|
assert resolve_keyframe(kfs, 1.0) is None
|
||||||
|
|
||||||
|
def test_resolve_keyframe_exact():
|
||||||
|
kfs = [(2.0, 0.3, "9:16", True, False)]
|
||||||
|
assert resolve_keyframe(kfs, 2.0) == (2.0, 0.3, "9:16", True, False)
|
||||||
|
|
||||||
|
def test_resolve_keyframe_between():
|
||||||
|
kfs = [
|
||||||
|
(1.0, 0.2, None, False, False),
|
||||||
|
(5.0, 0.8, "1:1", False, True),
|
||||||
|
]
|
||||||
|
assert resolve_keyframe(kfs, 3.0) == (1.0, 0.2, None, False, False)
|
||||||
|
|
||||||
|
def test_resolve_keyframe_after_last():
|
||||||
|
kfs = [
|
||||||
|
(1.0, 0.2, None, False, False),
|
||||||
|
(5.0, 0.8, "1:1", False, True),
|
||||||
|
]
|
||||||
|
assert resolve_keyframe(kfs, 10.0) == (5.0, 0.8, "1:1", False, True)
|
||||||
|
|
||||||
|
def test_resolve_keyframe_tolerance():
|
||||||
|
kfs = [(4.0, 0.5, None, True, True)]
|
||||||
|
assert resolve_keyframe(kfs, 3.96) == (4.0, 0.5, None, True, True)
|
||||||
|
|||||||
Reference in New Issue
Block a user