### Install PyonFX on OpenSUSE Source: https://pyonfx.readthedocs.io/en/latest/quick%20start Installs PyonFX and its dependencies on OpenSUSE systems. This includes Python 3, pip, and development packages for Cairo, GObject introspection, and GTK. ```bash sudo zypper install python3 python3-pip cairo-devel pkg-config python3-devel gcc gobject-introspection-devel python3-gobject python3-gobject-Gdk typelib-1_0-Gtk-3_0 libgtk-3-0 python3 -m pip install --upgrade pyonfx ``` -------------------------------- ### Install PyonFX dependencies on macOS Source: https://pyonfx.readthedocs.io/en/latest/_sources/quick%20start Installs necessary dependencies on macOS using Homebrew, including Python, py3cairo, pygobject3, pango, cairo, and glib. This prepares the system for PyonFX installation. ```sh brew install python py3cairo pygobject3 pango cairo glib ``` -------------------------------- ### Install PyonFX on macOS Source: https://pyonfx.readthedocs.io/en/latest/quick%20start Installs PyonFX and its dependencies on macOS, potentially requiring Homebrew. This command installs Python, py3cairo, pygobject3, pango, cairo, and glib. ```bash brew install python py3cairo pygobject3 pango cairo glib python3 -m pip install --upgrade pyonfx ``` -------------------------------- ### Install/Upgrade PyonFX using pip Source: https://pyonfx.readthedocs.io/en/latest/quick%20start This command installs or upgrades the PyonFX library using pip, the Python package installer. It is a fundamental step for using PyonFX on any system. ```bash pip install --upgrade pyonfx ``` ```bash python3 -m pip install --upgrade pyonfx ``` -------------------------------- ### Install PyonFX on Ubuntu/Debian Source: https://pyonfx.readthedocs.io/en/latest/quick%20start Installs PyonFX and its dependencies on Ubuntu/Debian systems. This includes essential packages like python3, pip, and development tools for GObject introspection and Cairo. ```bash sudo apt install python3 python3-pip libgirepository-2.0-dev gobject-introspection libcairo2-dev build-essential gir1.2-gtk-3.0 python3-gi python3-gi-cairo python3 -m pip install --upgrade pyonfx ``` -------------------------------- ### Install PyonFX on Arch Linux (AUR) Source: https://pyonfx.readthedocs.io/en/latest/quick%20start Installs PyonFX using the AUR (Arch User Repository) helper 'paru'. This is a convenient method for Arch Linux users to install community-maintained packages. ```bash paru -S python-pyonfx ``` -------------------------------- ### Install PyonFX dependencies on Arch Linux (Manual) Source: https://pyonfx.readthedocs.io/en/latest/_sources/quick%20start Installs core dependencies on Arch Linux for manual PyonFX installation, including Python, pip, cairo, gobject-introspection, and pango. This is an alternative to using AUR. ```sh sudo pacman -S --needed python python-pip python-cairo python-gobject pango ``` -------------------------------- ### Install PyonFX on Fedora Source: https://pyonfx.readthedocs.io/en/latest/quick%20start Installs PyonFX and its dependencies on Fedora systems. This includes Python 3, pip, GCC, and development packages for GObject introspection, Cairo, and GTK. ```bash sudo dnf install python3 python3-pip gcc gobject-introspection-devel cairo-devel pkg-config python3-devel python3-gobject gtk3 python3 -m pip install --upgrade pyonfx ``` -------------------------------- ### Install PyonFX dependencies on OpenSUSE Source: https://pyonfx.readthedocs.io/en/latest/_sources/quick%20start Installs required packages on OpenSUSE for PyonFX, including Python 3, pip, cairo development files, pkg-config, Python 3 development files, GCC, gobject-introspection development files, Python GI bindings, and GTK3 runtime libraries. ```sh sudo zypper install python3 python3-pip cairo-devel pkg-config python3-devel gcc gobject-introspection-devel python3-gobject python3-gobject-Gdk typelib-1_0-Gtk-3_0 libgtk-3-0 ``` -------------------------------- ### Install/Upgrade PyonFX using pip Source: https://pyonfx.readthedocs.io/en/latest/_sources/quick%20start This command installs or upgrades the PyonFX library using pip, the Python package installer. It ensures you have the latest version of the library available for use in your Python scripts. This is a common step across multiple operating systems. ```sh pip install --upgrade pyonfx ``` ```sh python3 -m pip install --upgrade pyonfx ``` ```sh python -m pip install --upgrade pyonfx ``` -------------------------------- ### Running Python Scripts Source: https://pyonfx.readthedocs.io/en/latest/_sources/quick%20start Commands to execute Python scripts. The first command uses the default 'python' interpreter, while the second uses 'python3', which is useful when both Python 2 and 3 are installed. ```sh python namefile.py ``` ```sh python3 namefile.py ``` -------------------------------- ### Install PyonFX using AUR on Arch Linux Source: https://pyonfx.readthedocs.io/en/latest/_sources/quick%20start Installs the PyonFX package from the Arch User Repository (AUR) using the `paru` helper. This is a convenient way to install community-maintained packages on Arch Linux. ```sh paru -S python-pyonfx ``` -------------------------------- ### Install PyonFX dependencies on Ubuntu/Debian Source: https://pyonfx.readthedocs.io/en/latest/_sources/quick%20start Installs essential packages on Ubuntu/Debian systems required for PyonFX, including Python 3, pip, development libraries for gobject-introspection, cairo, and build tools. It also installs Python GI bindings for GTK and Cairo. ```sh sudo apt install python3 python3-pip libgirepository-2.0-dev gobject-introspection libcairo2-dev build-essential gir1.2-gtk-3.0 python3-gi python3-gi-cairo ``` -------------------------------- ### Install PyonFX on Arch Linux (Manual) Source: https://pyonfx.readthedocs.io/en/latest/quick%20start Manually installs PyonFX and its dependencies on Arch Linux using pacman and pip. This method ensures essential packages like python, pip, cairo, gobject, and pango are installed. ```bash sudo pacman -S --needed python python-pip python-cairo python-gobject pango python -m pip install --upgrade pyonfx ``` -------------------------------- ### Install PyonFX dependencies on Fedora Source: https://pyonfx.readthedocs.io/en/latest/_sources/quick%20start Installs necessary packages on Fedora systems for PyonFX, including Python 3, pip, GCC, gobject-introspection development files, cairo development files, pkg-config, Python 3 development headers, and GTK3. ```sh sudo dnf install python3 python3-pip gcc gobject-introspection-devel cairo-devel pkg-config python3-devel python3-gobject gtk3 ``` -------------------------------- ### Set Pango Cairo Backend on macOS Source: https://pyonfx.readthedocs.io/en/latest/quick%20start Sets the Pango Cairo backend to fontconfig for correct rendering on macOS. This is a workaround for potential rendering issues. ```bash PANGOCAIRO_BACKEND=fc python3 namefile.py ``` -------------------------------- ### Set PangoCairo backend on macOS Source: https://pyonfx.readthedocs.io/en/latest/_sources/quick%20start This command sets the PangoCairo backend to fontconfig, which may resolve rendering issues if output is not displayed correctly on macOS. It's an environment variable setting applied before running a Python script. ```sh PANGOCAIRO_BACKEND=fc python3 namefile.py ``` -------------------------------- ### Initialize and Get ASS Data with Pyonfx Source: https://pyonfx.readthedocs.io/en/latest/reference/ass%20core Demonstrates how to initialize an Ass object with input and output file paths and then retrieve the metadata, styles, and lines from the ASS file. ```python io = Ass("in.ass") meta, styles, lines = io.get_data() ``` -------------------------------- ### Example: Shape Bounding Box Calculation Source: https://pyonfx.readthedocs.io/en/latest/reference/shape Demonstrates how to calculate the bounding box of a Shape object using the bounding() method. It shows examples with and without the 'exact' parameter, illustrating the difference in output for complex curves. ```python print( "Left-top: %d %d\nRight-bottom: %d %d" % ( Shape("m 10 5 l 25 5 25 42 10 42").bounding() ) ) print( Shape("m 313 312 b 254 287 482 38 277 212 l 436 269 b 378 388 461 671 260 481").bounding() ) print( Shape("m 313 312 b 254 287 482 38 277 212 l 436 269 b 378 388 461 671 260 481").bounding(exact=True) ) ``` -------------------------------- ### Interpolate Values with Easing (Python) Source: https://pyonfx.readthedocs.io/en/latest/reference/utils Interpolates between two values (numbers or ASS color strings) based on a percentage and an optional acceleration function. This allows for smooth transitions and animations between the start and end values. It supports various predefined easing functions and custom acceleration curves. ```python from pyonfx.utils import Utils # Interpolate between two numbers with linear easing interpolated_number = Utils.interpolate(0.5, 10, 20) # Interpolate between two ASS color strings with 'ease-out' acceleration interpolated_color = Utils.interpolate(0.7, "&HFFFFFF&", "&H000000&", acc="out_sine") # Interpolate with a custom power value for acceleration interpolated_custom = Utils.interpolate(0.4, 0, 100, acc=3.0) # Example usage from documentation 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) ) ``` -------------------------------- ### Get Frame Color Changes in Python Source: https://pyonfx.readthedocs.io/en/latest/reference/utils Fetches the specific color(s) that apply to the current frame, considering the line's start time. This function allows filtering by primary (c1), border (c3), and shadow (c4) colors. ```python # 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) ``` -------------------------------- ### Pyonfx Ass Class Initialization Source: https://pyonfx.readthedocs.io/en/latest/reference/ass%20core Illustrates the initialization of the Ass class, which manages ASS file content. It takes input and output paths and has options for keeping the original file, enabling extended information, and handling vertical Kanji. Default values are provided for output path and optional parameters. ```python _class _pyonfx.ass_core.Ass(_path_input : str_, _path_output : str = 'output.ass'_, _keep_original : bool = True_, _extended : bool = True_, _vertical_kanji : bool = False_)[source] Contains all the informations about a file in the ASS format and the methods to work with it for both input and output. Usually you will create an Ass object and use it for input and output (see example section). PyonFX set automatically an absolute path for all the info in the output, so that wherever you will put your generated file, it should always load correctly video and audio. Parameters: * **path_input** (_str_) – Path for the input file (either relative to your .py file or absolute). * **path_output** (_str_) – Path for the output file (either relative to your .py file or absolute) (DEFAULT: “Output.ass”). * **keep_original** (_bool_) – If True, you will find all the lines of the input file commented before the new lines generated. * **extended** (_bool_) – Calculate more informations from lines (usually you will not have to touch this). * **vertical_kanji** (_bool_) – If True, line text with alignment 4, 5 or 6 will be positioned vertically. Additionally, `line` fields will be re-calculated based on the re-positioned `line.chars`. * **progress** (_bool_) – If True, a progress bar will be displayed when iterating over the lines. ``` -------------------------------- ### Get Color Changes for a Line in Python Source: https://pyonfx.readthedocs.io/en/latest/reference/utils Retrieves all color changes within a specified line's start and end times. It can filter based on primary (c1), border (c3), and shadow (c4) colors. If filters are not provided, it intelligently selects essential colors. ```python # 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) ``` -------------------------------- ### Initialize ColorUtility with Subtitle Lines (Python) Source: https://pyonfx.readthedocs.io/en/latest/_modules/pyonfx/utils Demonstrates how to instantiate the ColorUtility class, either with all lines from a file or a subset. This is crucial for performance as the object should ideally be created once. ```python # 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] ]) ``` -------------------------------- ### ColorUtility Constructor Source: https://pyonfx.readthedocs.io/en/latest/reference/utils Initializes a ColorUtility object. It can parse a list of lines or a single line to manage color changes. ```APIDOC ## ColorUtility Constructor ### Description Initializes a ColorUtility object. It can parse a list of lines or a single line to manage color changes. ### Method `__init__` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **lines** (list of Line) - Required - List of lines to be parsed. - **offset** (integer) - Optional - Milliseconds to shift all color changes. ### Request Example ```python # 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] ]) ``` ### Response #### Success Response (200) - **ColorUtility object** - Returns a ColorUtility object. #### Response Example ```json { "ColorUtility object": "" } ``` ``` -------------------------------- ### PyonFX ColorUtility: Get FR Color Change Source: https://pyonfx.readthedocs.io/en/latest/reference/utils Explains the `ColorUtility.get_fr_color_change()` function within the PyonFX library. This function is specific to getting a 'fr' (likely frame rate or forward) color change. It probably requires color inputs and a time or frame factor. ```python from pyonfx.color import ColorUtility # Example placeholder for ColorUtility.get_fr_color_change() # actual usage may vary based on function signature fr_color_change = ColorUtility.get_fr_color_change('#00FF00', '#FFFF00', 10) ``` -------------------------------- ### Initializing Ass Class with File Paths in Python Source: https://pyonfx.readthedocs.io/en/latest/_modules/pyonfx/ass_core Illustrates the initialization of the `Ass` class, which takes input and output file paths. It includes optional parameters for keeping original formatting and enabling extended data processing. File path validation is performed, raising `FileNotFoundError` if the input file does not exist. ```python io = Ass( path_input: str, path_output: str = "output.ass", keep_original: bool = True, extended: bool = True, vertical_kanji: bool = False, ) ``` -------------------------------- ### PyonFX ColorUtility: Get Color Change Source: https://pyonfx.readthedocs.io/en/latest/reference/utils Details the `ColorUtility.get_color_change()` function from the PyonFX library. This function is used to calculate color transitions or changes. It likely takes color values and a factor as input. ```python from pyonfx.color import ColorUtility # Example placeholder for ColorUtility.get_color_change() # actual usage may vary based on function signature color_change = ColorUtility.get_color_change('#FF0000', '#0000FF', 0.5) ``` -------------------------------- ### ColorUtility Class Initialization and Regex Compilation (Python) Source: https://pyonfx.readthedocs.io/en/latest/_modules/pyonfx/utils The core initialization logic for ColorUtility, including the compilation of regular expressions to parse various ASS tags like color changes and transformations. It iterates through provided lines to extract color information. ```python def __init__(self, lines: list[Line], offset: int = 0): self.color_changes = [] self.c1_req = False self.c3_req = False self.c4_req = False # Compiling regex tag_all = re.compile(r"{.*?}") tag_t = re.compile(r"\\t( *?(-?\d+?) *?, *?(-?\d+?) *?, *(.+?) *?)") tag_c1 = re.compile(r"\\1c(&H.{6}&") tag_c3 = re.compile(r"\\3c(&H.{6}&") tag_c4 = re.compile(r"\\4c(&H.{6}&") for line in lines: # Obtaining all tags enclosured in curly brackets tags = tag_all.findall(line.raw_text) # Let's search all color changes in the tags for tag in tags: # Get everything beside \t to see if there are some colors there other_tags = tag_t.sub("", tag) # Searching for colors in the other tags c1, c3, c4 = ( tag_c1.search(other_tags), tag_c3.search(other_tags), tag_c4.search(other_tags), ) # If we found something, add to the list as a color change if c1 or c3 or c4: if c1: c1 = c1.group(0) self.c1_req = True if c3: c3 = c3.group(0) self.c3_req = True if c4: c4 = c4.group(0) self.c4_req = True self.color_changes.append( { "start": line.start_time + offset, "end": line.start_time + offset, "acc": 1, "c1": c1, "c3": c3, "c4": c4, } ) # Find all transformation in tag ts = tag_t.findall(tag) # Working with each transformation for t in ts: # Parsing start, end, optional acceleration and colors start, end, acc_colors = int(t[0]), int(t[1]), t[2].split(",") acc, c1, c3, c4 = 1, None, None, None # Do we have also acceleration? if len(acc_colors) == 1: c1, c3, c4 = ( tag_c1.search(acc_colors[0]), tag_c3.search(acc_colors[0]), ``` -------------------------------- ### Pyonfx Ass Class Methods Source: https://pyonfx.readthedocs.io/en/latest/reference/ass%20core Details key methods of the Ass class, including adding styles, writing lines to the output buffer, and saving the processed ASS data to a file. The save() method handles the actual file writing. ```python add_style(_style_name : str_, _style : Style_) → None[source] Adds a given ASS style into the output if it doesn’t already exist. The style is serialized and inserted into the [V4+ Styles] section. write_line(_line : Line_) → None[source] Appends a line to the output list (which is private) that later on will be written to the output file when calling save(). Use it whenever you’ve prepared a line, it will not impact performance since you will not actually write anything until `save()` will be called. Parameters: **line** (`Line`) – A line object. If not valid, TypeError is raised. save(_quiet : bool = False_) → None[source] Write everything inside the private output list to a file. Parameters: **quiet** (_bool_) – If True, you will not get printed any message. ``` -------------------------------- ### Python: Ass class initialization Source: https://pyonfx.readthedocs.io/en/latest/_modules/pyonfx/ass_core Initializes an Ass object for managing ASS subtitle files. It takes input and output file paths and options for keeping original lines and extending information. The output path defaults to 'Output.ass'. ```python from pyonfx.ass import Ass # Example usage: # ass_file = Ass(path_input='input.ass', path_output='output.ass', keep_original=True, extended=False) ``` -------------------------------- ### FrameUtility Class for Video Frame Iteration (Python) Source: https://pyonfx.readthedocs.io/en/latest/_modules/pyonfx/utils The FrameUtility class facilitates frame-by-frame processing in video analysis. It iterates from a start to end time in milliseconds, yielding frame information. It supports VFR and CFR videos using a midpoint approach for frame timing to ensure subtitle visibility. ```python class FrameUtility: """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 """ def __init__( self, start_ms: int, end_ms: int, timestamps: ABCTimestamps | None, n_fr: int = 1, ): # Check for invalid values if start_ms < 0 or end_ms < 0: raise ValueError("Parameters 'start_ms' and 'end_ms' must be >= 0.") if end_ms < start_ms: raise ValueError("Parameter 'start_ms' is expected to be <= 'end_ms'.") if n_fr <= 0: raise ValueError("Parameter 'n_fr' must be > 0.") if timestamps is None: raise ValueError( ``` -------------------------------- ### Convert Bezier Curves to Lines in Pyonfx Shape Source: https://pyonfx.readthedocs.io/en/latest/_modules/pyonfx/shape This method processes a list of shape elements, converting any Bezier curve commands ('b') into a series of line segments. It maintains the current point and appends the new line segments to a flattened list of elements. Handles cases where a Bezier curve might appear without a preceding starting point. ```python def process_elements(self): line_points[:-1] if line_points else [] ) # Process elements flattened_elements = [] current_point = None for element in self: if element.command == "b": if current_point is None: raise ValueError("Bezier curve found without a starting point") # Convert bezier to line segments p0 = current_point p1, p2, p3 = element.coordinates line_points = _bezier_to_lines(p0, p1, p2, p3) # Add line segments for point in line_points: flattened_elements.append(ShapeElement("l", [point])) # Add final point flattened_elements.append(ShapeElement("l", [p3])) current_point = p3 elif element.command == "c": # Bezier curves are already converted to lines pass else: # Keep other commands as-is and track current point flattened_elements.append(element) if element.coordinates: current_point = element.coordinates[-1] # Update shape with flattened elements self.elements = flattened_elements return self ``` -------------------------------- ### Reading ASS File Data in Python Source: https://pyonfx.readthedocs.io/en/latest/_modules/pyonfx/ass_core Demonstrates how to initialize the Ass class with an input file path and retrieve metadata, styles, and lines from an ASS file. Assumes the 'Ass' class and related components are available. ```python io = Ass("in.ass") meta, styles, lines = io.get_data() ``` -------------------------------- ### Convert Color Between Different Models (ASS, RGB, HSV) Source: https://pyonfx.readthedocs.io/en/latest/reference/convert Provides a versatile method for converting colors between different formats like ASS, RGB, and HSV. It accepts color input as a string or a tuple of integers/floats. Users must specify the input and output color models and can optionally control output rounding. The return type varies based on the output format. ```python from pyonfx.convert import Convert, ColorModel # Convert ASS color to RGB tuple ass_color = "&H0000FF&" rgb_color = Convert.color(ass_color, ColorModel.ASS, ColorModel.RGB) print(f"ASS color {ass_color} converted to RGB: {rgb_color}") # Convert RGB tuple to ASS color string rgb_tuple = (255, 165, 0) # Orange ass_output = Convert.color(rgb_tuple, ColorModel.RGB, ColorModel.ASS) print(f"RGB tuple {rgb_tuple} converted to ASS: {ass_output}") # Convert RGB string to HSV tuple (rounded) rgb_string = "#FF0000" # Red hsv_color_rounded = Convert.color(rgb_string, ColorModel.RGB, ColorModel.HSV) print(f"RGB string {rgb_string} converted to HSV (rounded): {hsv_color_rounded}") # Convert RGB string to HSV tuple (not rounded) hsv_color_unrounded = Convert.color(rgb_string, ColorModel.RGB, ColorModel.HSV, round_output=False) print(f"RGB string {rgb_string} converted to HSV (unrounded): {hsv_color_unrounded}") ``` -------------------------------- ### FrameUtility: Iterate and Add Time-Based Values in Python Source: https://pyonfx.readthedocs.io/en/latest/_modules/pyonfx/utils The FrameUtility class in Python is designed to manage frame-based time transformations, similar to ASS \\t tags. It allows for iterating through frames and adding values that change over time with specified acceleration. Dependencies include a timestamps object and TimeType enum. It takes start and end times, and an end value as input. ```python class FrameUtility: def __init__(self, start_ms: float, end_ms: float, timestamps, n_fr: int = 1): """Initialize FrameUtility. Parameters: start_ms (float): The start time in milliseconds. end_ms (float): The end time in milliseconds. timestamps: An object that provides time conversion utilities. n_fr (int, optional): Number of frames per step. Defaults to 1. """ if timestamps is None: raise ValueError( "Parameter 'timestamps' cannot be None (hint: does your ASS file have a video specified?)." ) self.timestamps = timestamps self.start_ms = start_ms self.end_ms = end_ms self.start_fr = self.curr_fr = timestamps.time_to_frame( start_ms, TimeType.START, 3 ) self.end_fr = timestamps.time_to_frame(end_ms, TimeType.END, 3) self.end_ms_snapped = timestamps.frame_to_time( self.end_fr, TimeType.END, 3, True ) self.n_fr = n_fr self.i = 0 self.n = self.end_fr - self.start_fr + 1 def __iter__(self): # Generate values for the frames on demand. The end time is always clamped to the end_ms value. for self.i in range(0, self.n, self.n_fr): yield ( self.timestamps.frame_to_time(self.curr_fr, TimeType.START, 3, True), min( self.timestamps.frame_to_time( self.curr_fr + self.n_fr - 1, TimeType.END, 3, True ), self.end_ms_snapped, ), self.i + 1, self.n, ) self.curr_fr += self.n_fr # Reset the object to make it usable again self.reset() def reset(self): """ Resets the FrameUtility object to its starting values. It is a mandatory operation if you want to reuse the same object. """ self.i = 0 self.curr_fr = self.start_fr def add( self, 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: """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 """ curr_ms = self.timestamps.frame_to_time( self.i + (self.n_fr - 1) // 2, TimeType.END, 3, True ) if curr_ms <= start_time: return 0 elif curr_ms >= end_time: return end_value curr = curr_ms - start_time total = end_time - start_time return Utils.interpolate(curr / total, 0, end_value, accelerator) ``` -------------------------------- ### Progress Bar for Iterables (Python) Source: https://pyonfx.readthedocs.io/en/latest/reference/utils Wraps an iterable (like Lines, Words, Syllables, or Chars) with a tqdm progress bar for visual feedback during long operations. It accepts additional keyword arguments for customizing the tqdm progress bar's appearance and behavior. ```python from typing import Iterable from pyonfx.utils import Utils # Assuming you have an iterable of objects my_iterable = [...] # Wrap the iterable with a progress bar for item in Utils.progress_bar(my_iterable, desc="Processing items"): # Process each item ``` -------------------------------- ### FrameUtility: Iterate through video frames Source: https://pyonfx.readthedocs.io/en/latest/reference/utils The FrameUtility class generates frame timings, yielding start time, end time, current frame index, and total frames. It's useful for processing video frames sequentially, ensuring smooth transitions and avoiding gaps. It supports both Constant Frame Rate (CFR) and Variable Frame Rate (VFR) videos. ```python from pyonfx.utils import FrameUtility # Assuming 'io' is an Ass object with input_timestamps and a 20 fps video # Example usage: # FU = FrameUtility(0, 110, io.input_timestamps) # for s, e, i, n in FU: # print(f"Frame {i}/{n}: {s} - {e}") ``` -------------------------------- ### Pyonfx Line Object Methods Source: https://pyonfx.readthedocs.io/en/latest/reference/ass%20core Details the methods available for the Line object, specifically the copy() method for creating a deep copy of a line and the _from_ass_line class method for parsing a single ASS line into a Line object. ```python copy() → Line[source] Returns: A deep copy of this object (line) _classmethod _from_ass_line(_line : str_, _line_index : int_, _styles : dict[str, Style]_) → Line[source] Parses a single ASS line and returns the corresponding Line object. ``` -------------------------------- ### Get Frame Color Change with Conditional Logic (Python) Source: https://pyonfx.readthedocs.io/en/latest/_modules/pyonfx/utils Retrieves interpolated color changes for the current frame based on specified color types (primary, border, shadow). It handles cases where no color changes are defined, when the current time is beyond the last defined color change, and interpolates colors between the base style and the first color change, or between consecutive color changes. ```python if c4: transform = base_c4 + transform if c3: transform = base_c3 + transform if c1: transform = base_c1 + transform return transform ``` ```python def get_fr_color_change( self, line: Line, c1: bool | None = None, c3: bool | None = None, c4: bool | None = None, ) -> str: """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: .. code-block:: python3 :emphasize-lines: 5 # 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) """ # If we don't have user's settings, we set c values # to the ones that we previously saved c1 = self.c1_req if c1 is None else c1 c3 = self.c3_req if c3 is None else c3 c4 = self.c4_req if c4 is None else c4 if line.styleref is None: raise ValueError("Line has no styleref") # Reading default colors base_c1 = "\1c" + line.styleref.color1 base_c3 = "\3c" + line.styleref.color3 base_c4 = "\4c" + line.styleref.color4 # Searching valid color_change current_time = line.start_time latest_index = -1 for i, color_change in enumerate(self.color_changes): if current_time >= color_change["start"]: latest_index = i # If no color change is found, take default from style if latest_index == -1: colors = "" if c1: colors += base_c1 if c3: colors += base_c3 if c4: colors += base_c4 return colors # If we have passed the end of the lastest color change available, then take the final values of it if current_time >= self.color_changes[latest_index]["end"]: colors = "" if c1 and self.color_changes[latest_index]["c1"]: colors += self.color_changes[latest_index]["c1"] if c3 and self.color_changes[latest_index]["c3"]: colors += self.color_changes[latest_index]["c3"] if c4 and self.color_changes[latest_index]["c4"]: colors += self.color_changes[latest_index]["c4"] return colors # Else, interpolate the latest color change start = current_time - self.color_changes[latest_index]["start"] end = ( self.color_changes[latest_index]["end"] - self.color_changes[latest_index]["start"] ) pct = start / end # If we're in the first color_change, interpolate with base colors if latest_index == 0: colors = "" if c1 and self.color_changes[latest_index]["c1"]: colors += "\1c" + Utils.interpolate( pct, base_c1[3:], self.color_changes[latest_index]["c1"][3:], self.color_changes[latest_index]["acc"], ) if c3 and self.color_changes[latest_index]["c3"]: colors += "\3c" + Utils.interpolate( pct, base_c3[3:], self.color_changes[latest_index]["c3"][3:], self.color_changes[latest_index]["acc"], ) if c4 and self.color_changes[latest_index]["c4"]: colors += "\4c" + Utils.interpolate( pct, base_c4[3:], self.color_changes[latest_index]["c4"][3:], self.color_changes[latest_index]["acc"], ) return colors # Else, we interpolate between current color change and previous colors = "" if c1: colors += "\1c" + Utils.interpolate( pct, self.color_changes[latest_index - 1]["c1"][3:], self.color_changes[latest_index]["c1"][3:], ``` -------------------------------- ### Open Subtitle File with MPV Media Player Source: https://pyonfx.readthedocs.io/en/latest/_modules/pyonfx/ass_core Opens the generated subtitle file in the MPV media player, optionally linking it with a video file. This method supports hot-reloading subtitles in an existing MPV instance via IPC, launching a new MPV process, or falling back to Aegisub if MPV is not found and `aegisub_fallback` is True. It requires the subtitle file to be saved first; otherwise, it prints an error and returns False. It accepts parameters for video path, start time, full-screen mode, and additional MPV options. ```python def open_mpv( self, video_path: str | None = None, *, video_start: str | None = None, full_screen: bool = False, extra_mpv_options: list[str] = [], aegisub_fallback: bool = True, ) -> bool: """Opens the output subtitle file using MPV media player along with the associated video. This method attempts to: - Use an already running MPV instance to hot-reload subtitles via an IPC socket if detected. - Launch a new MPV process with IPC enabled if no such instance exists. - Fall back to opening the output in Aegisub if MPV is not available and aegisub_fallback is True. Parameters: video_path (str | None): The absolute path to the video file to be played. If None, the video path from meta.video is used. video_start (str | None): The starting time for video playback (e.g., "00:01:23"). If None, playback starts from the beginning. full_screen (bool): If True, launches MPV in full-screen mode; otherwise, in windowed mode. extra_mpv_options (list[str]): Additional command-line options to pass to MPV. aegisub_fallback (bool): If True, falls back to opening the output with Aegisub when MPV is not found in the system PATH. Returns: True if MPV is successfully launched or the subtitles are hot-reloaded in an existing MPV instance; False otherwise (e.g., if the output file has not been saved or MPV is unavailable). """ if not self._saved: print( "[ERROR] You've tried to open the output with MPV before having saved. Check your code." ) return False ``` -------------------------------- ### Wrap Iterable with TQDM Progress Bar (Python) Source: https://pyonfx.readthedocs.io/en/latest/_modules/pyonfx/utils Wraps an iterable of ASS objects (Lines, Words, Syllables, Chars) with a tqdm progress bar for visual feedback during processing. It dynamically sets the description and unit based on the object type and supports custom tqdm arguments. ```python import re from typing import Callable, Iterable, Literal, TypeVar import rpeasings from tqdm import tqdm from video_timestamps import ABCTimestamps, TimeType from .ass_core import Char, Line, Syllable, Word from .convert import ColorModel, Convert [docs] class Utils: """ This class is a collection of static methods that will help the user in some tasks. """ _LineWordSyllableChar = TypeVar("_LineWordSyllableChar", Line, Word, Syllable, Char) [docs] @staticmethod def progress_bar( iterable: Iterable[_LineWordSyllableChar], **kwargs ) -> Iterable[_LineWordSyllableChar]: """Wraps an iterable of Lines, Words, Syllables, or Chars with a tqdm progress bar. Args: iterable: The iterable to wrap (list of Lines, Words, Syllables, Chars). **kwargs: Additional arguments for tqdm. Returns: An iterator with a progress bar. """ # Convert to list to support multiple passes and len() items = list(iterable) if not items: raise ValueError( "Iterable is empty; cannot determine type for progress bar." ) first = items[0] obj_name = type(first).__name__.lower() if obj_name not in ("line", "word", "syllable", "char"): raise TypeError( f"with_progress only supports Line, Word, Syllable, or Char (got {type(first)})." ) emoji = { "line": "🐰", "word": "🔤", "syllable": "🎤", "char": "🔠", } return tqdm( items, desc=kwargs.pop("desc", f"Processed {obj_name}s"), unit=kwargs.pop("unit", obj_name), leave=kwargs.pop("leave", False), ascii=kwargs.pop("ascii", " ▖▘▝▗▚▞█"), bar_format=kwargs.pop( "bar_format", emoji[obj_name] + " {desc}: |{bar}| {percentage:3.0f}% [{n_fmt}/{total_fmt}] " + "⏱️ {elapsed}<{remaining}, {rate_fmt}{postfix}", ), **kwargs, ) ``` -------------------------------- ### PyonFX Utils: Progress Bar Function Source: https://pyonfx.readthedocs.io/en/latest/reference/utils Demonstrates the usage of the `Utils.progress_bar()` function from the PyonFX library. This function is used to display a progress bar, likely for long-running operations. No specific dependencies are mentioned, and it takes an iterable as input. ```python from pyonfx import Utils # Example usage of Utils.progress_bar() for item in Utils.progress_bar(range(100)): # Process item pass ``` -------------------------------- ### PyonFX Utils: Acceleration Function Source: https://pyonfx.readthedocs.io/en/latest/reference/utils Illustrates the use of the `Utils.accelerate()` function in the PyonFX library. This function is designed to accelerate a sequence or operation, though its specific parameters and return values are not detailed here. It likely takes a sequence or numerical value as input. ```python from pyonfx import Utils # Example placeholder for Utils.accelerate() # actual usage may vary based on function signature accelerated_value = Utils.accelerate(5) ```