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
 
    
        typed_ffmpeg/__init__.py
    ADDED
    
    | 
         @@ -0,0 +1,25 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            from . import dag, filters
         
     | 
| 
      
 2 
     | 
    
         
            +
            from .base import afilter, filter_multi_output, input, merge_outputs, output, vfilter
         
     | 
| 
      
 3 
     | 
    
         
            +
            from .dag import Stream
         
     | 
| 
      
 4 
     | 
    
         
            +
            from .exceptions import FFMpegExecuteError, FFMpegTypeError, FFMpegValueError
         
     | 
| 
      
 5 
     | 
    
         
            +
            from .probe import probe
         
     | 
| 
      
 6 
     | 
    
         
            +
            from .streams import AudioStream, AVStream, VideoStream
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            __all__ = [
         
     | 
| 
      
 9 
     | 
    
         
            +
                "filters",
         
     | 
| 
      
 10 
     | 
    
         
            +
                "input",
         
     | 
| 
      
 11 
     | 
    
         
            +
                "output",
         
     | 
| 
      
 12 
     | 
    
         
            +
                "merge_outputs",
         
     | 
| 
      
 13 
     | 
    
         
            +
                "FFMpegExecuteError",
         
     | 
| 
      
 14 
     | 
    
         
            +
                "FFMpegTypeError",
         
     | 
| 
      
 15 
     | 
    
         
            +
                "FFMpegValueError",
         
     | 
| 
      
 16 
     | 
    
         
            +
                "Stream",
         
     | 
| 
      
 17 
     | 
    
         
            +
                "probe",
         
     | 
| 
      
 18 
     | 
    
         
            +
                "AudioStream",
         
     | 
| 
      
 19 
     | 
    
         
            +
                "VideoStream",
         
     | 
| 
      
 20 
     | 
    
         
            +
                "AVStream",
         
     | 
| 
      
 21 
     | 
    
         
            +
                "vfilter",
         
     | 
| 
      
 22 
     | 
    
         
            +
                "afilter",
         
     | 
| 
      
 23 
     | 
    
         
            +
                "filter_multi_output",
         
     | 
| 
      
 24 
     | 
    
         
            +
                "dag",
         
     | 
| 
      
 25 
     | 
    
         
            +
            ]
         
     | 
    
        typed_ffmpeg/base.py
    ADDED
    
    | 
         @@ -0,0 +1,114 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            """
         
     | 
| 
      
 2 
     | 
    
         
            +
            This module defined the basic functions for creating the ffmpeg filter graph.
         
     | 
| 
      
 3 
     | 
    
         
            +
            """
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            from typing import Any
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            from .dag.io._input import input
         
     | 
| 
      
 8 
     | 
    
         
            +
            from .dag.io._output import output
         
     | 
| 
      
 9 
     | 
    
         
            +
            from .dag.nodes import FilterableStream, FilterNode, GlobalNode, GlobalStream, OutputStream
         
     | 
| 
      
 10 
     | 
    
         
            +
            from .schema import StreamType
         
     | 
| 
      
 11 
     | 
    
         
            +
            from .streams.audio import AudioStream
         
     | 
| 
      
 12 
     | 
    
         
            +
            from .streams.video import VideoStream
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            def merge_outputs(*streams: OutputStream) -> GlobalStream:
         
     | 
| 
      
 16 
     | 
    
         
            +
                """
         
     | 
| 
      
 17 
     | 
    
         
            +
                Merge multiple output streams into one.
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                Args:
         
     | 
| 
      
 20 
     | 
    
         
            +
                    *streams: The output streams to merge.
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                Returns:
         
     | 
| 
      
 23 
     | 
    
         
            +
                    The merged output stream.
         
     | 
| 
      
 24 
     | 
    
         
            +
                """
         
     | 
| 
      
 25 
     | 
    
         
            +
                return GlobalNode(inputs=streams).stream()
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
            def vfilter(
         
     | 
| 
      
 29 
     | 
    
         
            +
                *streams: FilterableStream, name: str, input_typings: tuple[StreamType, ...] = (StreamType.video,), **kwargs: Any
         
     | 
| 
      
 30 
     | 
    
         
            +
            ) -> VideoStream:
         
     | 
| 
      
 31 
     | 
    
         
            +
                """
         
     | 
| 
      
 32 
     | 
    
         
            +
                Apply a custom video filter which has only one output to this stream
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                Args:
         
     | 
| 
      
 35 
     | 
    
         
            +
                    *streams: the streams to apply the filter to
         
     | 
| 
      
 36 
     | 
    
         
            +
                    name: the name of the filter
         
     | 
| 
      
 37 
     | 
    
         
            +
                    input_typings: the input typings of the filter
         
     | 
| 
      
 38 
     | 
    
         
            +
                    **kwargs: the arguments for the filter
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                Returns:
         
     | 
| 
      
 41 
     | 
    
         
            +
                    the output stream
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                Note:
         
     | 
| 
      
 44 
     | 
    
         
            +
                    This function is for custom filter which is not implemented in typed-ffmpeg
         
     | 
| 
      
 45 
     | 
    
         
            +
                """
         
     | 
| 
      
 46 
     | 
    
         
            +
                return FilterNode(
         
     | 
| 
      
 47 
     | 
    
         
            +
                    name=name,
         
     | 
| 
      
 48 
     | 
    
         
            +
                    inputs=streams,
         
     | 
| 
      
 49 
     | 
    
         
            +
                    output_typings=(StreamType.video,),
         
     | 
| 
      
 50 
     | 
    
         
            +
                    input_typings=input_typings,
         
     | 
| 
      
 51 
     | 
    
         
            +
                    kwargs=tuple(kwargs.items()),
         
     | 
| 
      
 52 
     | 
    
         
            +
                ).video(0)
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
            def afilter(
         
     | 
| 
      
 56 
     | 
    
         
            +
                *streams: FilterableStream, name: str, input_typings: tuple[StreamType, ...] = (StreamType.audio,), **kwargs: Any
         
     | 
| 
      
 57 
     | 
    
         
            +
            ) -> AudioStream:
         
     | 
