typed-ffmpeg-compatible 2.4.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- typed_ffmpeg/__init__.py +25 -0
 - typed_ffmpeg/base.py +114 -0
 - typed_ffmpeg/common/__init__.py +0 -0
 - typed_ffmpeg/common/schema.py +308 -0
 - typed_ffmpeg/common/serialize.py +132 -0
 - typed_ffmpeg/dag/__init__.py +13 -0
 - typed_ffmpeg/dag/compile.py +51 -0
 - typed_ffmpeg/dag/context.py +221 -0
 - typed_ffmpeg/dag/factory.py +31 -0
 - typed_ffmpeg/dag/global_runnable/__init__.py +0 -0
 - typed_ffmpeg/dag/global_runnable/global_args.py +178 -0
 - typed_ffmpeg/dag/global_runnable/runnable.py +174 -0
 - typed_ffmpeg/dag/io/__init__.py +0 -0
 - typed_ffmpeg/dag/io/_input.py +197 -0
 - typed_ffmpeg/dag/io/_output.py +320 -0
 - typed_ffmpeg/dag/io/output_args.py +327 -0
 - typed_ffmpeg/dag/nodes.py +479 -0
 - typed_ffmpeg/dag/schema.py +210 -0
 - typed_ffmpeg/dag/utils.py +41 -0
 - typed_ffmpeg/dag/validate.py +172 -0
 - typed_ffmpeg/exceptions.py +42 -0
 - typed_ffmpeg/filters.py +3572 -0
 - typed_ffmpeg/probe.py +43 -0
 - typed_ffmpeg/py.typed +0 -0
 - typed_ffmpeg/schema.py +29 -0
 - typed_ffmpeg/streams/__init__.py +5 -0
 - typed_ffmpeg/streams/audio.py +7358 -0
 - typed_ffmpeg/streams/av.py +22 -0
 - typed_ffmpeg/streams/channel_layout.py +39 -0
 - typed_ffmpeg/streams/video.py +13469 -0
 - typed_ffmpeg/types.py +119 -0
 - typed_ffmpeg/utils/__init__.py +0 -0
 - typed_ffmpeg/utils/escaping.py +49 -0
 - typed_ffmpeg/utils/lazy_eval/__init__.py +0 -0
 - typed_ffmpeg/utils/lazy_eval/operator.py +134 -0
 - typed_ffmpeg/utils/lazy_eval/schema.py +211 -0
 - typed_ffmpeg/utils/run.py +27 -0
 - typed_ffmpeg/utils/snapshot.py +26 -0
 - typed_ffmpeg/utils/typing.py +17 -0
 - typed_ffmpeg/utils/view.py +64 -0
 - typed_ffmpeg_compatible-2.4.1.dist-info/LICENSE +21 -0
 - typed_ffmpeg_compatible-2.4.1.dist-info/METADATA +182 -0
 - typed_ffmpeg_compatible-2.4.1.dist-info/RECORD +45 -0
 - typed_ffmpeg_compatible-2.4.1.dist-info/WHEEL +4 -0
 - typed_ffmpeg_compatible-2.4.1.dist-info/entry_points.txt +3 -0
 
| 
         @@ -0,0 +1,41 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            from __future__ import annotations
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            from collections import deque
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            # Another approach to determine if a graph is a DAG is to try to perform a topological sort.
         
     | 
| 
      
 6 
     | 
    
         
            +
            # If the topological sort is successful (i.e., all vertices are visited exactly once),
         
     | 
| 
      
 7 
     | 
    
         
            +
            # the graph is a DAG. If the topological sort cannot include all vertices (i.e., the graph has a cycle),
         
     | 
| 
      
 8 
     | 
    
         
            +
            # it is not a DAG. Here is a basic implementation using Kahn's Algorithm:
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            def is_dag(graph: dict[str, set[str]]) -> bool:
         
     | 
| 
      
 12 
     | 
    
         
            +
                """
         
     | 
| 
      
 13 
     | 
    
         
            +
                Determine if a graph is a directed acyclic graph (DAG).
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                Args:
         
     | 
| 
      
 16 
     | 
    
         
            +
                    graph: The graph to check.
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                Returns:
         
     | 
| 
      
 19 
     | 
    
         
            +
                    Whether the graph is a DAG.
         
     | 
| 
      
 20 
     | 
    
         
            +
                """
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                in_degree = {u: 0 for u in graph}  # Initialize in-degree of each node to 0
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                # Calculate in-degree of each node
         
     | 
