3333import zlib
3434from typing import TYPE_CHECKING , Any , ClassVar , Literal , Self , overload
3535
36+ from twitchio import Colour
37+
3638from .enums import Animation , AnimationSpeed , EventPosition , Font
37- from .exceptions import BlueprintError
39+ from .exceptions import AudioAlreadyLoadedError , BlueprintError
3840
3941
4042if TYPE_CHECKING :
@@ -55,16 +57,65 @@ def __init__(self) -> None:
5557 self ._raw : OverlayEventT = {
5658 "parts" : [],
5759 "audio" : "" ,
58- "duration" : 800000 ,
60+ "duration" : 5000 ,
5961 "duration_is_audio" : False ,
6062 "force_override" : False ,
61- "stack_event" : True ,
63+ "stack_event" : False ,
6264 }
6365
6466 def escape (self , value : str , / ) -> str :
67+ """Method which escapes the provided the content via the ``html`` standard library.
68+
69+ Parameters
70+ ----------
71+ value: str
72+ The content to escape.
73+
74+ Returns
75+ -------
76+ str
77+ The escaped content.
78+ """
6579 return html .escape (value )
6680
67- def add_html (self ) -> Self : ...
81+ def add_html (
82+ self ,
83+ html : str ,
84+ animation : Animation | None = None ,
85+ animation_speed : AnimationSpeed | None = None ,
86+ ) -> Self :
87+ """Method to add a custom HTML segment to this event.
88+
89+ .. important::
90+
91+ This method is unsafe and care should be taken to ensure that the input is trusted. The input provided to this
92+ method will **NOT** be escaped and could be used to inject scripts into the Javascript of the overlay.
93+
94+ .. warning::
95+
96+ We do not recommend using this method if any of the input comes from a third-party or external source; including
97+ chatters or other API's.
98+
99+ Parameters
100+ ----------
101+ html: str
102+ The ``HTML`` to use as a segment for this event. Ensure the content provided to this parameter is trusted.
103+ animation: :class:`~twitchio.ext.overlays.Animation` | ``None``
104+ An optional animation to apply to the provided HTML content. Defaults to ``None``.
105+ animation_speed: :class:`~twitchio.ext.overlays.AnimationSpeed` | ``None``
106+ An optional speed of animation to provide. Defaults to ``None`` which is the default speed.
107+
108+ Returns
109+ -------
110+ OverlayEvent
111+ This method returns ``self`` which allows for fluid-style chaining.
112+ """
113+ middle = f" animate__{ animation .value } " if animation else ""
114+ speed = f" animate__{ animation_speed .value } " if animation_speed else ""
115+ part : OverlayPartT = {"content" : html , "animation" : middle , "speed" : speed }
116+
117+ self ._raw ["parts" ].append (part )
118+ return self
68119
69120 def add_text (
70121 self ,
@@ -74,28 +125,76 @@ def add_text(
74125 animation_speed : AnimationSpeed | None = None ,
75126 font : Font | None = None , # TODO: Default Font...
76127 size : int = 24 ,
128+ colour : Colour | None = None ,
77129 ) -> Self :
78- # TODO: Font...
130+ """Method to add custom text content as a segement to this event.
131+
132+ .. note::
133+
134+ The text content provided to this method is escaped via :meth:`.escape`.
135+
136+ Parameters
137+ ----------
138+ content: str
139+ The text content to use as a segment for this event. This content will be escaped.
140+ animation: :class:`~twitchio.ext.overlays.Animation` | ``None``
141+ An optional animation to apply to the provided text content. Defaults to ``None``.
142+ animation_speed: :class:`~twitchio.ext.overlays.AnimationSpeed` | ``None``
143+ An optional speed of animation to provide. Defaults to ``None`` which is the default speed.
144+ font: :class:`~twitchio.ext.overlays.Font` | ``None``
145+ ...
146+ size: int
147+ An optional :class:`int` which is the size of the font in `px`. Defaults to ``24``.
148+ colour: :class:`twitchio.Colour`
149+ An optional :class:`twitchio.Colour` or :class:`twitchio.Color` to use for the text. Defaults to ``None``, which
150+ will be ``black`` / ``#000000``.
151+
152+ Returns
153+ -------
154+ OverlayEvent
155+ This method returns ``self`` which allows for fluid-style chaining.
156+ """
79157
80158 if not isinstance (size , int ): # pyright: ignore[reportUnnecessaryIsInstance]
81159 raise TypeError (f"Parameter 'size' expected 'int' got { type (size )!r} ." )
82160
83161 escaped = self .escape (content )
84162 middle = f" animate__{ animation .value } " if animation else ""
85163 speed = f" animate__{ animation_speed .value } " if animation_speed else ""
164+ col = colour or Colour .from_hex ("#000000" )
86165
87- part : OverlayPartT = {"content" : escaped , "animation" : middle , "speed" : speed , "size" : size }
166+ part : OverlayPartT = {"content" : escaped , "animation" : middle , "speed" : speed , "size" : size , "colour" : col . html }
88167 self ._raw ["parts" ].append (part )
89168
90169 return self
91170
92171 def add_image (self ) -> Self : ...
93172
94- def set_audio (self ) -> Self : ...
173+ def set_audio (self , name : str , / ) -> Self : ...
174+
175+ def set_duration (self , value : int | None = 5000 , * , as_audio : bool = False ) -> Self :
176+ """Method which sets the overall duration of this overlay event.
177+
178+ Parameters
179+ ----------
180+ value: int | None
181+ The duration of the event in ``ms (milliseconds)`` as an :class:`int`. Could be ``None`` which would mean the
182+ event will not be removed from the overlay until another condition is met, such as a new event overriding it.
183+ Defaults to ``5000`` (5 seconds).
184+ as_audio: bool
185+ An optional bool which when set will ensure the event lasts for as long as any audio provided does. If a duration
186+ is provided when this parameter is set to ``True``, the duration will be additive. Defaults to ``False``.
187+
188+ Returns
189+ -------
190+ OverlayEvent
191+ This method returns ``self`` which allows for fluid-style chaining.
192+ """
193+ duration = max (0 , value or 0 ) or None
194+ self ._raw ["duration" ] = duration
195+ self ._raw ["duration_is_audio" ] = as_audio
95196
96- def set_position (self ) -> Self : ...
97-
98- def set_duration (self , value : int , * , as_audio : bool = False ) -> Self : ...
197+ return self
99198
100199 @overload
101200 def _compress (self , convert : Literal [True ] = True ) -> str : ...
@@ -140,6 +239,13 @@ def to_blueprint(self) -> str:
140239 return self ._compress ()
141240
142241 def as_dict (self ) -> OverlayEventT :
242+ """Method which does a deep-copy on the raw data and returns it as a dictionary.
243+
244+ Returns
245+ -------
246+ dict
247+ The raw data for this event.
248+ """
143249 return copy .deepcopy (self ._raw )
144250
145251
@@ -177,6 +283,7 @@ def __new__(cls, *args: Any, **Kwargs: Any) -> Self:
177283 def __init__ (self , * , secret : str ) -> None :
178284 self ._secret = secret
179285 self ._position = EventPosition .center
286+ self ._audio_paths : dict [str , os .PathLike [str ]] = {}
180287
181288 @property
182289 def secret (self ) -> str :
@@ -260,6 +367,12 @@ def set_position(self, position: EventPosition, /) -> Self:
260367 self ._position = position
261368 return self
262369
370+ async def mount_audio (self , name : str , * , path : os .PathLike [str ]) -> ...:
371+ if name in self ._audio_paths :
372+ raise AudioAlreadyLoadedError (f"Audio with the name '{ name } ' has already been added to { self !r} ." )
373+
374+ self ._audio_paths [name ] = path
375+
263376 def generate_html (self ) -> str :
264377 template = self .template
265378 js = self .javascript
0 commit comments