| 
      
 58 
     | 
    
         
            +
                """
         
     | 
| 
      
 59 
     | 
    
         
            +
                Apply a custom audio filter which has only one output to this stream
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                Args:
         
     | 
| 
      
 62 
     | 
    
         
            +
                    *streams: the streams to apply the filter to
         
     | 
| 
      
 63 
     | 
    
         
            +
                    name: the name of the filter
         
     | 
| 
      
 64 
     | 
    
         
            +
                    input_typings: the input typings of the filter
         
     | 
| 
      
 65 
     | 
    
         
            +
                    **kwargs: the arguments for the filter
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                Returns:
         
     | 
| 
      
 68 
     | 
    
         
            +
                    the output stream
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                Note:
         
     | 
| 
      
 71 
     | 
    
         
            +
                    This function is for custom filter which is not implemented in typed-ffmpeg
         
     | 
| 
      
 72 
     | 
    
         
            +
                """
         
     | 
| 
      
 73 
     | 
    
         
            +
                return FilterNode(
         
     | 
| 
      
 74 
     | 
    
         
            +
                    name=name,
         
     | 
| 
      
 75 
     | 
    
         
            +
                    inputs=streams,
         
     | 
| 
      
 76 
     | 
    
         
            +
                    output_typings=(StreamType.audio,),
         
     | 
| 
      
 77 
     | 
    
         
            +
                    input_typings=input_typings,
         
     | 
| 
      
 78 
     | 
    
         
            +
                    kwargs=tuple(kwargs.items()),
         
     | 
| 
      
 79 
     | 
    
         
            +
                ).audio(0)
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
            def filter_multi_output(
         
     | 
| 
      
 83 
     | 
    
         
            +
                *streams: FilterableStream,
         
     | 
| 
      
 84 
     | 
    
         
            +
                name: str,
         
     | 
| 
      
 85 
     | 
    
         
            +
                input_typings: tuple[StreamType, ...] = (),
         
     | 
| 
      
 86 
     | 
    
         
            +
                output_tyings: tuple[StreamType, ...] = (),
         
     | 
| 
      
 87 
     | 
    
         
            +
                **kwargs: Any
         
     | 
| 
      
 88 
     | 
    
         
            +
            ) -> FilterNode:
         
     | 
| 
      
 89 
     | 
    
         
            +
                """
         
     | 
| 
      
 90 
     | 
    
         
            +
                Apply a custom filter which has multiple outputs to this stream
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                Args:
         
     | 
| 
      
 93 
     | 
    
         
            +
                    *streams: the streams to apply the filter to
         
     | 
| 
      
 94 
     | 
    
         
            +
                    name: the name of the filter
         
     | 
| 
      
 95 
     | 
    
         
            +
                    input_typings: the input typings of the filter
         
     | 
| 
      
 96 
     | 
    
         
            +
                    output_tyings: the output typings of the filter
         
     | 
| 
      
 97 
     | 
    
         
            +
                    **kwargs: the arguments for the filter
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                Returns:
         
     | 
| 
      
 100 
     | 
    
         
            +
                    the FilterNode
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
                Note:
         
     | 
| 
      
 103 
     | 
    
         
            +
                    This function is for custom filter which is not implemented in typed-ffmpeg
         
     | 
| 
      
 104 
     | 
    
         
            +
                """
         
     | 
| 
      
 105 
     | 
    
         
            +
                return FilterNode(
         
     | 
| 
      
 106 
     | 
    
         
            +
                    name=name,
         
     | 
| 
      
 107 
     | 
    
         
            +
                    kwargs=tuple(kwargs.items()),
         
     | 
| 
      
 108 
     | 
    
         
            +
                    inputs=streams,
         
     | 
| 
      
 109 
     | 
    
         
            +
                    input_typings=input_typings,
         
     | 
| 
      
 110 
     | 
    
         
            +
                    output_typings=output_tyings,
         
     | 
| 
      
 111 
     | 
    
         
            +
                )
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
            __all__ = ["input", "output", "merge_outputs", "vfilter", "afilter", "filter_multi_output"]
         
     | 
| 
         
            File without changes
         
     | 
| 
         @@ -0,0 +1,308 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            from __future__ import annotations
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            from dataclasses import dataclass
         
     | 
| 
      
 4 
     | 
    
         
            +
            from enum import Enum
         
     | 
| 
      
 5 
     | 
    
         
            +
            from typing import Literal
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            class StreamType(str, Enum):
         
     | 
| 
      
 9 
     | 
    
         
            +
                """
         
     | 
| 
      
 10 
     | 
    
         
            +
                The type of a stream. (audio or video)
         
     | 
| 
      
 11 
     | 
    
         
            +
                """
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                audio = "audio"
         
     | 
| 
      
 14 
     | 
    
         
            +
                """it is an audio stream"""
         
     | 
| 
      
 15 
     | 
    
         
            +
                video = "video"
         
     | 
| 
      
 16 
     | 
    
         
            +
                """it is a video stream"""
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            class FFMpegFilterOptionType(str, Enum):
         
     | 
| 
      
 20 
     | 
    
         
            +
                boolean = "boolean"
         
     | 
| 
      
 21 
     | 
    
         
            +
                duration = "duration"
         
     | 
| 
      
 22 
     | 
    
         
            +
                color = "color"
         
     | 
| 
      
 23 
     | 
    
         
            +
                flags = "flags"
         
     | 
| 
      
 24 
     | 
    
         
            +
                dictionary = "dictionary"
         
     | 
| 
      
 25 
     | 
    
         
            +
                pix_fmt = "pix_fmt"
         
     | 
| 
      
 26 
     | 
    
         
            +
                int = "int"
         
     | 
| 
      
 27 
     | 
    
         
            +
                int64 = "int64"
         
     | 
| 
      
 28 
     | 
    
         
            +
                double = "double"
         
     | 
| 
      
 29 
     | 
    
         
            +
                float = "float"
         
     | 
| 
      
 30 
     | 
    
         
            +
                string = "string"
         
     | 
| 
      
 31 
     | 
    
         
            +
                video_rate = "video_rate"
         
     | 
| 
      
 32 
     | 
    
         
            +
                image_size = "image_size"
         
     | 
| 
      
 33 
     | 
    
         
            +
                rational = "rational"
         
     | 
| 
      
 34 
     | 
    
         
            +
                sample_fmt = "sample_fmt"
         
     | 
