Utils

class pyonfx.utils.Utils[source]

This class is a collection of static methods that will help the user in some tasks.

static progress_bar(iterable: Iterable[_LineWordSyllableChar], **kwargs) Iterable[_LineWordSyllableChar][source]

Wraps an iterable of Lines, Words, Syllables, or Chars with a tqdm progress bar.

Parameters:
  • iterable – The iterable to wrap (list of Lines, Words, Syllables, Chars).

  • **kwargs – Additional arguments for tqdm.

Returns:

An iterator with a progress bar.

static all_non_empty(lines_words_syls_or_chars: Iterable[_LineWordSyllableChar], *, filter_whitespace_text: bool = True, filter_empty_duration: bool = False, renumber_indexes: bool = True, progress_bar: bool = True) Iterable[_LineWordSyllableChar][source]

Return a filtered copy of the given objects list excluding the empty ones.

Parameters:
  • lines_words_syls_or_chars (list of Line, Word, Syllable or Char)

  • filter_whitespace_text (bool, optional) – If True, objects are filtered based on their text attribute.

  • filter_empty_duration (bool, optional) – If True, objects are filtered based on their duration attribute.

  • renumber_indexes (bool, optional) – If True, the i, word_i and syl_i attributes of the surviving objects are re-assigned to reflect their new position in the returned list.

  • progress_bar (bool, optional) – If True, the result is wrapped with progress_bar().

Returns:

The filtered objects list.

static accelerate(pct: float, acc: float | Literal['in_back', 'out_back', 'in_out_back', 'in_bounce', 'out_bounce', 'in_out_bounce', 'in_circ', 'out_circ', 'in_out_circ', 'in_cubic', 'out_cubic', 'in_out_cubic', 'in_elastic', 'out_elastic', 'in_out_elastic', 'in_expo', 'out_expo', 'in_out_expo', 'in_quad', 'out_quad', 'in_out_quad', 'in_quart', 'out_quart', 'in_out_quart', 'in_quint', 'out_quint', 'in_out_quint', 'in_sine', 'out_sine', 'in_out_sine'] | Callable[[float], float] = 1.0) float[source]

Applies an acceleration function to transform a percentage value.

Parameters:
  • pct (float) – Progress percentage value, typically between 0.0 and 1.0.

  • acc (float | str | Accelerator, optional) – Acceleration function to apply: - float: Power value (1.0 = linear, >1.0 = ease-in, <1.0 = ease-out) - str: Preset easing function name. Consult this website to help you choose: https://easings.net/ - Accelerator: Custom accelerator function

Returns:

The transformed percentage value.

Return type:

float

static interpolate(pct: float, val1: _FloatStr, val2: _FloatStr, acc: float | Literal['in_back', 'out_back', 'in_out_back', 'in_bounce', 'out_bounce', 'in_out_bounce', 'in_circ', 'out_circ', 'in_out_circ', 'in_cubic', 'out_cubic', 'in_out_cubic', 'in_elastic', 'out_elastic', 'in_out_elastic', 'in_expo', 'out_expo', 'in_out_expo', 'in_quad', 'out_quad', 'in_out_quad', 'in_quart', 'out_quart', 'in_out_quart', 'in_quint', 'out_quint', 'in_out_quint', 'in_sine', 'out_sine', 'in_out_sine'] | Callable[[float], float] = 1.0) _FloatStr[source]

Interpolates 2 given values (ASS colors, ASS alpha channels or numbers) by percent value. Supports various acceleration/easing functions for smooth animations.

Parameters:
  • pct (float) – Percent value of the interpolation (0.0 to 1.0).

  • val1 (int, float or str) – First value to interpolate (either string or number).

  • val2 (int, float or str) – Second value to interpolate (either string or number).

  • acc (float | str | Accelerator, optional) – Acceleration function to apply: - float: Power value (1.0 = linear, >1.0 = ease-in, <1.0 = ease-out), same as in ASS t tag. - str: Preset name (“ease”, “ease-in”, “ease-out”, “ease-in-out”). - Accelerator: Custom accelerator object. You can check out CubicBezier or build your own.

Returns:

Interpolated value of given 2 values (so either a string or a number).

Examples

