Python: captionbob.effects
captionbob.effects.titan
Titan effect: heavy slanted capitals, keywords that glow and an emoji.
Each caption shows one or two words in heavy white capitals with no end marks. The letters lean to the right, they have a dark gray outline, and a light halo lies around them, a little higher than the letters. A keyword has its letters in one strong color, an outline in a dark shade of that color and a halo in a shade of that color, which is stronger and wider than the halo of a plain word. The keywords of a video take the keyword colors in turn. Each caption swings in: for a moment at its start its letters stand upright and wider, and then they lean again.
The halo is an event on the layer under the caption: the caption with a wider outline in the halo colors, moved up and blurred. The color of a halo is the color of its letters times the halo shade.
A caption with a keyword has an emoji in the middle of the video, above the caption. The emoji rises in from a little lower at the start of the caption. A short time before the caption ends, it drops down behind the caption. The ffmpeg filters scale and overlay draw the emoji under the ASS script, so the caption covers it. The effect gives the place of the emoji as ffmpeg expressions on an emoji layer, and it reads no image.
The effect draws nothing itself. It builds the text of an ASS script and the emoji layers, and ffmpeg draws them on the video. The common part of the effects is in effect_common.py.
The defaults are the look of the source video of the effect, measured on its frames and scaled to a video of 1080x1920.
Source file: captionbob/effects/titan.py
Settings
The command line and the HTTP API call this effect the style titan. Its settings class is TitanSettings. Each setting has the default below.
| Setting | Type | Default | Description |
|---|---|---|---|
most_words |
integer | 2 |
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 | 126 |
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 | "#2D2D2D" |
Color of the outline of the text, as "#RRGGBB". |
stroke_width |
number | 9.0 |
Width of the outline in pixels. 0 gives no outline. |
bottom_position |
number | 0.61 |
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. |
capitals |
boolean | true |
True to show the words in capitals. |
letter_width |
number | 0.78 |
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. |
slant |
number | 0.19 |
How far the top of a letter is to the right of its bottom, as a part of the height of the letter. 0 gives upright letters, and a value below 0 leans them to the left. |
swing_width |
number | 1.08 |
The width of the letters in the middle of the swing, as a part of their width, above 0. In the middle of the swing the letters stand upright. |
swing_seconds |
number | 0.1 |
The time of the swing at the start of a caption. A time shorter than 2 milliseconds gives no swing. |
halo_shade |
number | 0.65 |
The color of the halo of a plain word as a part of the color of its letters, from 0 for black to 1. |
halo_spread |
number | 20.0 |
How far the halo of a plain word reaches out from its outline before the blur, in pixels. |
halo_blur |
number | 24.0 |
The blur of the halo in pixels. |
halo_opacity |
number | 0.75 |
Strength of the halo of a plain word, from 0 for no halo to 1. |
halo_lift |
number | 4.0 |
How far the halo lies above the caption, in pixels. A value below 0 puts it below the caption. |
keyword_colors |
list | ["#E70223", "#62FF28", "#E70223", "#F4FA2B"] |
The colors of the letters of the keywords, one or more. The keywords of a video take them in turn. |
keyword_outline_shade |
number | 0.35 |
The color of the outline of a keyword as a part of the color of its letters, from 0 for black to 1. |
keyword_halo_spread |
number | 28.0 |
How far the halo of a keyword reaches out from its outline before the blur, in pixels. Its color is the color of the keyword times the halo shade. |
keyword_halo_opacity |
number | 0.9 |
Strength of the halo of a keyword, from 0 for no halo to 1. |
emoji_size |
number | 148.0 |
The side of the emoji in pixels, above 0. |
emoji_gap |
number | 14.0 |
Room in pixels between the emoji and the top of the row of the caption. |
emoji_rise |
number | 45.0 |
How far below its place the emoji starts, in pixels. |
emoji_rise_seconds |
number | 0.04 |
The time in which the emoji rises to its place. 0 shows it at its place at once. |
emoji_drop |
number | 131.0 |
How far the emoji drops at the end of its caption, in pixels. |
emoji_drop_seconds |
number | 0.07 |
The time in which the emoji drops. 0 drops it at once. |
emoji_drop_lead_seconds |
number | 0.13 |
How long before the end of its caption the emoji starts to drop. The drop never starts before the caption. |
hold_seconds |
number | 0.45 |
The longest time that a caption stays after its last word ends. The caption goes earlier when the next caption starts. |
shaded_color
def shaded_color(color: str, shade: float) -> str
Give a color with each of its red, green and blue times a shade.
Args:
color: The color, as "#RRGGBB".shade: The part of the color that stays, from 0 for black to 1.
Returns:
The shaded color, as "#RRGGBB".
Raises:
ValueError: When the color is not in the form "#RRGGBB".
swing_tags
def swing_tags(
rest_tags: str, middle_tags: str, swing_milliseconds: int) -> str
Write the tags that swing a text from its look to another look and back.
The text has its look at the start of the event. In the first half of the swing time it goes to the middle look in a straight line, and in the second half it goes back.
Args:
rest_tags: The tags of the look of the text.middle_tags: The tags of the look in the middle of the swing.swing_milliseconds: The swing time from the start of the event.
Returns:
The tags with no curly brackets. A swing shorter than SWING_HALVES milliseconds, or with the same look in its middle, gives the rest tags alone.
TitanEmojiLayer
class TitanEmojiLayer(NamedTuple)
The emoji above one caption, as the ffmpeg filters draw it.
The emoji is a square of one size, in the middle of the video. Its bottom starts the rise below its place and moves up to its place in the rise time. From the start of the drop, it moves down by the drop in the drop time and stays there to the end of the caption.
Args:
image_path: The emoji image. An animated image plays from its first frame at the start of the caption.start_seconds: Time at which the caption starts.end_seconds: Time at which the caption ends.size: The side of the emoji in pixels.middle_x: The column of the middle of the emoji.bottom_edge: The row below the emoji at its place: the emoji fills the rows above this row.rise: How far below its place the emoji starts, in pixels.rise_seconds: The time in which the emoji rises to its place.drop: How far the emoji drops, in pixels.drop_start_seconds: Time at which the emoji starts to drop.drop_seconds: The time in which the emoji drops.
TitanEmojiLayer.size_expression
def size_expression(self) -> str
Write the side of the emoji as ffmpeg reads it.
Returns:
An ffmpeg expression of a whole number of pixels, 1 or above.
TitanEmojiLayer.left_expression
def left_expression(self) -> str
Write the left column of the emoji as the overlay filter reads it.
Returns:
An ffmpeg expression with the width w of the emoji frame.
TitanEmojiLayer.top_expression
def top_expression(self) -> str
Write the top row of the emoji as the overlay filter reads it.
Returns:
An ffmpeg expression of the time t and the height h of the emoji frame.
TitanLayers
class TitanLayers(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.
TitanSettings
@dataclass(frozen=True)
class TitanSettings(CaptionSettings)
The look and the timing of the titan effect.
The settings of CaptionSettings come first: the word counts, the font, the colors, the stroke, the bottom position and the side margin. These settings follow them. The primary color is the color of the plain words, and the stroke is their outline. The halo and the emoji reach into the side margins.
Args:
capitals: True to show the words in capitals.letter_width: 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.slant: How far the top of a letter is to the right of its bottom, as a part of the height of the letter. 0 gives upright letters, and a value below 0 leans them to the left.swing_width: The width of the letters in the middle of the swing, as a part of their width, above 0. In the middle of the swing the letters stand upright.swing_seconds: The time of the swing at the start of a caption. A time shorter than 2 milliseconds gives no swing.halo_shade: The color of the halo of a plain word as a part of the color of its letters, from 0 for black to 1.halo_spread: How far the halo of a plain word reaches out from its outline before the blur, in pixels.halo_blur: The blur of the halo in pixels.halo_opacity: Strength of the halo of a plain word, from 0 for no halo to 1.halo_lift: How far the halo lies above the caption, in pixels. A value below 0 puts it below the caption.keyword_colors: The colors of the letters of the keywords, one or more. The keywords of a video take them in turn.keyword_outline_shade: The color of the outline of a keyword as a part of the color of its letters, from 0 for black to 1.keyword_halo_spread: How far the halo of a keyword reaches out from its outline before the blur, in pixels. Its color is the color of the keyword times the halo shade.keyword_halo_opacity: Strength of the halo of a keyword, from 0 for no halo to 1.emoji_size: The side of the emoji in pixels, above 0.emoji_gap: Room in pixels between the emoji and the top of the row of the caption.emoji_rise: How far below its place the emoji starts, in pixels.emoji_rise_seconds: The time in which the emoji rises to its place. 0 shows it at its place at once.emoji_drop: How far the emoji drops at the end of its caption, in pixels.emoji_drop_seconds: The time in which the emoji drops. 0 drops it at once.emoji_drop_lead_seconds: How long before the end of its caption the emoji starts to drop. The drop never starts before the caption.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 is not a whole number.ValueError: When a value is outside its range, when a color is not in the form "#RRGGBB", when the keyword colors are not a tuple of one color or more, or when the font name is empty or holds a comma.
TitanSettings.has_halo
@property
def has_halo(self) -> bool
True when the settings give a halo that can be seen, around the plain words or around the keywords.
TitanSettings.halo_style_line
def halo_style_line(self, margin_vertical: int) -> str
Write the ASS style line of the halo of these settings.
The halo is the text in the color of the letters times the halo shade, with an outline that is wider than the stroke by the halo spread. The event of the halo blurs this outline.
Args:
margin_vertical: Room in pixels between the bottom of the caption and the bottom edge of the video.
Returns:
The style line of the halo.
Raises:
ValueError: When the font name, the font size, the primary color, the outline width or a margin is outside its range.
TitanEffect
class TitanEffect
def __init__(
self, settings: TitanSettings,
keyword_picker: KeywordPicker = DEFAULT_KEYWORD_PICKER) -> None
Builds the ASS script and the emoji layers of heavy slanted capitals with a halo and keywords that glow.
Args:
settings: The look and the timing of the effect.keyword_picker: Names the keywords of each caption. With no value, the keyword of a caption is its longest word when that word has seven letters or more.
TitanEffect.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 caption group is the halo and the caption, from the lower layer up, with the same times. There is no halo event when the halo cannot be seen. An event starts with the first word of its caption. It ends 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.
TitanEffect.build_layers
def build_layers(
self, words: Sequence[Word], video: VideoInfo,
emoji_path: Path) -> TitanLayers
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.