| 
      
 35 
     | 
    
         
            +
                binary = "binary"
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
            class FFMpegFilterType(str, Enum):
         
     | 
| 
      
 39 
     | 
    
         
            +
                af = "af"
         
     | 
| 
      
 40 
     | 
    
         
            +
                asrc = "asrc"
         
     | 
| 
      
 41 
     | 
    
         
            +
                asink = "asink"
         
     | 
| 
      
 42 
     | 
    
         
            +
                vf = "vf"
         
     | 
| 
      
 43 
     | 
    
         
            +
                vsrc = "vsrc"
         
     | 
| 
      
 44 
     | 
    
         
            +
                vsink = "vsink"
         
     | 
| 
      
 45 
     | 
    
         
            +
                avsrc = "avsrc"
         
     | 
| 
      
 46 
     | 
    
         
            +
                avf = "avf"
         
     | 
| 
      
 47 
     | 
    
         
            +
                vaf = "vaf"
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
            @dataclass(frozen=True, kw_only=True)
         
     | 
| 
      
 51 
     | 
    
         
            +
            class FFMpegFilterOptionChoice:
         
     | 
| 
      
 52 
     | 
    
         
            +
                name: str
         
     | 
| 
      
 53 
     | 
    
         
            +
                help: str
         
     | 
| 
      
 54 
     | 
    
         
            +
                value: str | int
         
     | 
| 
      
 55 
     | 
    
         
            +
                flags: str | None = None
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
            @dataclass(frozen=True, kw_only=True)
         
     | 
| 
      
 59 
     | 
    
         
            +
            class FFMpegFilterOption:
         
     | 
| 
      
 60 
     | 
    
         
            +
                name: str
         
     | 
| 
      
 61 
     | 
    
         
            +
                alias: tuple[str, ...] = ()
         
     | 
| 
      
 62 
     | 
    
         
            +
                description: str
         
     | 
| 
      
 63 
     | 
    
         
            +
                type: FFMpegFilterOptionType
         
     | 
| 
      
 64 
     | 
    
         
            +
                min: str | None = None
         
     | 
| 
      
 65 
     | 
    
         
            +
                max: str | None = None
         
     | 
| 
      
 66 
     | 
    
         
            +
                default: bool | int | float | str | None = None
         
     | 
| 
      
 67 
     | 
    
         
            +
                required: bool = False
         
     | 
| 
      
 68 
     | 
    
         
            +
                choices: tuple[FFMpegFilterOptionChoice, ...] = ()
         
     | 
| 
      
 69 
     | 
    
         
            +
                flags: str | None = None
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
            @dataclass(frozen=True, kw_only=True)
         
     | 
| 
      
 73 
     | 
    
         
            +
            class FFMpegIOType:
         
     | 
| 
      
 74 
     | 
    
         
            +
                name: str | None = None
         
     | 
| 
      
 75 
     | 
    
         
            +
                type: StreamType
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
            @dataclass(frozen=True, kw_only=True)
         
     | 
| 
      
 79 
     | 
    
         
            +
            class FFMpegFilterDef:
         
     | 
| 
      
 80 
     | 
    
         
            +
                name: str
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                typings_input: str | tuple[Literal["video", "audio"], ...] = ()
         
     | 
| 
      
 83 
     | 
    
         
            +
                typings_output: str | tuple[Literal["video", "audio"], ...] = ()
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
            @dataclass(frozen=True, kw_only=True)
         
     | 
| 
      
 87 
     | 
    
         
            +
            class FFMpegFilter:
         
     | 
| 
      
 88 
     | 
    
         
            +
                id: str | None = None
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                name: str
         
     | 
| 
      
 91 
     | 
    
         
            +
                description: str
         
     | 
| 
      
 92 
     | 
    
         
            +
                ref: str | None = None
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                # Flags
         
     | 
| 
      
 95 
     | 
    
         
            +
                is_support_slice_threading: bool | None = None
         
     | 
| 
      
 96 
     | 
    
         
            +
                is_support_timeline: bool | None = None
         
     | 
| 
      
 97 
     | 
    
         
            +
                is_support_framesync: bool | None = None
         
     | 
| 
      
 98 
     | 
    
         
            +
                is_support_command: bool | None = None
         
     | 
| 
      
 99 
     | 
    
         
            +
                is_filter_sink: bool | None = None
         
     | 
| 
      
 100 
     | 
    
         
            +
                is_filter_source: bool | None = None
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
                # IO Typing
         
     | 
| 
      
 103 
     | 
    
         
            +
                is_dynamic_input: bool = False
         
     | 
| 
      
 104 
     | 
    
         
            +
                is_dynamic_output: bool = False
         
     | 
| 
      
 105 
     | 
    
         
            +
                stream_typings_input: tuple[FFMpegIOType, ...] = ()
         
     | 
| 
      
 106 
     | 
    
         
            +
                stream_typings_output: tuple[FFMpegIOType, ...] = ()
         
     | 
| 
      
 107 
     | 
    
         
            +
                formula_typings_input: str | None = None
         
     | 
| 
      
 108 
     | 
    
         
            +
                formula_typings_output: str | None = None
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
                pre: tuple[tuple[str, str], ...] = ()
         
     | 
| 
      
 111 
     | 
    
         
            +
                options: tuple[FFMpegFilterOption, ...] = ()
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
                @property
         
     | 
| 
      
 114 
     | 
    
         
            +
                def pre_dict(self) -> dict[str, str]:
         
     | 
| 
      
 115 
     | 
    
         
            +
                    return dict(self.pre)
         
     | 
| 
      
 116 
     | 
    
         
            +
             
     | 
| 
      
 117 
     | 
    
         
            +
                @property
         
     | 
| 
      
 118 
     | 
    
         
            +
                def to_def(self) -> FFMpegFilterDef:
         
     | 
| 
      
 119 
     | 
    
         
            +
                    return FFMpegFilterDef(
         
     | 
| 
      
 120 
     | 
    
         
            +
                        name=self.name,
         
     | 
| 
      
 121 
     | 
    
         
            +
                        typings_input=self.formula_typings_input or tuple(k.type.value for k in self.stream_typings_input),
         
     | 
| 
      
 122 
     | 
    
         
            +
                        typings_output=self.formula_typings_output or tuple(k.type.value for k in self.stream_typings_output),
         
     | 
| 
      
 123 
     | 
    
         
            +
                    )
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
                @property
         
     | 