| 
      
 25 
     | 
    
         
            +
                for u in graph:
         
     | 
| 
      
 26 
     | 
    
         
            +
                    for v in graph[u]:
         
     | 
| 
      
 27 
     | 
    
         
            +
                        in_degree[v] += 1
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                queue = deque([u for u in graph if in_degree[u] == 0])
         
     | 
| 
      
 30 
     | 
    
         
            +
                count = 0
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                while queue:
         
     | 
| 
      
 33 
     | 
    
         
            +
                    u = queue.popleft()
         
     | 
| 
      
 34 
     | 
    
         
            +
                    count += 1
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                    for v in graph[u]:
         
     | 
| 
      
 37 
     | 
    
         
            +
                        in_degree[v] -= 1
         
     | 
| 
      
 38 
     | 
    
         
            +
                        if in_degree[v] == 0:
         
     | 
| 
      
 39 
     | 
    
         
            +
                            queue.append(v)
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                return count == len(graph)
         
     | 
| 
         @@ -0,0 +1,172 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            from __future__ import annotations
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            from dataclasses import replace
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            from ..exceptions import FFMpegValueError
         
     | 
| 
      
 6 
     | 
    
         
            +
            from ..streams.audio import AudioStream
         
     | 
| 
      
 7 
     | 
    
         
            +
            from ..streams.video import VideoStream
         
     | 
| 
      
 8 
     | 
    
         
            +
            from .context import DAGContext
         
     | 
| 
      
 9 
     | 
    
         
            +
            from .nodes import FilterNode, InputNode
         
     | 
| 
      
 10 
     | 
    
         
            +
            from .schema import Node, Stream
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            def remove_split(current_stream: Stream, mapping: dict[Stream, Stream] = None) -> tuple[Stream, dict[Stream, Stream]]:
         
     | 
| 
      
 14 
     | 
    
         
            +
                """
         
     | 
| 
      
 15 
     | 
    
         
            +
                Rebuild the graph with the given mapping.
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                Args:
         
     | 
| 
      
 18 
     | 
    
         
            +
                    current_stream: The stream to rebuild the graph with.
         
     | 
| 
      
 19 
     | 
    
         
            +
                    mapping: The mapping to rebuild the graph with.
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                Returns:
         
     | 
| 
      
 22 
     | 
    
         
            +
                    A tuple of the new node and the new mapping.
         
     | 
| 
      
 23 
     | 
    
         
            +
                """
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                # remove all split nodes
         
     | 
| 
      
 26 
     | 
    
         
            +
                # add split nodes to the graph
         
     | 
| 
      
 27 
     | 
    
         
            +
                if mapping is None:
         
     | 
| 
      
 28 
     | 
    
         
            +
                    mapping = {}
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                if current_stream in mapping:
         
     | 
| 
      
 31 
     | 
    
         
            +
                    return mapping[current_stream], mapping
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                if not current_stream.node.inputs:
         
     | 
| 
      
 34 
     | 
    
         
            +
                    mapping[current_stream] = current_stream
         
     | 
| 
      
 35 
     | 
    
         
            +
                    return current_stream, mapping
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                if isinstance(current_stream.node, FilterNode):
         
     | 
| 
      
 38 
     | 
    
         
            +
                    # if the current node is a split node, we need to remove it
         
     | 
| 
      
 39 
     | 
    
         
            +
                    if current_stream.node.name in ("split", "asplit"):
         
     | 
| 
      
 40 
     | 
    
         
            +
                        new_stream, _mapping = remove_split(current_stream=current_stream.node.inputs[0], mapping=mapping)
         
     | 
| 
      
 41 
     | 
    
         
            +
                        mapping[current_stream] = mapping[current_stream.node.inputs[0]]
         
     | 
| 
      
 42 
     | 
    
         
            +
                        return mapping[current_stream.node.inputs[0]], mapping
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                inputs = {}
         
     | 
| 
      
 45 
     | 
    
         
            +
                for idx, input_stream in sorted(
         
     | 
| 
      
 46 
     | 
    
         
            +
                    enumerate(current_stream.node.inputs), key=lambda idx_stream: -len(idx_stream[1].node.upstream_nodes)
         
     | 
| 
      
 47 
     | 
    
         
            +
                ):
         
     | 
