captionbob docs

Version 1.0.0
Contents ↓

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.