| 
      
 126 
     | 
    
         
            +
                def input_typings(self) -> set[StreamType]:
         
     | 
| 
      
 127 
     | 
    
         
            +
                    if self.is_filter_source:
         
     | 
| 
      
 128 
     | 
    
         
            +
                        return set()
         
     | 
| 
      
 129 
     | 
    
         
            +
                    if not self.is_dynamic_input:
         
     | 
| 
      
 130 
     | 
    
         
            +
                        return {i.type for i in self.stream_typings_input}
         
     | 
| 
      
 131 
     | 
    
         
            +
                    else:
         
     | 
| 
      
 132 
     | 
    
         
            +
                        assert self.formula_typings_input, f"{self.name} has no input"
         
     | 
| 
      
 133 
     | 
    
         
            +
                        if "video" not in self.formula_typings_input:
         
     | 
| 
      
 134 
     | 
    
         
            +
                            assert "audio" in self.formula_typings_input, f"{self.name} has no video input"
         
     | 
| 
      
 135 
     | 
    
         
            +
                            return {StreamType.audio}
         
     | 
| 
      
 136 
     | 
    
         
            +
                        elif "audio" not in self.formula_typings_input:
         
     | 
| 
      
 137 
     | 
    
         
            +
                            assert "video" in self.formula_typings_input, f"{self.name} has no audio input"
         
     | 
| 
      
 138 
     | 
    
         
            +
                            return {StreamType.video}
         
     | 
| 
      
 139 
     | 
    
         
            +
                        assert (
         
     | 
| 
      
 140 
     | 
    
         
            +
                            "video" in self.formula_typings_input and "audio" in self.formula_typings_input
         
     | 
| 
      
 141 
     | 
    
         
            +
                        ), f"{self.name} has no video or audio input"
         
     | 
| 
      
 142 
     | 
    
         
            +
                        return {StreamType.video, StreamType.audio}
         
     | 
| 
      
 143 
     | 
    
         
            +
             
     | 
| 
      
 144 
     | 
    
         
            +
                @property
         
     | 
| 
      
 145 
     | 
    
         
            +
                def output_typings(self) -> set[StreamType]:
         
     | 
| 
      
 146 
     | 
    
         
            +
                    if self.is_filter_sink:
         
     | 
| 
      
 147 
     | 
    
         
            +
                        return set()
         
     | 
| 
      
 148 
     | 
    
         
            +
                    if not self.is_dynamic_output:
         
     | 
| 
      
 149 
     | 
    
         
            +
                        return {i.type for i in self.stream_typings_output}
         
     | 
| 
      
 150 
     | 
    
         
            +
                    else:
         
     | 
| 
      
 151 
     | 
    
         
            +
                        assert self.formula_typings_output, f"{self.name} has no output"
         
     | 
| 
      
 152 
     | 
    
         
            +
                        if "video" not in self.formula_typings_output:
         
     | 
| 
      
 153 
     | 
    
         
            +
                            assert "audio" in self.formula_typings_output, f"{self.name} has no video output"
         
     | 
| 
      
 154 
     | 
    
         
            +
                            return {StreamType.audio}
         
     | 
| 
      
 155 
     | 
    
         
            +
                        elif "audio" not in self.formula_typings_output:
         
     | 
| 
      
 156 
     | 
    
         
            +
                            assert "video" in self.formula_typings_output, f"{self.name} has no audio output"
         
     | 
| 
      
 157 
     | 
    
         
            +
                            return {StreamType.video}
         
     | 
| 
      
 158 
     | 
    
         
            +
                        assert (
         
     | 
| 
      
 159 
     | 
    
         
            +
                            "video" in self.formula_typings_output and "audio" in self.formula_typings_output
         
     | 
| 
      
 160 
     | 
    
         
            +
                        ), f"{self.name} has no video or audio output"
         
     | 
| 
      
 161 
     | 
    
         
            +
                        return {StreamType.video, StreamType.audio}
         
     | 
| 
      
 162 
     | 
    
         
            +
             
     | 
| 
      
 163 
     | 
    
         
            +
                @property
         
     | 
| 
      
 164 
     | 
    
         
            +
                def filter_type(self) -> FFMpegFilterType:
         
     | 
| 
      
 165 
     | 
    
         
            +
                    if self.is_filter_sink:
         
     | 
| 
      
 166 
     | 
    
         
            +
                        assert len(self.input_typings) == 1
         
     | 
| 
      
 167 
     | 
    
         
            +
                        if {StreamType.video} == self.input_typings:
         
     | 
| 
      
 168 
     | 
    
         
            +
                            return FFMpegFilterType.vsink
         
     | 
| 
      
 169 
     | 
    
         
            +
                        if {StreamType.audio} == self.input_typings:
         
     | 
| 
      
 170 
     | 
    
         
            +
                            return FFMpegFilterType.asink
         
     | 
| 
      
 171 
     | 
    
         
            +
                    elif self.is_filter_source:
         
     | 
| 
      
 172 
     | 
    
         
            +
                        if {StreamType.video, StreamType.audio} == self.output_typings:
         
     | 
| 
      
 173 
     | 
    
         
            +
                            return FFMpegFilterType.avsrc
         
     | 
| 
      
 174 
     | 
    
         
            +
                        if {StreamType.video} == self.output_typings:
         
     | 
| 
      
 175 
     | 
    
         
            +
                            return FFMpegFilterType.vsrc
         
     | 
| 
      
 176 
     | 
    
         
            +
                        if {StreamType.audio} == self.output_typings:
         
     | 
| 
      
 177 
     | 
    
         
            +
                            return FFMpegFilterType.asrc
         
     | 
| 
      
 178 
     | 
    
         
            +
             
     | 
| 
      
 179 
     | 
    
         
            +
                    assert self.input_typings
         
     | 
| 
      
 180 
     | 
    
         
            +
             
     | 
| 
      
 181 
     | 
    
         
            +
                    if self.input_typings == {StreamType.video}:
         
     | 
| 
      
 182 
     | 
    
         
            +
                        if StreamType.audio in self.output_typings:
         
     | 