| 
      
 48 
     | 
    
         
            +
                    new_stream, _mapping = remove_split(current_stream=input_stream, mapping=mapping)
         
     | 
| 
      
 49 
     | 
    
         
            +
                    inputs[idx] = new_stream
         
     | 
| 
      
 50 
     | 
    
         
            +
                    mapping |= _mapping
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                new_node = replace(
         
     | 
| 
      
 53 
     | 
    
         
            +
                    current_stream.node, inputs=tuple(stream for idx, stream in sorted(inputs.items(), key=lambda x: x[0]))
         
     | 
| 
      
 54 
     | 
    
         
            +
                )
         
     | 
| 
      
 55 
     | 
    
         
            +
                new_stream = replace(current_stream, node=new_node)
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                mapping[current_stream] = new_stream
         
     | 
| 
      
 58 
     | 
    
         
            +
                return new_stream, mapping
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
            def add_split(
         
     | 
| 
      
 62 
     | 
    
         
            +
                current_stream: Stream,
         
     | 
| 
      
 63 
     | 
    
         
            +
                down_node: Node = None,
         
     | 
| 
      
 64 
     | 
    
         
            +
                down_index: int = None,
         
     | 
| 
      
 65 
     | 
    
         
            +
                context: DAGContext = None,
         
     | 
| 
      
 66 
     | 
    
         
            +
                mapping: dict[tuple[Stream, Node | None, int | None], Stream] = None,
         
     | 
| 
      
 67 
     | 
    
         
            +
            ) -> tuple[Stream, dict[tuple[Stream, Node | None, int | None], Stream]]:
         
     | 
| 
      
 68 
     | 
    
         
            +
                """
         
     | 
| 
      
 69 
     | 
    
         
            +
                Add split nodes to the graph.
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                Args:
         
     | 
| 
      
 72 
     | 
    
         
            +
                    current_stream: The stream to add split nodes to.
         
     | 
| 
      
 73 
     | 
    
         
            +
                    down_node: The node use current_stream as input.
         
     | 
| 
      
 74 
     | 
    
         
            +
                    down_index: The index of the input stream in down_node.
         
     | 
| 
      
 75 
     | 
    
         
            +
                    context: The DAG context.
         
     | 
| 
      
 76 
     | 
    
         
            +
                    mapping: The mapping to add split nodes to.
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                Returns:
         
     | 
| 
      
 79 
     | 
    
         
            +
                    A tuple of the new node and the new mapping.
         
     | 
| 
      
 80 
     | 
    
         
            +
                """
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                if not context:
         
     | 
| 
      
 83 
     | 
    
         
            +
                    context = DAGContext.build(current_stream.node)
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                if mapping is None:
         
     | 
| 
      
 86 
     | 
    
         
            +
                    mapping = {}
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                if (current_stream, down_node, down_index) in mapping:
         
     | 
| 
      
 89 
     | 
    
         
            +
                    return mapping[(current_stream, down_node, down_index)], mapping
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                inputs = {}
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                for idx, input_stream in sorted(
         
     | 
| 
      
 94 
     | 
    
         
            +
                    enumerate(current_stream.node.inputs), key=lambda idx_stream: -len(idx_stream[1].node.upstream_nodes)
         
     | 
| 
      
 95 
     | 
    
         
            +
                ):
         
     | 
| 
      
 96 
     | 
    
         
            +
                    new_stream, _mapping = add_split(
         
     | 
| 
      
 97 
     | 
    
         
            +
                        current_stream=input_stream, down_node=current_stream.node, down_index=idx, mapping=mapping, context=context
         
     | 
| 
      
 98 
     | 
    
         
            +
                    )
         
     | 
| 
      
 99 
     | 
    
         
            +
                    inputs[idx] = new_stream
         
     | 
| 
      
 100 
     | 
    
         
            +
                    mapping |= _mapping
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
                new_node = replace(
         
     | 
| 
      
 103 
     | 
    
         
            +
                    current_stream.node, inputs=tuple(stream for idx, stream in sorted(inputs.items(), key=lambda x: x[0]))
         
     | 
| 
      
 104 
     | 
    
         
            +
                )
         
     | 
| 
      
 105 
     | 
    
         
            +
                new_stream = replace(current_stream, node=new_node)
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
                num = len(context.get_outgoing_nodes(current_stream))
         
     | 
