Python: captionbob.effects
captionbob.effects.apex
The apex effect: bold white capitals whose keyword grows when spoken.
Each caption shows its words in bold white capitals, with no end marks. A caption ends at each end mark, such as a comma. A short caption stays on one line, and a longer caption is cut into two lines, and the top line takes the larger half of the words. The keywords of the captions take the keyword colors red, yellow and green in turn. A keyword has the size of the other words until it is spoken, and then it grows to the keyword scale.
Each letter has a black outline and a soft dark halo around it. Each line of a caption has three events: the halo, the outline and the letters. The top line stands on the caption bottom, and the second line hangs from the line gap below it.
Each caption zooms in: it starts at the zoom scale of its size and grows to its full size in the zoom time, fast at the start and slow at the end. The captions take the tilts in turn, and a tilted caption turns around the middle of its lines as one piece.
A caption with a keyword has an emoji below it or above it, in the middle of the video. The captions with a keyword take the emoji places in turn. The emoji grows with its caption. ASS cannot draw an image, so build_layers gives the emoji layers for the ffmpeg overlay filter next to the script.
Source file: captionbob/effects/apex.py
Settings
The command line and the HTTP API call this effect the style apex. Its settings class is ApexSettings. Each setting has the default below.
| Setting | Type | Default | Description |
|---|---|---|---|
most_words |
integer | 3 |
The most words that one caption shows. |
fewest_words |
integer | 1 |
The fewest words that one caption shows, when its sentence is long enough for that. |
font_name |
string | "Ubuntu" |
Name of the font of the text. |
font_size |
integer | 72 |
Height of the font in pixels. |
bold |
boolean | true |
True for bold text. |
primary_color |
string | "#FFFFFF" |
Color of the text, as "#RRGGBB". |
stroke_color |
string | "#FFFFFF" |
Color of the outline of the text, as "#RRGGBB". |
stroke_width |
number | 2.0 |
Width of the outline in pixels. 0 gives no outline. |
bottom_position |
number | 0.677 |
Where the bottom of the caption sits, as a part of the video height from the top. 0.75 is three quarters of the way down. |
side_margin |
integer | 54 |
Room in pixels that stays free on the left and on the right of the caption. A caption that is too wide for the room between the margins breaks into lines. |
letter_width |
number | 1.1 |
The width of the letters as a part of the width that the font gives them, above 0. 1 keeps the width of the font. |
line_gap |
number | 4.4 |
Room in pixels between the bottom of the top line and the top of the second line. A value below 0 puts the lines closer. |
capitals |
boolean | true |
True to write the words in capitals. |
end_at_marks |
boolean | true |
True to end a caption at each end mark, such as a comma. |
line_letters |
integer | 13 |
The most letters, with the spaces, of a caption that stays on one line, 1 or above. |
keyword_colors |
list | ["#DE1B2F", "#FAFF31", "#DE1B2F", "#4AFF41", "#4AFF41", "#DE1B2F", "#FAFF31"] |
The colors of the keywords, as "#RRGGBB". The keywords take them in turn. |
keyword_scale |
number | 1.1 |
The size of a keyword from the time at which it is spoken, as a part of the size of the other words, above 0. |
outline_color |
string | "#000000" |
The color of the outline and of the halo, as "#RRGGBB". |
outline_spread |
number | 5.0 |
How far the outline reaches past the stroke, in pixels. |
outline_blur |
number | 2.0 |
How far the edge of the outline fades out, in pixels. |
outline_opacity |
number | 0.9 |
The strength of the outline, from 0 to 1. |
halo_spread |
number | 9.0 |
How far the halo reaches past the outline, in pixels. |
halo_blur |
number | 7.0 |
How far the edge of the halo fades out, in pixels. |
halo_opacity |
number | 0.8 |
The strength of the halo, from 0 to 1. |
tilts |
list | [-2.0, 0.0, 3.0, -1.5, 0.0, 2.5, -3.5, -2.5, 2.0, -2.0, 0.0, 3.0, -3.0, 0.5, 3.0, -1.5, 0.0, 3.5, -3.0, 0.0, 5.0, -3.5, -1.0] |
The tilts of the captions in degrees clockwise, one or more. The captions take them in turn. |
zoom_scale |
number | 0.8 |
The size of a caption and of its emoji at the start of the caption, as a part of the full size, above 0. |
zoom_seconds |
number | 0.6 |
The time in which a caption grows to its full size. 0 shows it at its full size at once. |
zoom_ease |
number | 0.6 |
The ease of the zoom of the tag \t, above 0. 1 grows at one speed, and a value below 1 is fast at the start and slow at the end. |
emoji_size |
number | 116.0 |
The side of the emoji in pixels, above 0. |
emoji_gap |
number | 12.0 |
Room in pixels between the lines of a caption and its emoji. |
emoji_places |
list | ["below", "below", "above", "above", "above", "below", "below"] |
EMOJI_BELOW or EMOJI_ABOVE for each emoji, one or more. The captions with a keyword take them in turn. |
hold_seconds |
number | 0.2 |
The longest time that a caption stays after its last word ends. The caption goes earlier when the next caption starts. |
ApexSettings
@dataclass(frozen=True)
class ApexSettings(LineSettings)
The look and the timing of the apex effect.
The settings of CaptionSettings come first: the word counts, the font, the colors, the stroke, the bottom position and the side margin. The settings of LineSettings follow them, from the letter width to the line letters, and then the settings of the effect and the hold time. The effect changes the defaults of some of them. The primary color is the color of the words that are not keywords. The stroke is the outline of the letters, which makes them heavier in the color of their word. The outline of the settings of the effect is the black outline past it. The bottom position is the bottom of the top line.
Args:
keyword_colors: The colors of the keywords, as "#RRGGBB". The keywords take them in turn.keyword_scale: The size of a keyword from the time at which it is spoken, as a part of the size of the other words, above 0.outline_color: The color of the outline and of the halo, as "#RRGGBB".outline_spread: How far the outline reaches past the stroke, in pixels.outline_blur: How far the edge of the outline fades out, in pixels.outline_opacity: The strength of the outline, from 0 to 1.halo_spread: How far the halo reaches past the outline, in pixels.halo_blur: How far the edge of the halo fades out, in pixels.halo_opacity: The strength of the halo, from 0 to 1.tilts: The tilts of the captions in degrees clockwise, one or more. The captions take them in turn.zoom_scale: The size of a caption and of its emoji at the start of the caption, as a part of the full size, above 0.zoom_seconds: The time in which a caption grows to its full size. 0 shows it at its full size at once.zoom_ease: The ease of the zoom of the tag \t, above 0. 1 grows at one speed, and a value below 1 is fast at the start and slow at the end.emoji_size: The side of the emoji in pixels, above 0.emoji_gap: Room in pixels between the lines of a caption and its emoji.emoji_places: EMOJI_BELOW or EMOJI_ABOVE for each emoji, one or more. The captions with a keyword take them in turn.hold_seconds: The longest time that a caption stays after its last word ends. The caption goes earlier when the next caption starts.
Raises:
TypeError: When a word count or the line letters is not a whole number.ValueError: When a value is outside its range, when a color is not in the form "#RRGGBB", when an emoji place is not known, or when the font name is empty or holds a comma.
ApexSettings.outline_width
def outline_width(self) -> float
Give the width of the border of the outline: the stroke and the outline spread, in pixels.
ApexSettings.halo_width
def halo_width(self) -> float
Give the width of the border of the halo: the stroke, the outline spread and the halo spread, in pixels.
zoom_tags
def zoom_tags(settings: ApexSettings, border_width: float) -> str
Write the tags of a text that zooms in with its border.
The text starts at the zoom scale of its size, with the letter width, and grows to its full size in the zoom time, with the zoom ease of the tag \t. libass does not scale a border with the text, so the border grows with it.
Args:
settings: The settings of the effect.border_width: The width of the border at the full size, in pixels.
Returns:
The tags with no curly brackets. With no zoom time, or with the zoom scale 1, the text has its full size at once.
keyword_size_tags
def keyword_size_tags(
font_size: int, keyword_scale: float, spoken_milliseconds: int) -> str
Write the tags of a keyword that grows when it is spoken.
Args:
font_size: The font size of the other words.keyword_scale: The size of the keyword when it is spoken, as a part of the size of the other words.spoken_milliseconds: The tag time at which the keyword is spoken, 0 for a keyword that is spoken from the start of the event.
Returns:
The tags with no curly brackets. The keyword has the font size of the other words until it is spoken. libass reads the time 0 in the tag \t as the whole length of the event, so a keyword that is spoken from the start has its grown size with no timed tag.
keyword_own_tags
def keyword_own_tags(tags: str) -> str
Give the tags of a keyword whose choice is its tags.
Args:
tags: The tags of the keyword, with no curly brackets.
Returns:
The same tags.
ApexLayers
class ApexLayers(NamedTuple)
What the effect gives for the words of one video.
Args:
ass_text: The text of the ASS script.emojis: One emoji layer for each caption with a keyword, in time order.
ApexCaption
class ApexCaption(NamedTuple)
One caption of the apex effect.
Args:
caption: The caption with the times of its events and its words as spoken.group: The words of the caption as the effect shows them.lines: The lines of the caption from the top, with the words as the effect shows them.keyword_colors: The color of each keyword, by its place in the words of the caption.tilt: The tilt of the caption in degrees clockwise.emoji_place: EMOJI_BELOW or EMOJI_ABOVE for a caption with a keyword, or None for a caption with no keyword.
ApexEffect
class ApexEffect
def __init__(
self, settings: ApexSettings,
keyword_picker: KeywordPicker = DEFAULT_KEYWORD_PICKER) -> None
Builds the ASS script and the emoji layers of bold white capitals with a black outline and a dark halo, whose keyword is red, yellow or green and grows when it is spoken, that zoom in and tilt, with an emoji below or above.
Args:
settings: The look and the timing of the effect.keyword_picker: Names the keywords of each caption. A caption with a keyword has an emoji. With no value, the keyword of a caption is its longest word when that word has seven letters or more.
ApexEffect.build_ass_text
def build_ass_text(self, words: Sequence[Word], video: VideoInfo) -> str
Build the ASS script of the effect for the words of one video.
Each line of each caption has a halo, an outline and a text event. The events of a caption start with its first word. They end when the next caption starts, and at the latest the hold time after its last word and at the end of the video.
Args:
words: The words of the video in time order, each with its times.video: The frame size and the duration of the video.
Returns:
The text of the ASS script. It has no event when there are no words.
Raises:
ValueError: When the side margins leave no room for the caption in the width of the video, or when the keyword picker gives a place that is not a word of its caption.
ApexEffect.build_layers
def build_layers(
self, words: Sequence[Word], video: VideoInfo,
emoji_path: Path) -> ApexLayers
Build the ASS script and the emoji layers for one video.
The script is the script of build_ass_text. Each caption with a keyword has one emoji layer with the times of the caption.
Args:
words: The words of the video in time order, each with its times.video: The frame size and the duration of the video.emoji_path: The emoji image, such as an animated GIF or a PNG with a see-through background. The effect does not read the image. ffmpeg reads it when it draws the layers.
Returns:
The script and the emoji layers.
Raises:
TypeError: When the emoji path is not a Path.FileNotFoundError: When the emoji image is not a file.ValueError: When the side margins leave no room for the caption in the width of the video, or when the keyword picker gives a place that is not a word of its caption.