| 
      
 183 
     | 
    
         
            +
                            return FFMpegFilterType.vaf
         
     | 
| 
      
 184 
     | 
    
         
            +
                        if self.output_typings == {StreamType.video}:
         
     | 
| 
      
 185 
     | 
    
         
            +
                            return FFMpegFilterType.vf
         
     | 
| 
      
 186 
     | 
    
         
            +
             
     | 
| 
      
 187 
     | 
    
         
            +
                    if self.input_typings == {StreamType.audio}:
         
     | 
| 
      
 188 
     | 
    
         
            +
                        if self.output_typings == {StreamType.audio}:
         
     | 
| 
      
 189 
     | 
    
         
            +
                            return FFMpegFilterType.af
         
     | 
| 
      
 190 
     | 
    
         
            +
                        if StreamType.video in self.output_typings:
         
     | 
| 
      
 191 
     | 
    
         
            +
                            return FFMpegFilterType.avf
         
     | 
| 
      
 192 
     | 
    
         
            +
             
     | 
| 
      
 193 
     | 
    
         
            +
                    if self.input_typings == {StreamType.video, StreamType.audio}:
         
     | 
| 
      
 194 
     | 
    
         
            +
                        return FFMpegFilterType.avf
         
     | 
| 
      
 195 
     | 
    
         
            +
             
     | 
| 
      
 196 
     | 
    
         
            +
                    raise ValueError(f"Unknown filter type for {self.name}")
         
     | 
| 
      
 197 
     | 
    
         
            +
             
     | 
| 
      
 198 
     | 
    
         
            +
             
     | 
| 
      
 199 
     | 
    
         
            +
            class FFMpegOptionFlag(int, Enum):
         
     | 
| 
      
 200 
     | 
    
         
            +
                OPT_FUNC_ARG = 1 << 0
         
     | 
| 
      
 201 
     | 
    
         
            +
                """
         
     | 
| 
      
 202 
     | 
    
         
            +
                The OPT_TYPE_FUNC option takes an argument.
         
     | 
| 
      
 203 
     | 
    
         
            +
                Must not be used with other option types, as for those it holds:
         
     | 
| 
      
 204 
     | 
    
         
            +
                - OPT_TYPE_BOOL do not take an argument
         
     | 
| 
      
 205 
     | 
    
         
            +
                - all other types do
         
     | 
| 
      
 206 
     | 
    
         
            +
                """
         
     | 
| 
      
 207 
     | 
    
         
            +
             
     | 
| 
      
 208 
     | 
    
         
            +
                OPT_EXIT = 1 << 1
         
     | 
| 
      
 209 
     | 
    
         
            +
                """
         
     | 
| 
      
 210 
     | 
    
         
            +
                Program will immediately exit after processing this option
         
     | 
| 
      
 211 
     | 
    
         
            +
                """
         
     | 
| 
      
 212 
     | 
    
         
            +
             
     | 
| 
      
 213 
     | 
    
         
            +
                OPT_EXPERT = 1 << 2
         
     | 
| 
      
 214 
     | 
    
         
            +
                """
         
     | 
| 
      
 215 
     | 
    
         
            +
                Option is intended for advanced users. Only affects help output.
         
     | 
| 
      
 216 
     | 
    
         
            +
                """
         
     | 
| 
      
 217 
     | 
    
         
            +
             
     | 
| 
      
 218 
     | 
    
         
            +
                OPT_VIDEO = 1 << 3
         
     | 
| 
      
 219 
     | 
    
         
            +
                OPT_AUDIO = 1 << 4
         
     | 
| 
      
 220 
     | 
    
         
            +
                OPT_SUBTITLE = 1 << 5
         
     | 
| 
      
 221 
     | 
    
         
            +
                OPT_DATA = 1 << 6
         
     | 
| 
      
 222 
     | 
    
         
            +
             
     | 
| 
      
 223 
     | 
    
         
            +
                OPT_PERFILE = 1 << 7
         
     | 
| 
      
 224 
     | 
    
         
            +
                """
         
     | 
| 
      
 225 
     | 
    
         
            +
                The option is per-file (currently ffmpeg-only). At least one of OPT_INPUT or OPT_OUTPUT must be set when this flag is in use.
         
     | 
| 
      
 226 
     | 
    
         
            +
                """
         
     | 
| 
      
 227 
     | 
    
         
            +
             
     | 
| 
      
 228 
     | 
    
         
            +
                OPT_FLAG_OFFSET = 1 << 8
         
     | 
| 
      
 229 
     | 
    
         
            +
                """
         
     | 
| 
      
 230 
     | 
    
         
            +
                Option is specified as an offset in a passed optctx.
         
     | 
| 
      
 231 
     | 
    
         
            +
                Always use as OPT_OFFSET in option definitions.
         
     | 
| 
      
 232 
     | 
    
         
            +
                """
         
     | 
| 
      
 233 
     | 
    
         
            +
             
     | 
| 
      
 234 
     | 
    
         
            +
                OPT_OFFSET = OPT_FLAG_OFFSET | OPT_PERFILE
         
     | 
| 
      
 235 
     | 
    
         
            +
                """
         
     | 
| 
      
 236 
     | 
    
         
            +
                Option is to be stored in a SpecifierOptList.
         
     | 
| 
      
 237 
     | 
    
         
            +
                Always use as OPT_SPEC in option definitions.
         
     | 
| 
      
 238 
     | 
    
         
            +
                """
         
     | 
| 
      
 239 
     | 
    
         
            +
                OPT_FLAG_SPEC = 1 << 9
         
     | 
| 
      
 240 
     | 
    
         
            +
                """
         
     | 
| 
      
 241 
     | 
    
         
            +
                Option is to be stored in a SpecifierOptList.
         
     | 
| 
      
 242 
     | 
    
         
            +
                Always use as OPT_SPEC in option definitions.
         
     | 
| 
      
 243 
     | 
    
         
            +
                """
         
     | 
| 
      
 244 
     | 
    
         
            +
             
     | 
| 
      
 245 
     | 
    
         
            +
                OPT_SPEC = OPT_FLAG_SPEC | OPT_OFFSET
         
     | 