| 
      
 108 
     | 
    
         
            +
                if num < 2:
         
     | 
| 
      
 109 
     | 
    
         
            +
                    mapping[(current_stream, down_node, down_index)] = new_stream
         
     | 
| 
      
 110 
     | 
    
         
            +
                    return new_stream, mapping
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
                if isinstance(current_stream.node, InputNode):
         
     | 
| 
      
 113 
     | 
    
         
            +
                    for idx, (node, index) in enumerate(context.get_outgoing_nodes(current_stream)):
         
     | 
| 
      
 114 
     | 
    
         
            +
                        # if the current node is InputNode, we don't need to split it
         
     | 
| 
      
 115 
     | 
    
         
            +
                        mapping[(current_stream, node, index)] = new_stream
         
     | 
| 
      
 116 
     | 
    
         
            +
                    return new_stream, mapping
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
                if isinstance(new_stream, VideoStream):
         
     | 
| 
      
 119 
     | 
    
         
            +
                    split_node = new_stream.split(outputs=num)
         
     | 
| 
      
 120 
     | 
    
         
            +
                    for idx, (node, index) in enumerate(context.get_outgoing_nodes(current_stream)):
         
     | 
| 
      
 121 
     | 
    
         
            +
                        mapping[(current_stream, node, index)] = split_node.video(idx)
         
     | 
| 
      
 122 
     | 
    
         
            +
                    return mapping[(current_stream, down_node, down_index)], mapping
         
     | 
| 
      
 123 
     | 
    
         
            +
                elif isinstance(new_stream, AudioStream):
         
     | 
| 
      
 124 
     | 
    
         
            +
                    split_node = new_stream.asplit(outputs=num)
         
     | 
| 
      
 125 
     | 
    
         
            +
                    for idx, (node, index) in enumerate(context.get_outgoing_nodes(current_stream)):
         
     | 
| 
      
 126 
     | 
    
         
            +
                        mapping[(current_stream, node, index)] = split_node.audio(idx)
         
     | 
| 
      
 127 
     | 
    
         
            +
                    return mapping[(current_stream, down_node, down_index)], mapping
         
     | 
| 
      
 128 
     | 
    
         
            +
                else:
         
     | 
| 
      
 129 
     | 
    
         
            +
                    raise FFMpegValueError(f"unsupported stream type: {current_stream}")
         
     | 
| 
      
 130 
     | 
    
         
            +
             
     | 
| 
      
 131 
     | 
    
         
            +
             
     | 
| 
      
 132 
     | 
    
         
            +
            def fix_graph(stream: Stream) -> Stream:
         
     | 
| 
      
 133 
     | 
    
         
            +
                """
         
     | 
| 
      
 134 
     | 
    
         
            +
                Fix the graph by removing and adding split nodes.
         
     | 
| 
      
 135 
     | 
    
         
            +
             
     | 
| 
      
 136 
     | 
    
         
            +
                Args:
         
     | 
| 
      
 137 
     | 
    
         
            +
                    stream: The stream to fix.
         
     | 
| 
      
 138 
     | 
    
         
            +
             
     | 
| 
      
 139 
     | 
    
         
            +
                Returns:
         
     | 
| 
      
 140 
     | 
    
         
            +
                    The fixed stream.
         
     | 
| 
      
 141 
     | 
    
         
            +
             
     | 
| 
      
 142 
     | 
    
         
            +
                Note:
         
     | 
| 
      
 143 
     | 
    
         
            +
                    Fix the graph by resetting split nodes.
         
     | 
| 
      
 144 
     | 
    
         
            +
                    This function is for internal use only.
         
     | 
| 
      
 145 
     | 
    
         
            +
                """
         
     | 
| 
      
 146 
     | 
    
         
            +
             
     | 
| 
      
 147 
     | 
    
         
            +
                stream, _ = remove_split(stream)
         
     | 
| 
      
 148 
     | 
    
         
            +
                stream, _ = add_split(stream)
         
     | 
| 
      
 149 
     | 
    
         
            +
                return stream
         
     | 
| 
      
 150 
     | 
    
         
            +
             
     | 
| 
      
 151 
     | 
    
         
            +
             
     | 
| 
      
 152 
     | 
    
         
            +
            def validate(stream: Stream, auto_fix: bool = True) -> Stream:
         
     | 
