Skip to content

Commit 3537e39

Browse files
committed
feat: add text asset
1 parent 2672b72 commit 3537e39

File tree

1 file changed

+222
-1
lines changed

1 file changed

+222
-1
lines changed

videodb/timeline_v2.py

Lines changed: 222 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ class AssetType(str, Enum):
66
video = "video"
77
image = "image"
88
audio = "audio"
9+
text = "text"
910

1011

1112
class Fit(str, Enum):
@@ -39,6 +40,36 @@ class Filter(str, Enum):
3940
negative = "negative"
4041

4142

43+
class TextAlignment(str, Enum):
44+
"""Place the text in one of nine predefined positions of the background."""
45+
46+
top = "top"
47+
top_right = "top_right"
48+
right = "right"
49+
bottom_right = "bottom_right"
50+
bottom = "bottom"
51+
bottom_left = "bottom_left"
52+
left = "left"
53+
top_left = "top_left"
54+
center = "center"
55+
56+
57+
class HorizontalAlignment(str, Enum):
58+
"""Horizontal text alignment options."""
59+
60+
left = "left"
61+
center = "center"
62+
right = "right"
63+
64+
65+
class VerticalAlignment(str, Enum):
66+
"""Vertical text alignment options."""
67+
68+
top = "top"
69+
center = "center"
70+
bottom = "bottom"
71+
72+
4273
class Offset:
4374
def __init__(self, x: float = 0, y: float = 0):
4475
self.x = x
@@ -157,7 +188,197 @@ def to_json(self):
157188
}
158189

159190

160-
AnyAsset = Union[VideoAsset, ImageAsset, AudioAsset]
191+
class Font:
192+
"""Font styling properties for text assets."""
193+
194+
def __init__(
195+
self,
196+
family: str = "Clear Sans",
197+
size: int = 48,
198+
color: str = "#FFFFFF",
199+
opacity: float = 1.0,
200+
weight: Optional[int] = None,
201+
):
202+
if size < 1:
203+
raise ValueError("size must be at least 1")
204+
if not (0.0 <= opacity <= 1.0):
205+
raise ValueError("opacity must be between 0.0 and 1.0")
206+
if weight is not None and not (100 <= weight <= 900):
207+
raise ValueError("weight must be between 100 and 900")
208+
209+
self.family = family
210+
self.size = size
211+
self.color = color
212+
self.opacity = opacity
213+
self.weight = weight
214+
215+
def to_json(self):
216+
data = {
217+
"family": self.family,
218+
"size": self.size,
219+
"color": self.color,
220+
"opacity": self.opacity,
221+
}
222+
if self.weight is not None:
223+
data["weight"] = self.weight
224+
return data
225+
226+
227+
class Border:
228+
"""Text border properties."""
229+
230+
def __init__(self, color: str = "#000000", width: float = 0.0):
231+
if width < 0.0:
232+
raise ValueError("width must be non-negative")
233+
self.color = color
234+
self.width = width
235+
236+
def to_json(self):
237+
return {
238+
"color": self.color,
239+
"width": self.width,
240+
}
241+
242+
243+
class Shadow:
244+
"""Text shadow properties."""
245+
246+
def __init__(self, color: str = "#000000", x: float = 0.0, y: float = 0.0):
247+
if x < 0.0:
248+
raise ValueError("x must be non-negative")
249+
if y < 0.0:
250+
raise ValueError("y must be non-negative")
251+
self.color = color
252+
self.x = x
253+
self.y = y
254+
255+
def to_json(self):
256+
return {
257+
"color": self.color,
258+
"x": self.x,
259+
"y": self.y,
260+
}
261+
262+
263+
class Background:
264+
"""Text background styling properties."""
265+
266+
def __init__(
267+
self,
268+
width: float = 0.0,
269+
height: float = 0.0,
270+
color: str = "#000000",
271+
border_width: float = 0.0,
272+
opacity: float = 1.0,
273+
text_alignment: TextAlignment = TextAlignment.center,
274+
):
275+
if width < 0.0:
276+
raise ValueError("width must be non-negative")
277+
if height < 0.0:
278+
raise ValueError("height must be non-negative")
279+
if border_width < 0.0:
280+
raise ValueError("border_width must be non-negative")
281+
if not (0.0 <= opacity <= 1.0):
282+
raise ValueError("opacity must be between 0.0 and 1.0")
283+
284+
self.width = width
285+
self.height = height
286+
self.color = color
287+
self.border_width = border_width
288+
self.opacity = opacity
289+
self.text_alignment = text_alignment
290+
291+
def to_json(self):
292+
return {
293+
"width": self.width,
294+
"height": self.height,
295+
"color": self.color,
296+
"border_width": self.border_width,
297+
"opacity": self.opacity,
298+
"text_alignment": self.text_alignment.value,
299+
}
300+
301+
302+
class Alignment:
303+
"""Text alignment properties."""
304+
305+
def __init__(
306+
self,
307+
horizontal: HorizontalAlignment = HorizontalAlignment.center,
308+
vertical: VerticalAlignment = VerticalAlignment.center,
309+
):
310+
self.horizontal = horizontal
311+
self.vertical = vertical
312+
313+
def to_json(self):
314+
return {
315+
"horizontal": self.horizontal.value,
316+
"vertical": self.vertical.value,
317+
}
318+
319+
320+
class TextAsset(BaseAsset):
321+
"""The TextAsset is used to create text sequences from text strings with full control over the text styling and positioning."""
322+
323+
type = AssetType.text
324+
325+
def __init__(
326+
self,
327+
text: str,
328+
font: Optional[Font] = None,
329+
border: Optional[Border] = None,
330+
shadow: Optional[Shadow] = None,
331+
background: Optional[Background] = None,
332+
alignment: Optional[Alignment] = None,
333+
tabsize: int = 4,
334+
line_spacing: float = 0,
335+
width: Optional[int] = None,
336+
height: Optional[int] = None,
337+
):
338+
if tabsize < 1:
339+
raise ValueError("tabsize must be at least 1")
340+
if line_spacing < 0.0:
341+
raise ValueError("line_spacing must be non-negative")
342+
if width is not None and width < 1:
343+
raise ValueError("width must be at least 1")
344+
if height is not None and height < 1:
345+
raise ValueError("height must be at least 1")
346+
347+
self.text = text
348+
self.font = font if font is not None else Font()
349+
self.border = border
350+
self.shadow = shadow
351+
self.background = background
352+
self.alignment = alignment if alignment is not None else Alignment()
353+
self.tabsize = tabsize
354+
self.line_spacing = line_spacing
355+
self.width = width
356+
self.height = height
357+
358+
def to_json(self):
359+
data = {
360+
"type": self.type,
361+
"text": self.text,
362+
"font": self.font.to_json(),
363+
"alignment": self.alignment.to_json(),
364+
"tabsize": self.tabsize,
365+
"line_spacing": self.line_spacing,
366+
}
367+
if self.border:
368+
data["border"] = self.border.to_json()
369+
if self.shadow:
370+
data["shadow"] = self.shadow.to_json()
371+
if self.background:
372+
data["background"] = self.background.to_json()
373+
if self.width is not None:
374+
data["width"] = self.width
375+
if self.height is not None:
376+
data["height"] = self.height
377+
378+
return data
379+
380+
381+
AnyAsset = Union[VideoAsset, ImageAsset, AudioAsset, TextAsset]
161382

162383

163384
class Clip:

0 commit comments

Comments
 (0)