typed-ffmpeg-compatible 2.6.4__py3-none-any.whl → 2.7.2__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/common/schema.py +4 -1
- typed_ffmpeg/common/serialize.py +43 -25
- typed_ffmpeg/dag/nodes.py +1 -4
- typed_ffmpeg/dag/schema.py +2 -1
- {typed_ffmpeg_compatible-2.6.4.dist-info → typed_ffmpeg_compatible-2.7.2.dist-info}/METADATA +1 -1
- {typed_ffmpeg_compatible-2.6.4.dist-info → typed_ffmpeg_compatible-2.7.2.dist-info}/RECORD +9 -9
- {typed_ffmpeg_compatible-2.6.4.dist-info → typed_ffmpeg_compatible-2.7.2.dist-info}/LICENSE +0 -0
- {typed_ffmpeg_compatible-2.6.4.dist-info → typed_ffmpeg_compatible-2.7.2.dist-info}/WHEEL +0 -0
- {typed_ffmpeg_compatible-2.6.4.dist-info → typed_ffmpeg_compatible-2.7.2.dist-info}/entry_points.txt +0 -0
typed_ffmpeg/common/schema.py
CHANGED
@@ -12,7 +12,10 @@ from dataclasses import dataclass
|
|
12
12
|
from enum import Enum
|
13
13
|
from typing import Literal
|
14
14
|
|
15
|
+
from .serialize import Serializable, serializable
|
15
16
|
|
17
|
+
|
18
|
+
@serializable
|
16
19
|
class StreamType(str, Enum):
|
17
20
|
"""
|
18
21
|
Enumeration of possible stream types in FFmpeg.
|
@@ -198,7 +201,7 @@ class FFMpegFilterDef:
|
|
198
201
|
|
199
202
|
|
200
203
|
@dataclass(frozen=True, kw_only=True)
|
201
|
-
class FFMpegFilter:
|
204
|
+
class FFMpegFilter(Serializable):
|
202
205
|
"""
|
203
206
|
Comprehensive representation of an FFmpeg filter with all its metadata.
|
204
207
|
|
typed_ffmpeg/common/serialize.py
CHANGED
@@ -9,7 +9,6 @@ saved to disk and loaded back.
|
|
9
9
|
|
10
10
|
from __future__ import annotations
|
11
11
|
|
12
|
-
import importlib
|
13
12
|
import json
|
14
13
|
from dataclasses import fields, is_dataclass
|
15
14
|
from enum import Enum
|
@@ -19,43 +18,62 @@ from typing import Any
|
|
19
18
|
|
20
19
|
from ..utils.forzendict import FrozenDict
|
21
20
|
|
21
|
+
CLASS_REGISTRY: dict[str, type[Serializable | Enum]] = {}
|
22
|
+
"""
|
23
|
+
A registry of classes that have been loaded, and can be deserialized.
|
24
|
+
"""
|
25
|
+
|
26
|
+
|
27
|
+
def serializable(
|
28
|
+
cls: type[Serializable] | type[Enum],
|
29
|
+
) -> type[Serializable] | type[Enum]:
|
30
|
+
"""
|
31
|
+
Register a class with the serialization system.
|
32
|
+
"""
|
33
|
+
assert cls.__name__ not in CLASS_REGISTRY, (
|
34
|
+
f"Class {cls.__name__} already registered"
|
35
|
+
)
|
36
|
+
CLASS_REGISTRY[cls.__name__] = cls
|
37
|
+
|
38
|
+
return cls
|
39
|
+
|
22
40
|
|
23
|
-
|
41
|
+
class Serializable:
|
24
42
|
"""
|
25
|
-
|
43
|
+
A base class for all serializable classes.
|
44
|
+
"""
|
45
|
+
|
46
|
+
def __init_subclass__(cls, **kwargs: Any) -> None:
|
47
|
+
super().__init_subclass__(**kwargs)
|
48
|
+
serializable(cls)
|
49
|
+
|
50
|
+
|
51
|
+
def load_class(name: str) -> type[Serializable] | type[Enum]:
|
52
|
+
"""
|
53
|
+
Load a class from its name.
|
26
54
|
|
27
|
-
This function
|
28
|
-
|
29
|
-
to reconstruct objects from their class names.
|
55
|
+
This function looks up a class by its name in the CLASS_REGISTRY. It's used during
|
56
|
+
deserialization to reconstruct objects from their class names.
|
30
57
|
|
31
58
|
Args:
|
32
|
-
|
33
|
-
strict: If True, only allow loading classes from the ffmpeg package
|
34
|
-
as a security measure
|
59
|
+
name: The simple class name (e.g., 'FilterNode')
|
35
60
|
|
36
61
|
Returns:
|
37
62
|
The class object that can be instantiated
|
38
63
|
|
39
64
|
Raises:
|
40
|
-
AssertionError: If
|
41
|
-
ImportError: If the module or class cannot be found
|
65
|
+
AssertionError: If the class name is not found in the registry
|
42
66
|
|
43
67
|
Example:
|
44
68
|
```python
|
45
69
|
# Load the FilterNode class
|
46
|
-
FilterNode = load_class('
|
70
|
+
FilterNode = load_class('FilterNode')
|
47
71
|
# Create an instance
|
48
72
|
node = FilterNode(name='scale', ...)
|
49
73
|
```
|
50
74
|
"""
|
51
|
-
|
52
|
-
|
53
|
-
f"Only support loading class from ffmpeg package: {path}"
|
54
|
-
)
|
55
|
-
|
56
|
-
module_path, class_name = path.rsplit(".", 1)
|
57
|
-
module = importlib.import_module(module_path)
|
58
|
-
return getattr(module, class_name)
|
75
|
+
assert name in CLASS_REGISTRY, f"Class {name} not registered"
|
76
|
+
return CLASS_REGISTRY[name]
|
59
77
|
|
60
78
|
|
61
79
|
def frozen(v: Any) -> Any:
|
@@ -110,7 +128,7 @@ def object_hook(obj: Any, strict: bool = True) -> Any:
|
|
110
128
|
```python
|
111
129
|
# A JSON object with class information
|
112
130
|
json_obj = {
|
113
|
-
"__class__": "
|
131
|
+
"__class__": "FilterNode",
|
114
132
|
"name": "scale",
|
115
133
|
"kwargs": {"width": 1280, "height": 720},
|
116
134
|
}
|
@@ -119,7 +137,7 @@ def object_hook(obj: Any, strict: bool = True) -> Any:
|
|
119
137
|
"""
|
120
138
|
if isinstance(obj, dict):
|
121
139
|
if obj.get("__class__"):
|
122
|
-
cls = load_class(obj.pop("__class__")
|
140
|
+
cls = load_class(obj.pop("__class__"))
|
123
141
|
|
124
142
|
if is_dataclass(cls):
|
125
143
|
# NOTE: in our application, the dataclass is always frozen
|
@@ -148,7 +166,7 @@ def loads(raw: str, strict: bool = True) -> Any:
|
|
148
166
|
Example:
|
149
167
|
```python
|
150
168
|
# Deserialize a filter graph from JSON
|
151
|
-
json_str = '{"__class__": "
|
169
|
+
json_str = '{"__class__": "FilterNode", "name": "scale", ...}'
|
152
170
|
filter_node = loads(json_str)
|
153
171
|
# filter_node is now a FilterNode instance
|
154
172
|
```
|
@@ -192,7 +210,7 @@ def to_dict_with_class_info(instance: Any) -> Any:
|
|
192
210
|
return str(instance)
|
193
211
|
elif is_dataclass(instance):
|
194
212
|
return {
|
195
|
-
"__class__":
|
213
|
+
"__class__": instance.__class__.__name__,
|
196
214
|
**{
|
197
215
|
k.name: to_dict_with_class_info(getattr(instance, k.name))
|
198
216
|
for k in fields(instance)
|
@@ -200,7 +218,7 @@ def to_dict_with_class_info(instance: Any) -> Any:
|
|
200
218
|
}
|
201
219
|
elif isinstance(instance, Enum):
|
202
220
|
return {
|
203
|
-
"__class__":
|
221
|
+
"__class__": instance.__class__.__name__,
|
204
222
|
"value": instance.value,
|
205
223
|
}
|
206
224
|
return instance
|
typed_ffmpeg/dag/nodes.py
CHANGED
@@ -665,10 +665,7 @@ class OutputNode(Node):
|
|
665
665
|
# !handle mapping
|
666
666
|
commands = []
|
667
667
|
|
668
|
-
if context
|
669
|
-
any(isinstance(k.node, FilterNode) for k in self.inputs)
|
670
|
-
or len([k for k in context.all_nodes if isinstance(k, OutputNode)]) > 1
|
671
|
-
):
|
668
|
+
if context:
|
672
669
|
for input in self.inputs:
|
673
670
|
if isinstance(input.node, InputNode):
|
674
671
|
commands += ["-map", input.label(context)]
|
typed_ffmpeg/dag/schema.py
CHANGED
@@ -5,6 +5,7 @@ from dataclasses import dataclass, replace
|
|
5
5
|
from functools import cached_property
|
6
6
|
from typing import TYPE_CHECKING, Literal
|
7
7
|
|
8
|
+
from ..common.serialize import Serializable
|
8
9
|
from ..utils.forzendict import FrozenDict
|
9
10
|
from ..utils.lazy_eval.schema import LazyValue
|
10
11
|
from .utils import is_dag
|
@@ -14,7 +15,7 @@ if TYPE_CHECKING:
|
|
14
15
|
|
15
16
|
|
16
17
|
@dataclass(frozen=True, kw_only=True)
|
17
|
-
class HashableBaseModel:
|
18
|
+
class HashableBaseModel(Serializable):
|
18
19
|
"""
|
19
20
|
A base class for hashable dataclasses.
|
20
21
|
"""
|
{typed_ffmpeg_compatible-2.6.4.dist-info → typed_ffmpeg_compatible-2.7.2.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: typed-ffmpeg-compatible
|
3
|
-
Version: 2.
|
3
|
+
Version: 2.7.2
|
4
4
|
Summary: Modern Python FFmpeg wrappers offer comprehensive support for complex filters, complete with detailed typing and documentation.
|
5
5
|
Home-page: https://livingbio.github.io/typed-ffmpeg/
|
6
6
|
License: MIT
|
@@ -1,8 +1,8 @@
|
|
1
1
|
typed_ffmpeg/__init__.py,sha256=NL_jH6nUb5pF8pMt3S51243qBTUvZdN-9sJpeuiznRE,1532
|
2
2
|
typed_ffmpeg/base.py,sha256=C5Tqbx2I0c-09D7aXKZoGkspu-lAAeAhuOns5zr3PXQ,6304
|
3
3
|
typed_ffmpeg/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
4
|
-
typed_ffmpeg/common/schema.py,sha256=
|
5
|
-
typed_ffmpeg/common/serialize.py,sha256=
|
4
|
+
typed_ffmpeg/common/schema.py,sha256=Gfq_C8T4F0Ie6E9vzmWMn-mzjN0tOfTK0mKHugW5vEs,19547
|
5
|
+
typed_ffmpeg/common/serialize.py,sha256=QH7UVPCXAHrqbAMK11DzFfy03XgFEHXWOrCVUJxBxR0,7764
|
6
6
|
typed_ffmpeg/dag/__init__.py,sha256=qAApSNqjbZ1DtUaV5bSku9RwG7MpMPa1HJO764cSBt4,849
|
7
7
|
typed_ffmpeg/dag/compile.py,sha256=GXMTFE1vqRDI-nuIZPtJwXQhCc61uCIGh5u4OBRp8gg,3129
|
8
8
|
typed_ffmpeg/dag/context.py,sha256=pv77lltW6yk-PRYlZ0MC6FICVxYLuyxQAS9Vx5vko0s,11691
|
@@ -14,8 +14,8 @@ typed_ffmpeg/dag/io/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
|
|
14
14
|
typed_ffmpeg/dag/io/_input.py,sha256=KRLTSQPEfmgPcPEAJdeWRHZhNsClaJCB9Ac6czMOrmE,7214
|
15
15
|
typed_ffmpeg/dag/io/_output.py,sha256=_no6ffAOABznbLNTki8CYr7pvr4Sa0LweRfn38-cszs,12470
|
16
16
|
typed_ffmpeg/dag/io/output_args.py,sha256=SThIhZh9PXs2m6Fz5JsSy8oS-Te7GM_oz7HRuZo0-eI,13901
|
17
|
-
typed_ffmpeg/dag/nodes.py,sha256=
|
18
|
-
typed_ffmpeg/dag/schema.py,sha256=
|
17
|
+
typed_ffmpeg/dag/nodes.py,sha256=rV6pGPOo6hCuI_4Iipq8pwEz32U2iz7vITaG4C6dmCs,28964
|
18
|
+
typed_ffmpeg/dag/schema.py,sha256=51t38NHgztXp5RAPKgSKqFoSQaU5IsnhIxuxnx2Udl4,6098
|
19
19
|
typed_ffmpeg/dag/utils.py,sha256=hydh7_kjpOCw8WEGhXMxIXR4Ek-3DeoOt6esInuK2Xw,1941
|
20
20
|
typed_ffmpeg/dag/validate.py,sha256=tWswVibP_BFNOiWX0LgnfMrg3TBm7QIt060Ofd9ychE,9569
|
21
21
|
typed_ffmpeg/exceptions.py,sha256=D4SID6WOwkjVV8O8mAjrEDHWn-8BRDnK_jteaDof1SY,2474
|
@@ -41,8 +41,8 @@ typed_ffmpeg/utils/run.py,sha256=mSoAdcvD-InldqkRgWNc8iXKgJJoEMAOE4PL2gVmtqw,217
|
|
41
41
|
typed_ffmpeg/utils/snapshot.py,sha256=mKILRm6qiQV2egaD-70MSUEl-DFoLD5w_v9GZIequI4,2181
|
42
42
|
typed_ffmpeg/utils/typing.py,sha256=DBQn_gCF8C_DTwsfMHeCgfnNUROwAjlIcHrQ7lNDOoE,1187
|
43
43
|
typed_ffmpeg/utils/view.py,sha256=4DJmyGIkUeE6-V0azudDCWm608mRdeRhTZHMgnew9Kw,3409
|
44
|
-
typed_ffmpeg_compatible-2.
|
45
|
-
typed_ffmpeg_compatible-2.
|
46
|
-
typed_ffmpeg_compatible-2.
|
47
|
-
typed_ffmpeg_compatible-2.
|
48
|
-
typed_ffmpeg_compatible-2.
|
44
|
+
typed_ffmpeg_compatible-2.7.2.dist-info/LICENSE,sha256=8Aaya5i_09Cou2i3QMxTwz6uHGzi_fGA4uhkco07-A4,1066
|
45
|
+
typed_ffmpeg_compatible-2.7.2.dist-info/METADATA,sha256=djaV8dOPAbYJax4MQV1G0kDMuZXwlOxNbCpxzbgD3pw,7249
|
46
|
+
typed_ffmpeg_compatible-2.7.2.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
|
47
|
+
typed_ffmpeg_compatible-2.7.2.dist-info/entry_points.txt,sha256=KfZmNsM16GT_lF1otASIN6E3i6xXHXoB1gMeEdlptjA,44
|
48
|
+
typed_ffmpeg_compatible-2.7.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|
{typed_ffmpeg_compatible-2.6.4.dist-info → typed_ffmpeg_compatible-2.7.2.dist-info}/entry_points.txt
RENAMED
File without changes
|