| 
      
 246 
     | 
    
         
            +
                """
         
     | 
| 
      
 247 
     | 
    
         
            +
                Option applies per-stream (implies OPT_SPEC).
         
     | 
| 
      
 248 
     | 
    
         
            +
                """
         
     | 
| 
      
 249 
     | 
    
         
            +
                OPT_FLAG_PERSTREAM = 1 << 10
         
     | 
| 
      
 250 
     | 
    
         
            +
                """
         
     | 
| 
      
 251 
     | 
    
         
            +
                Option applies per-stream (implies OPT_SPEC).
         
     | 
| 
      
 252 
     | 
    
         
            +
                """
         
     | 
| 
      
 253 
     | 
    
         
            +
                OPT_PERSTREAM = OPT_FLAG_PERSTREAM | OPT_SPEC
         
     | 
| 
      
 254 
     | 
    
         
            +
             
     | 
| 
      
 255 
     | 
    
         
            +
                OPT_INPUT = 1 << 11
         
     | 
| 
      
 256 
     | 
    
         
            +
                """
         
     | 
| 
      
 257 
     | 
    
         
            +
                ffmpeg-only - specifies whether an OPT_PERFILE option applies to input, output, or both.
         
     | 
| 
      
 258 
     | 
    
         
            +
                """
         
     | 
| 
      
 259 
     | 
    
         
            +
                OPT_OUTPUT = 1 << 12
         
     | 
| 
      
 260 
     | 
    
         
            +
                """
         
     | 
| 
      
 261 
     | 
    
         
            +
                ffmpeg-only - specifies whether an OPT_PERFILE option applies to input, output, or both.
         
     | 
| 
      
 262 
     | 
    
         
            +
                """
         
     | 
| 
      
 263 
     | 
    
         
            +
             
     | 
| 
      
 264 
     | 
    
         
            +
                OPT_HAS_ALT = 1 << 13
         
     | 
| 
      
 265 
     | 
    
         
            +
                """
         
     | 
| 
      
 266 
     | 
    
         
            +
                This option is a "canonical" form, to which one or more alternatives exist. These alternatives are listed in u1.names_alt.
         
     | 
| 
      
 267 
     | 
    
         
            +
                """
         
     | 
| 
      
 268 
     | 
    
         
            +
                OPT_HAS_CANON = 1 << 14
         
     | 
| 
      
 269 
     | 
    
         
            +
                """
         
     | 
| 
      
 270 
     | 
    
         
            +
                This option is an alternative form of some other option, whose name is stored in u1.name_canon
         
     | 
| 
      
 271 
     | 
    
         
            +
                """
         
     | 
| 
      
 272 
     | 
    
         
            +
             
     | 
| 
      
 273 
     | 
    
         
            +
             
     | 
| 
      
 274 
     | 
    
         
            +
            class FFMpegOptionType(str, Enum):
         
     | 
| 
      
 275 
     | 
    
         
            +
                OPT_TYPE_FUNC = "OPT_TYPE_FUNC"
         
     | 
| 
      
 276 
     | 
    
         
            +
                OPT_TYPE_BOOL = "OPT_TYPE_BOOL"
         
     | 
| 
      
 277 
     | 
    
         
            +
                OPT_TYPE_STRING = "OPT_TYPE_STRING"
         
     | 
| 
      
 278 
     | 
    
         
            +
                OPT_TYPE_INT = "OPT_TYPE_INT"
         
     | 
| 
      
 279 
     | 
    
         
            +
                OPT_TYPE_INT64 = "OPT_TYPE_INT64"
         
     | 
| 
      
 280 
     | 
    
         
            +
                OPT_TYPE_FLOAT = "OPT_TYPE_FLOAT"
         
     | 
| 
      
 281 
     | 
    
         
            +
                OPT_TYPE_DOUBLE = "OPT_TYPE_DOUBLE"
         
     | 
| 
      
 282 
     | 
    
         
            +
                OPT_TYPE_TIME = "OPT_TYPE_TIME"
         
     | 
| 
      
 283 
     | 
    
         
            +
             
     | 
| 
      
 284 
     | 
    
         
            +
             
     | 
| 
      
 285 
     | 
    
         
            +
            @dataclass(frozen=True, kw_only=True)
         
     | 
| 
      
 286 
     | 
    
         
            +
            class FFMpegOption:
         
     | 
| 
      
 287 
     | 
    
         
            +
                name: str
         
     | 
| 
      
 288 
     | 
    
         
            +
                type: FFMpegOptionType
         
     | 
| 
      
 289 
     | 
    
         
            +
                flags: int
         
     | 
| 
      
 290 
     | 
    
         
            +
                help: str
         
     | 
| 
      
 291 
     | 
    
         
            +
                argname: str | None = None
         
     | 
| 
      
 292 
     | 
    
         
            +
                canon: str | None = None
         
     | 
| 
      
 293 
     | 
    
         
            +
             
     | 
| 
      
 294 
     | 
    
         
            +
                @property
         
     | 
| 
      
 295 
     | 
    
         
            +
                def is_input_option(self) -> bool:
         
     | 
| 
      
 296 
     | 
    
         
            +
                    return bool(self.flags & FFMpegOptionFlag.OPT_INPUT)
         
     | 
| 
      
 297 
     | 
    
         
            +
             
     | 
| 
      
 298 
     | 
    
         
            +
                @property
         
     | 
| 
      
 299 
     | 
    
         
            +
                def is_output_option(self) -> bool:
         
     | 
| 
      
 300 
     | 
    
         
            +
                    return bool(self.flags & FFMpegOptionFlag.OPT_OUTPUT)
         
     | 
| 
      
 301 
     | 
    
         
            +
             
     | 
| 
      
 302 
     | 
    
         
            +
                @property
         
     | 
| 
      
 303 
     | 
    
         
            +
                def is_global_option(self) -> bool:
         
     | 
| 
      
 304 
     | 
    
         
            +
                    return not self.is_input_option and not self.is_output_option and not (self.flags & FFMpegOptionFlag.OPT_EXIT)
         
     | 
| 
      
 305 
     | 
    
         
            +
             
     | 
| 
      
 306 
     | 
    
         
            +
                @property
         
     | 
| 
      
 307 
     | 
    
         
            +
                def is_support_stream_specifier(self) -> bool:
         
     | 
