Coverage for apps/scene.py: 100%
43 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-09 04:47 +0000
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-09 04:47 +0000
1"""
2Scene information for video generation apps.
3TODO consolidate info and segment.
4TODO make it JSON encode/decode friendly for saving/loading from disk or database
5"""
7from typing import List
8from typing import Optional
10from dataclasses import dataclass
11from dataclasses import field
14def format_time(seconds: float) -> str:
15 """Format seconds as HH:MM:SS."""
16 minutes, secs = divmod(int(seconds), 60)
17 hours, minutes = divmod(minutes, 60)
18 return f"{hours:02d}:{minutes:02d}:{secs:02d}"
21@dataclass
22class SceneSegment:
23 """A scene segment in a video."""
24 scene_id: int
25 start_frame: int
26 end_frame: int
27 start_sec: float
28 end_sec: float
30 frame_image_paths: List[str] = field(default_factory=list)
31 descriptions: List[str] = field(default_factory=list)
33 audio_path: Optional[str] = None
34 transcript: Optional[str] = None
35 language: Optional[str] = None
36 translation: Optional[str] = None
38 def add_image_path(
39 self,
40 image_path: str
41 ) -> None:
42 if image_path:
43 self.frame_image_paths.append(image_path)
45 def add_description(
46 self,
47 description: str
48 ) -> None:
49 if description:
50 self.descriptions.append(description)
52 def get_start(self) -> str:
53 return format_time(self.start_sec)
55 def get_end(self) -> str:
56 return format_time(self.end_sec)
58 @property
59 def duration_sec(self) -> float:
60 return self.end_sec - self.start_sec
62 def __str__(self) -> str:
63 ret = f"[{self.start_frame:4d}-{self.end_frame:4d}, {self.start_sec:4.1f}-{self.end_sec:4.1f}]"
64 if self.transcript:
65 ret += f": {self.transcript[0:60]}..."
66 for description in self.descriptions:
67 ret += f" | {description[0:60]}..."
68 if self.frame_image_paths:
69 ret += f" | {len(self.frame_image_paths)} images"
70 return ret