print( Utils.interpolate(0.5, 10, 20) )
print( Utils.interpolate(0.9, "&HFFFFFF&", "&H000000&") )
print( Utils.interpolate(0.5, 10, 20, "ease-in") )
print( Utils.interpolate(0.5, 10, 20, 2.0) )
>>> 15.0
>>> &HE5E5E5&
>>> 13.05
>>> 12.5
class pyonfx.utils.FrameUtility(start_ms: int, end_ms: int, timestamps: ABCTimestamps | None, n_fr: int = 1)[source]

This class allows to accurately work in a frame per frame environment.

You can use it to iterate over the frames going from start_ms to end_ms and perform operations easily over multiple frames.

Parameters:
  • start_ms (positive int) – Initial time in ms.

  • end_ms (positive int) – Final time in ms.

  • timestamps (ABCTimestamps) – A timestamps object from [VideoTimestamps](https://github.com/moi15moi/VideoTimestamps/).

  • n_fr (positive int, optional) – Number of frames covered by each iteration.

Returns:

Returns a Generator yielding start_ms, end_ms, current frame index and total number of frames at each step.

Example

>>> # Let's assume to have an Ass object named "io" having a 20 fps video (i.e. frames are 50 ms long)
>>> FU = FrameUtility(0, 110, io.input_timestamps)
>>> for s, e, i, n in FU:
>>>     print(f"Frame {i}/{n}: {s} - {e}")
>>>
>>> Frame 1/3: 0 - 25
>>> Frame 2/3: 25 - 75
>>> Frame 3/3: 75 - 125

Note

Understanding FrameUtility:

When playing a video with subtitles (e.g., an .mkv file): - A subtitle line is displayed when the player’s current time falls between the line’s start and end times - Videos can have either constant frame rates (CFR) or variable frame rates (VFR)

Example with a CFR video at 20 fps (50ms per frame): - Player seeks frames at: 0ms, 50ms, 100ms, 150ms, …

When generating subtitle lines per frame, FrameUtility uses a “mid-point” approach: - Each frame’s timing is centered around the player’s seek time - This ensures the subtitle will be visible for the entire frame duration

Frame timings example: Frame #: Start - End (Player’s seek time) Frame 0: 0 - 25 (0, special case) Frame 1: 25 - 75 (50) Frame 2: 75 - 125 (100) Frame 3: 125 - 175 (150) …

This approach: - Ensures smooth frame transitions - Avoids flickering by avoiding gaps between frames - Works reliably for both CFR and VFR videos

reset()[source]

Resets the FrameUtility object to its starting values. It is a mandatory operation if you want to reuse the same object.

add(start_time: float, end_time: float, end_value: float, accelerator: float | Literal['in_back', 'out_back', 'in_out_back', 'in_bounce', 'out_bounce', 'in_out_bounce', 'in_circ', 'out_circ', 'in_out_circ', 'in_cubic', 'out_cubic', 'in_out_cubic', 'in_elastic', 'out_elastic', 'in_out_elastic', 'in_expo', 'out_expo', 'in_out_expo', 'in_quad', 'out_quad', 'in_out_quad', 'in_quart', 'out_quart', 'in_out_quart', 'in_quint', 'out_quint', 'in_out_quint', 'in_sine', 'out_sine', 'in_out_sine'] | Callable[[float], float] = 1.0) float[source]

Frame-by-frame equivalent of the ASS \t tag.

This function provides a frame-accurate way to transform numeric values over time, similar to how the ASS \t tag transforms styles. While \t handles complete style transformations, this method focuses on transforming individual numeric values that can then be used within style tags.

Note

Must be used within a for loop iterating a FrameUtility object.

Parameters:
  • start_time (float) – Initial time.

  • end_time (float) – Final time.

  • end_value (float) – Numeric value reached at end_time.

  • accelerator (float | str | Accelerator, optional) – Acceleration/easing to apply (check Utils.accelerate for more details).

Returns:

The transformed numeric value at the current frame of this FrameUtility object.

Examples

>>> # Let's assume to have an Ass object named "io" having a 20 fps video (i.e. frames are 50 ms long)
>>> FU = FrameUtility(25, 225, io.input_timestamps)
>>> for s, e, i, n in FU:
>>>     # We would like to transform the fsc value
>>>     # from 100 up 150 for the first 100 ms,
>>>     # and then from 150 to 100 for the remaining 200 ms
>>>     fsc = 100
>>>     fsc += FU.add(0, 100, 50)
>>>     fsc += FU.add(100, 200, -50)
>>>     print(f"Frame {i}/{n}: {s} - {e}; fsc: {fsc}")
>>>
>>> Frame 1/4: 25 - 75; fsc: 112.5
>>> Frame 2/4: 75 - 125; fsc: 137.5
>>> Frame 3/4: 125 - 175; fsc: 137.5
>>> Frame 4/4: 175 - 225; fsc: 112.5
class pyonfx.utils.ColorUtility(lines: list[Line], offset: int = 0)[source]

This class helps to obtain all the color transformations written in a list of lines (usually all the lines of your input .ass) to later retrieve all of those transformations that fit between the start_time and end_time of a line passed, without having to worry about interpolating times or other stressfull tasks.

It is highly suggested to create this object just one time in your script, for performance reasons.

Note

A few notes about the color transformations in your lines:

  • Every color-tag has to be in the format of c&Hxxxxxx&, do not forget the last &;

  • You can put color changes without using transformations, like {\1c&HFFFFFF&\3c&H000000&}Test, but those will be interpreted as {\t(0,0,\1c&HFFFFFF&\3c&H000000&)}Test;

  • For an example of how color changes should be put in your lines, check this.

Also, it is important to remember that color changes in your lines are treated as if they were continuous.

For example, let’s assume we have two lines:

  1. {\1c&HFFFFFF&\t(100,150,\1c&H000000&)}Line1, starting at 0ms, ending at 100ms;

  2. {}Line2, starting at 100ms, ending at 200ms.

Even if the second line doesn’t have any color changes and you would expect to have the style’s colors, it will be treated as it has \1c&H000000&. That could seem strange at first, but thinking about your generated lines, the majority will have start_time and end_time different from the ones of your original file.

Treating transformations as if they were continous, ColorUtility will always know the right colors to pick for you. Also, remember that even if you can’t always see them directly on Aegisub, you can use transformations with negative times or with times that exceed line total duration.

Parameters:
  • lines (list of Line) – List of lines to be parsed

  • offset (integer, optional) – Milliseconds you may want to shift all the color changes

Returns:

Returns a ColorUtility object.

Examples

# Parsing all the lines in the file
CU = ColorUtility(lines)
# Parsing just a single line (the first in this case) in the file
CU = ColorUtility([ line[0] ])
get_color_change(line: Line, c1: bool | None = None, c3: bool | None = None, c4: bool | None = None) str[source]

Returns all the color_changes in the object that fit (in terms of time) between line.start_time and line.end_time.

Parameters:
  • line (Line object) – The line of which you want to get the color changes

  • c1 (bool, optional) – If False, you will not get color values containing primary color

  • c3 (bool, optional) – If False, you will not get color values containing border color

  • c4 (bool, optional) – If False, you will not get color values containing shadow color

Returns:

A string containing color changes interpolated.

Note

If c1, c3 or c4 is/are None, the script will automatically recognize what you used in the color changes in the lines and put only the ones considered essential.

Examples

# Assume that we have l as a copy of line and we're iterating over all the syl in the current line
# All the fun stuff of the effect creation...
l.start_time = line.start_time + syl.start_time
l.end_time   = line.start_time + syl.end_time

l.text = "{\\an5\\pos(%.3f,%.3f)\\fscx120\\fscy120%s}%s" % (syl.center, syl.middle, CU.get_color_change(l), syl.text)
get_fr_color_change(line: Line, c1: bool | None = None, c3: bool | None = None, c4: bool | None = None) str[source]

Returns the single color(s) in the color_changes that fit the current frame (line.start_time) in your frame loop.

Note

If you get errors, try either modifying your \t values or set your fr parameter in FU object to 10.

Parameters:
  • line (Line object) – The line of which you want to get the color changes

  • c1 (bool, optional) – If False, you will not get color values containing primary color.

  • c3 (bool, optional) – If False, you will not get color values containing border color.

  • c4 (bool, optional) – If False, you will not get color values containing shadow color.

Returns:

A string containing color changes interpolated.

Examples

# Assume that we have l as a copy of line and we're iterating over all the syl in the current line and we're iterating over the frames
l.start_time = s
l.end_time   = e

l.text = "{\\an5\\pos(%.3f,%.3f)\\fscx120\\fscy120%s}%s" % (syl.center, syl.middle, CU.get_fr_color_change(l), syl.text)