| 
      
 308 
     | 
    
         
            +
                    return bool(self.flags & FFMpegOptionFlag.OPT_SPEC)
         
     | 
| 
         @@ -0,0 +1,132 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            from __future__ import absolute_import, annotations
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            import importlib
         
     | 
| 
      
 4 
     | 
    
         
            +
            import json
         
     | 
| 
      
 5 
     | 
    
         
            +
            from dataclasses import fields, is_dataclass
         
     | 
| 
      
 6 
     | 
    
         
            +
            from enum import Enum
         
     | 
| 
      
 7 
     | 
    
         
            +
            from functools import partial
         
     | 
| 
      
 8 
     | 
    
         
            +
            from pathlib import Path
         
     | 
| 
      
 9 
     | 
    
         
            +
            from typing import Any
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            def load_class(path: str, strict: bool = True) -> Any:
         
     | 
| 
      
 13 
     | 
    
         
            +
                """
         
     | 
| 
      
 14 
     | 
    
         
            +
                Load a class from a string path
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                Args:
         
     | 
| 
      
 17 
     | 
    
         
            +
                    path: The path to the class.
         
     | 
| 
      
 18 
     | 
    
         
            +
                    strict: If True, raise an error if the class is not in ffmpeg package.
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                Returns:
         
     | 
| 
      
 21 
     | 
    
         
            +
                    The class.
         
     | 
| 
      
 22 
     | 
    
         
            +
                """
         
     | 
| 
      
 23 
     | 
    
         
            +
                if strict:
         
     | 
| 
      
 24 
     | 
    
         
            +
                    assert path.startswith("ffmpeg."), f"Only support loading class from ffmpeg package: {path}"
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                module_path, class_name = path.rsplit(".", 1)
         
     | 
| 
      
 27 
     | 
    
         
            +
                module = importlib.import_module(module_path)
         
     | 
| 
      
 28 
     | 
    
         
            +
                return getattr(module, class_name)
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
            def frozen(v: Any) -> Any:
         
     | 
| 
      
 32 
     | 
    
         
            +
                """
         
     | 
| 
      
 33 
     | 
    
         
            +
                Convert the instance to a frozen instance
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                Args:
         
     | 
| 
      
 36 
     | 
    
         
            +
                    v: The instance to convert.
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                Returns:
         
     | 
| 
      
 39 
     | 
    
         
            +
                    The frozen instance.
         
     | 
| 
      
 40 
     | 
    
         
            +
                """
         
     | 
| 
      
 41 
     | 
    
         
            +
                if isinstance(v, list):
         
     | 
| 
      
 42 
     | 
    
         
            +
                    return tuple(frozen(i) for i in v)
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                if isinstance(v, dict):
         
     | 
| 
      
 45 
     | 
    
         
            +
                    return tuple((key, frozen(value)) for key, value in v.items())
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                return v
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
            def object_hook(obj: Any, strict: bool = True) -> Any:
         
     | 
| 
      
 51 
     | 
    
         
            +
                """
         
     | 
| 
      
 52 
     | 
    
         
            +
                Convert the dictionary to an instance
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                Args:
         
     | 
| 
      
 55 
     | 
    
         
            +
                    obj: The dictionary to convert.
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                Returns:
         
     | 
| 
      
 58 
     | 
    
         
            +
                    The instance.
         
     | 
| 
      
 59 
     | 
    
         
            +
                """
         
     | 
| 
      
 60 
     | 
    
         
            +
                if isinstance(obj, dict):
         
     | 
| 
      
 61 
     | 
    
         
            +
                    if obj.get("__class__"):
         
     | 
| 
      
 62 
     | 
    
         
            +
                        cls = load_class(obj.pop("__class__"), strict=strict)
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                        if is_dataclass(cls):
         
     | 
| 
      
 65 
     | 
    
         
            +
                            # NOTE: in our application, the dataclass is always frozen
         
     | 
| 
      
 66 
     | 
    
         
            +
                            return cls(**{k: frozen(v) for k, v in obj.items()})
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                        return cls(**{k: v for k, v in obj.items()})
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                return obj
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
            def loads(raw: str, strict: bool = True) -> Any:
         
     | 
| 
      
 74 
     | 
    
         
            +
                """
         
     | 
| 
      
 75 
     | 
    
         
            +
                Deserialize the JSON string to an instance
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                Args:
         
     | 
| 
      
 78 
     | 
    
         
            +
                    raw: The JSON string to deserialize.
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                Returns:
         
     | 
| 
      
 81 
     | 
    
         
            +
                    The deserialized instance.
         
     | 
| 
      
 82 
     | 
    
         
            +
                """
         
     | 
| 
      
 83 
     | 
    
         
            +
                object_hook_strict = partial(object_hook, strict=strict)
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                return json.loads(raw, object_hook=object_hook_strict)
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
            def to_dict_with_class_info(instance: Any) -> Any:
         
     | 
| 
      
 89 
     | 
    
         
            +
                """
         
     | 
| 
      
 90 
     | 
    
         
            +
                Convert the instance to a dictionary with class information
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                Args:
         
     | 
| 
      
 93 
     | 
    
         
            +
                    instance: The instance to convert.
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                Returns:
         
     | 
| 
      
 96 
     | 
    
         
            +
                    The dictionary with class information
         
     | 
| 
      
 97 
     | 
    
         
            +
                """
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                if isinstance(instance, dict):
         
     | 
| 
      
 100 
     | 
    
         
            +
                    return {k: to_dict_with_class_info(v) for k, v in instance.items()}
         
     | 
| 
      
 101 
     | 
    
         
            +
                elif isinstance(instance, list):
         
     | 
| 
      
 102 
     | 
    
         
            +
                    return [to_dict_with_class_info(v) for v in instance]
         
     | 
| 
      
 103 
     | 
    
         
            +
                elif isinstance(instance, tuple):
         
     | 
| 
      
 104 
     | 
    
         
            +
                    return tuple(to_dict_with_class_info(v) for v in instance)
         
     | 
| 
      
 105 
     | 
    
         
            +
                elif isinstance(instance, Path):
         
     | 
| 
      
 106 
     | 
    
         
            +
                    return str(instance)
         
     | 
