4747class Node :
4848 def __init__ (self , data : NodeDataT ) -> None :
4949 self ._raw = data
50- self ._type : Literal ["text" , "image" , "audio" , "html" ] = data .get ("type" , "text" )
50+ self ._type : Literal ["text" , "image" , "audio" , "html" , "stylesheet" ] = data .get ("type" , "text" )
5151
5252 element = data .get ("element" )
5353 self ._element = HTMLElement (element ) if element else None
@@ -57,7 +57,7 @@ def __init__(self, data: NodeDataT) -> None:
5757 self ._location = data .get ("location" )
5858
5959 @property
60- def type (self ) -> Literal ["text" , "image" , "audio" , "html" ]:
60+ def type (self ) -> Literal ["text" , "image" , "audio" , "html" , "stylesheet" ]:
6161 return self ._type
6262
6363 @property
@@ -76,13 +76,18 @@ def html_class(self) -> str | None:
7676 def location (self ) -> str | None :
7777 return self ._location
7878
79+ @property
80+ def raw (self ) -> str | None :
81+ return self ._raw .get ("raw" )
82+
7983
8084class Overlay :
8185 VERSION : ClassVar [int ] = 0
8286
8387 def __init__ (self , data : OverlayDataT | None = None , * , duration : int = 5000 ) -> None :
8488 self ._nodes : list [Node ] = []
8589 self ._audio : Node | None = None
90+ self ._stylesheet : Node | None = None
8691
8792 data = data or {}
8893 nodes = data .get ("nodes" , [])
@@ -93,6 +98,22 @@ def __init__(self, data: OverlayDataT | None = None, *, duration: int = 5000) ->
9398
9499 self ._duration = data .get ("delay" ) or duration
95100
101+ @property
102+ def nodes (self ) -> list [Node ]:
103+ return self ._nodes .copy ()
104+
105+ @property
106+ def audio (self ) -> Node | None :
107+ return self ._audio
108+
109+ @property
110+ def stylesheet (self ) -> Node | None :
111+ return self ._stylesheet
112+
113+ @property
114+ def duration (self ) -> int :
115+ return self ._duration
116+
96117 def add_text (
97118 self ,
98119 text : str ,
@@ -120,26 +141,74 @@ def add_text(
120141
121142 return self
122143
123- def add_image (self ) -> ...: ...
144+ def add_image (
145+ self ,
146+ file : str | os .PathLike [str ],
147+ * ,
148+ html_class : str | None = None ,
149+ html_id : str | None = None ,
150+ ) -> Self :
151+ data : NodeDataT = {}
124152
125- def add_html (self ) -> ...: ...
153+ uri : str = f"media/{ file } " if not str (file ).startswith (("http://" , "https://" )) else str (file )
154+ data ["type" ] = "image"
155+ data ["location" ] = str (file )
156+ data ["html_class" ] = html_class
157+ data ["html_id" ] = html_id
158+
159+ id_ = f'id="{ html_id } " ' if html_id else ""
160+ class_ = f'class="{ html_class } " ' if html_class else ""
161+ data ["raw" ] = f"<img { id_ } { class_ } src='{ html .escape (uri )} '></img>"
126162
127- def set_audio (self , file : str | os .PathLike [str ]) -> Self :
163+ node = Node (data )
164+ self ._nodes .append (node )
165+
166+ return self
167+
168+ def add_html (self , html : str ) -> Self :
169+ data : NodeDataT = {}
170+
171+ data ["type" ] = "html"
172+ data ["raw" ] = html
173+ node = Node (data )
174+
175+ self ._nodes .append (node )
176+ return self
177+
178+ def set_audio (self , file : str | os .PathLike [str ], * , duration : int | None = None , loop : bool = False ) -> Self :
128179 data : NodeDataT = {}
129180 identifier : str = secrets .token_urlsafe (4 )
130181
131- uri = f"media/{ file } " if not str (file ).startswith (("http://" , "https://" )) else file
182+ uri : str = f"media/{ file } " if not str (file ).startswith (("http://" , "https://" )) else str (file )
183+ loop_ = "loop" if loop else ""
184+
132185 data ["type" ] = "audio"
133186 data ["location" ] = str (file )
134187 data ["html_id" ] = identifier
135- data ["raw" ] = f"<audio id='{ identifier } '><source src='{ uri } '></audio>"
188+ data ["raw" ] = f"<audio id='{ identifier } ' { loop_ } ><source src='{ html .escape (uri )} '></audio>"
189+
190+ if duration :
191+ self .set_duration (duration )
136192
137193 node = Node (data )
138194 self ._audio = node
139195
140196 return self
141197
142- def set_stylesheet (self ) -> ...: ...
198+ def set_stylesheet (self , file : str | os .PathLike [str ]) -> Self :
199+ data : NodeDataT = {}
200+ identifier : str = secrets .token_urlsafe (4 )
201+
202+ uri : str = f"media/{ file } " if not str (file ).startswith (("http://" , "https://" )) else str (file )
203+ data ["type" ] = "stylesheet"
204+ data ["location" ] = str (file )
205+ data ["html_id" ] = identifier
206+ data ["raw" ] = f"<link id='{ identifier } ' rel='stylesheet' href='{ html .escape (uri )} '>"
207+
208+ node = Node (data )
209+ self ._stylesheet = node
210+
211+ return self
143212
144213 def set_duration (self , value : int , / ) -> Self :
145214 self ._duration = value
@@ -153,7 +222,10 @@ def build(self) -> OverlayDataT:
153222 nodes .append (node ._raw )
154223
155224 if self ._audio :
156- nodes .append (self ._audio ._raw )
225+ nodes .insert (0 , self ._audio ._raw )
226+
227+ if self ._stylesheet :
228+ nodes .insert (0 , self ._stylesheet ._raw )
157229
158230 return data
159231
0 commit comments