| 
      
 153 
     | 
    
         
            +
                """
         
     | 
| 
      
 154 
     | 
    
         
            +
                Validate the given DAG. If auto_fix is True, the graph will be automatically fixed to follow ffmpeg's rule.
         
     | 
| 
      
 155 
     | 
    
         
            +
             
     | 
| 
      
 156 
     | 
    
         
            +
                Args:
         
     | 
| 
      
 157 
     | 
    
         
            +
                    stream: The DAG to validate.
         
     | 
| 
      
 158 
     | 
    
         
            +
                    auto_fix: Whether to automatically fix the graph.
         
     | 
| 
      
 159 
     | 
    
         
            +
             
     | 
| 
      
 160 
     | 
    
         
            +
                Returns:
         
     | 
| 
      
 161 
     | 
    
         
            +
                    The validated DAG context.
         
     | 
| 
      
 162 
     | 
    
         
            +
                """
         
     | 
| 
      
 163 
     | 
    
         
            +
                if auto_fix:
         
     | 
| 
      
 164 
     | 
    
         
            +
                    stream = fix_graph(stream)
         
     | 
| 
      
 165 
     | 
    
         
            +
             
     | 
| 
      
 166 
     | 
    
         
            +
                # NOTE: we don't want to modify the original node
         
     | 
| 
      
 167 
     | 
    
         
            +
                # validators: list[] = []
         
     | 
| 
      
 168 
     | 
    
         
            +
             
     | 
| 
      
 169 
     | 
    
         
            +
                # for validator in validators:
         
     | 
| 
      
 170 
     | 
    
         
            +
                #     context = validator(context)
         
     | 
| 
      
 171 
     | 
    
         
            +
             
     | 
| 
      
 172 
     | 
    
         
            +
                return stream
         
     | 
| 
         @@ -0,0 +1,42 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class FFMpegError(Exception):
         
     | 
| 
      
 2 
     | 
    
         
            +
                """
         
     | 
| 
      
 3 
     | 
    
         
            +
                Base exception for all ffmpeg errors.
         
     | 
| 
      
 4 
     | 
    
         
            +
                """
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                ...
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            class FFMpegTypeError(FFMpegError, TypeError):
         
     | 
| 
      
 10 
     | 
    
         
            +
                """
         
     | 
| 
      
 11 
     | 
    
         
            +
                Base exception for all ffmpeg type errors.
         
     | 
| 
      
 12 
     | 
    
         
            +
                """
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            class FFMpegValueError(FFMpegError, ValueError):
         
     | 
| 
      
 16 
     | 
    
         
            +
                """
         
     | 
| 
      
 17 
     | 
    
         
            +
                Base exception for all ffmpeg value errors.
         
     | 
| 
      
 18 
     | 
    
         
            +
                """
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
            class FFMpegExecuteError(FFMpegError):
         
     | 
| 
      
 22 
     | 
    
         
            +
                """
         
     | 
| 
      
 23 
     | 
    
         
            +
                FFmpeg error
         
     | 
| 
      
 24 
     | 
    
         
            +
                """
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                def __init__(self, retcode: int | None, cmd: str, stdout: bytes, stderr: bytes):
         
     | 
| 
      
 27 
     | 
    
         
            +
                    """
         
     | 
| 
      
 28 
     | 
    
         
            +
                    Initialize the exception.
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                    Args:
         
     | 
| 
      
 31 
     | 
    
         
            +
                        retcode: The return code of the command.
         
     | 
| 
      
 32 
     | 
    
         
            +
                        cmd: The command that was run.
         
     | 
| 
      
 33 
     | 
    
         
            +
                        stdout: The stdout of the command.
         
     | 
| 
      
 34 
     | 
    
         
            +
                        stderr: The stderr of the command.
         
     | 
| 
      
 35 
     | 
    
         
            +
                    """
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                    self.stdout = stdout
         
     | 
| 
      
 38 
     | 
    
         
            +
                    self.stderr = stderr
         
     | 
| 
      
 39 
     | 
    
         
            +
                    self.cmd = cmd
         
     | 
| 
      
 40 
     | 
    
         
            +
                    self.retcode = retcode
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                    super(FFMpegExecuteError, self).__init__(f"{cmd} error (see stderr output for detail) {stderr!r}")
         
     |