| 
      
 107 
     | 
    
         
            +
                elif is_dataclass(instance):
         
     | 
| 
      
 108 
     | 
    
         
            +
                    return {
         
     | 
| 
      
 109 
     | 
    
         
            +
                        "__class__": f"{instance.__class__.__module__}.{instance.__class__.__name__}",
         
     | 
| 
      
 110 
     | 
    
         
            +
                        **{k.name: to_dict_with_class_info(getattr(instance, k.name)) for k in fields(instance)},
         
     | 
| 
      
 111 
     | 
    
         
            +
                    }
         
     | 
| 
      
 112 
     | 
    
         
            +
                elif isinstance(instance, Enum):
         
     | 
| 
      
 113 
     | 
    
         
            +
                    return {
         
     | 
| 
      
 114 
     | 
    
         
            +
                        "__class__": f"{instance.__class__.__module__}.{instance.__class__.__name__}",
         
     | 
| 
      
 115 
     | 
    
         
            +
                        "value": instance.value,
         
     | 
| 
      
 116 
     | 
    
         
            +
                    }
         
     | 
| 
      
 117 
     | 
    
         
            +
                return instance
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
            # Serialization
         
     | 
| 
      
 121 
     | 
    
         
            +
            def dumps(instance: Any) -> str:
         
     | 
| 
      
 122 
     | 
    
         
            +
                """
         
     | 
| 
      
 123 
     | 
    
         
            +
                Serialize the instance to a JSON string
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
                Args:
         
     | 
| 
      
 126 
     | 
    
         
            +
                    instance: The instance to serialize.
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
      
 128 
     | 
    
         
            +
                Returns:
         
     | 
| 
      
 129 
     | 
    
         
            +
                    The serialized instance.
         
     | 
| 
      
 130 
     | 
    
         
            +
                """
         
     | 
| 
      
 131 
     | 
    
         
            +
                obj = to_dict_with_class_info(instance)
         
     | 
| 
      
 132 
     | 
    
         
            +
                return json.dumps(obj, indent=2)
         
     | 
| 
         @@ -0,0 +1,13 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            from .nodes import FilterableStream, FilterNode, GlobalNode, InputNode, OutputNode, OutputStream
         
     | 
| 
      
 2 
     | 
    
         
            +
            from .schema import Node, Stream
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            __all__ = [
         
     | 
| 
      
 5 
     | 
    
         
            +
                "Node",
         
     | 
| 
      
 6 
     | 
    
         
            +
                "Stream",
         
     | 
| 
      
 7 
     | 
    
         
            +
                "FilterableStream",
         
     | 
| 
      
 8 
     | 
    
         
            +
                "FilterNode",
         
     | 
| 
      
 9 
     | 
    
         
            +
                "GlobalNode",
         
     | 
| 
      
 10 
     | 
    
         
            +
                "InputNode",
         
     | 
| 
      
 11 
     | 
    
         
            +
                "OutputNode",
         
     | 
| 
      
 12 
     | 
    
         
            +
                "OutputStream",
         
     | 
| 
      
 13 
     | 
    
         
            +
            ]
         
     | 
| 
         @@ -0,0 +1,51 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            from __future__ import annotations
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            from .context import DAGContext
         
     | 
| 
      
 4 
     | 
    
         
            +
            from .nodes import FilterNode, GlobalNode, InputNode, OutputNode
         
     | 
| 
      
 5 
     | 
    
         
            +
            from .schema import Stream
         
     | 
| 
      
 6 
     | 
    
         
            +
            from .validate import validate
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            def compile(stream: Stream, auto_fix: bool = True) -> list[str]:
         
     | 
| 
      
 10 
     | 
    
         
            +
                """
         
     | 
| 
      
 11 
     | 
    
         
            +
                Compile the stream into a list of arguments.
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                Args:
         
     | 
| 
      
 14 
     | 
    
         
            +
                    stream: The stream to compile.
         
     | 
| 
      
 15 
     | 
    
         
            +
                    auto_fix: Whether to automatically fix the stream.
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                Returns:
         
     | 
| 
      
 18 
     | 
    
         
            +
                    The list of arguments.
         
     | 
| 
      
 19 
     | 
    
         
            +
                """
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                stream = validate(stream, auto_fix=auto_fix)
         
     | 
| 
      
 22 
     | 
    
         
            +
                node = stream.node
         
     | 
| 
      
 23 
     | 
    
         
            +
                context = DAGContext.build(node)
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                # compile the global nodes
         
     | 
| 
      
 26 
     | 
    
         
            +
                commands = []
         
     | 
| 
      
 27 
     | 
    
         
            +
                global_nodes = [node for node in context.all_nodes if isinstance(node, GlobalNode)]
         
     | 
| 
      
 28 
     | 
    
         
            +
                for node in global_nodes:
         
     | 
| 
      
 29 
     | 
    
         
            +
                    commands += node.get_args(context)
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                # compile the input nodes
         
     | 
| 
      
 32 
     | 
    
         
            +
                input_nodes = [node for node in context.all_nodes if isinstance(node, InputNode)]
         
     | 
| 
      
 33 
     | 
    
         
            +
                for node in input_nodes:
         
     | 
| 
      
 34 
     | 
    
         
            +
                    commands += node.get_args(context)
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                # compile the filter nodes
         
     | 
| 
      
 37 
     | 
    
         
            +
                vf_commands = []
         
     | 
| 
      
 38 
     | 
    
         
            +
                filter_nodes = [node for node in context.all_nodes if isinstance(node, FilterNode)]
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                for node in sorted(filter_nodes, key=lambda node: len(node.upstream_nodes)):
         
     | 
| 
      
 41 
     | 
    
         
            +
                    vf_commands += ["".join(node.get_args(context))]
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                if vf_commands:
         
     | 
| 
      
 44 
     | 
    
         
            +
                    commands += ["-filter_complex", ";".join(vf_commands)]
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                # compile the output nodes
         
     | 
| 
      
 47 
     | 
    
         
            +
                output_nodes = [node for node in context.all_nodes if isinstance(node, OutputNode)]
         
     | 
| 
      
 48 
     | 
    
         
            +
                for node in output_nodes:
         
     | 
| 
      
 49 
     | 
    
         
            +
                    commands += node.get_args(context)
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                return commands
         
     |