typed-ffmpeg-compatible 2.4.1__tar.gz → 2.6.0__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/PKG-INFO +1 -1
  2. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/pyproject.toml +1 -1
  3. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/__init__.py +4 -0
  4. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/dag/nodes.py +6 -3
  5. typed_ffmpeg_compatible-2.6.0/src/typed_ffmpeg/info.py +161 -0
  6. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/utils/snapshot.py +4 -5
  7. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/LICENSE +0 -0
  8. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/README.md +0 -0
  9. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/base.py +0 -0
  10. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/common/__init__.py +0 -0
  11. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/common/schema.py +0 -0
  12. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/common/serialize.py +0 -0
  13. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/dag/__init__.py +0 -0
  14. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/dag/compile.py +0 -0
  15. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/dag/context.py +0 -0
  16. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/dag/factory.py +0 -0
  17. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/dag/global_runnable/__init__.py +0 -0
  18. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/dag/global_runnable/global_args.py +0 -0
  19. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/dag/global_runnable/runnable.py +0 -0
  20. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/dag/io/__init__.py +0 -0
  21. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/dag/io/_input.py +0 -0
  22. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/dag/io/_output.py +0 -0
  23. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/dag/io/output_args.py +0 -0
  24. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/dag/schema.py +0 -0
  25. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/dag/utils.py +0 -0
  26. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/dag/validate.py +0 -0
  27. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/exceptions.py +0 -0
  28. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/filters.py +0 -0
  29. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/probe.py +0 -0
  30. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/py.typed +0 -0
  31. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/schema.py +0 -0
  32. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/streams/__init__.py +0 -0
  33. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/streams/audio.py +0 -0
  34. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/streams/av.py +0 -0
  35. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/streams/channel_layout.py +0 -0
  36. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/streams/video.py +0 -0
  37. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/types.py +0 -0
  38. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/utils/__init__.py +0 -0
  39. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/utils/escaping.py +0 -0
  40. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/utils/lazy_eval/__init__.py +0 -0
  41. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/utils/lazy_eval/operator.py +0 -0
  42. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/utils/lazy_eval/schema.py +0 -0
  43. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/utils/run.py +0 -0
  44. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/utils/typing.py +0 -0
  45. {typed_ffmpeg_compatible-2.4.1 → typed_ffmpeg_compatible-2.6.0}/src/typed_ffmpeg/utils/view.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: typed-ffmpeg-compatible
3
- Version: 2.4.1
3
+ Version: 2.6.0
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,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "typed-ffmpeg-compatible"
3
- version = "2.4.1"
3
+ version = "2.6.0"
4
4
  description = "Modern Python FFmpeg wrappers offer comprehensive support for complex filters, complete with detailed typing and documentation."
5
5
  authors = ["lucemia <lucemia@gmail.com>"]
6
6
  readme = "README.md"
@@ -2,6 +2,7 @@ from . import dag, filters
2
2
  from .base import afilter, filter_multi_output, input, merge_outputs, output, vfilter
3
3
  from .dag import Stream
4
4
  from .exceptions import FFMpegExecuteError, FFMpegTypeError, FFMpegValueError
5
+ from .info import get_codecs, get_decoders, get_encoders
5
6
  from .probe import probe
6
7
  from .streams import AudioStream, AVStream, VideoStream
7
8
 
@@ -22,4 +23,7 @@ __all__ = [
22
23
  "afilter",
23
24
  "filter_multi_output",
24
25
  "dag",
26
+ "get_codecs",
27
+ "get_decoders",
28
+ "get_encoders",
25
29
  ]
@@ -351,7 +351,8 @@ class InputNode(Node):
351
351
  if isinstance(value, bool):
352
352
  if value is True:
353
353
  commands += [f"-{key}"]
354
- # NOTE: the -nooption is not supported
354
+ elif value is False:
355
+ commands += [f"-no{key}"]
355
356
  else:
356
357
  commands += [f"-{key}", str(value)]
357
358
  commands += ["-i", self.filename]
@@ -398,7 +399,8 @@ class OutputNode(Node):
398
399
  if isinstance(value, bool):
399
400
  if value is True:
400
401
  commands += [f"-{key}"]
401
- # NOTE: the -nooption is not supported
402
+ elif value is False:
403
+ commands += [f"-no{key}"]
402
404
  else:
403
405
  commands += [f"-{key}", str(value)]
404
406
  commands += [self.filename]
@@ -451,7 +453,8 @@ class GlobalNode(Node):
451
453
  if isinstance(value, bool):
452
454
  if value is True:
453
455
  commands += [f"-{key}"]
454
- # NOTE: the -no{key} format since not really for global options
456
+ elif value is False:
457
+ commands += [f"-no{key}"]
455
458
  else:
456
459
  commands += [f"-{key}", str(value)]
457
460
  return commands
@@ -0,0 +1,161 @@
1
+ import logging
2
+ import subprocess
3
+ from dataclasses import dataclass
4
+ from enum import Flag, auto
5
+
6
+ from .exceptions import FFMpegExecuteError
7
+ from .utils.run import command_line
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+
12
+ class CodecFlags(Flag):
13
+ video = auto()
14
+ audio = auto()
15
+ subtitle = auto()
16
+ frame_level_multithreading = auto()
17
+ slice_level_multithreading = auto()
18
+ experimental = auto()
19
+ draw_horiz_band = auto()
20
+ direct_rendering_method_1 = auto()
21
+
22
+
23
+ @dataclass(frozen=True)
24
+ class Codec:
25
+ name: str
26
+ flags: CodecFlags
27
+ description: str
28
+
29
+
30
+ def parse_codec_flags(flags: str) -> CodecFlags:
31
+ flags_enum = CodecFlags(0)
32
+ if flags[0] == "V":
33
+ flags_enum |= CodecFlags.video
34
+ if flags[0] == "A":
35
+ flags_enum |= CodecFlags.audio
36
+ if flags[0] == "S":
37
+ flags_enum |= CodecFlags.subtitle
38
+ if flags[1] == "F":
39
+ flags_enum |= CodecFlags.frame_level_multithreading
40
+ if flags[2] == "S":
41
+ flags_enum |= CodecFlags.slice_level_multithreading
42
+ if flags[3] == "X":
43
+ flags_enum |= CodecFlags.experimental
44
+ if flags[4] == "B":
45
+ flags_enum |= CodecFlags.draw_horiz_band
46
+ if flags[5] == "D":
47
+ flags_enum |= CodecFlags.direct_rendering_method_1
48
+ return flags_enum
49
+
50
+
51
+ def get_codecs() -> tuple[Codec, ...]:
52
+ args = ["ffmpeg", "-hide_banner", "-codecs"]
53
+ logger.info("Running ffmpeg command: %s", command_line(args))
54
+ p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
55
+ out, err = p.communicate()
56
+
57
+ retcode = p.poll()
58
+ if p.returncode != 0:
59
+ raise FFMpegExecuteError(retcode=retcode, cmd=command_line(args), stdout=out, stderr=err)
60
+
61
+ codecs = out.decode("utf-8")
62
+ codecs_lines = codecs.strip().split("\n")
63
+ # Skip header lines until we find the separator
64
+ for i, line in enumerate(codecs_lines):
65
+ if line.startswith(" ------"):
66
+ codecs_lines = codecs_lines[i + 1 :]
67
+ break
68
+ return tuple(
69
+ Codec(
70
+ name=parts[1],
71
+ flags=parse_codec_flags(parts[0]),
72
+ description=parts[2],
73
+ )
74
+ for line in codecs_lines
75
+ for parts in [line.split(None, 3)]
76
+ )
77
+
78
+
79
+ class CoderFlags(Flag):
80
+ video = auto()
81
+ audio = auto()
82
+ subtitle = auto()
83
+ frame_level_multithreading = auto()
84
+ slice_level_multithreading = auto()
85
+ experimental = auto()
86
+ draw_horiz_band = auto()
87
+ direct_rendering_method_1 = auto()
88
+
89
+
90
+ def parse_coder_flags(flags: str) -> CoderFlags:
91
+ flags_enum = CoderFlags(0)
92
+ if flags[0] == "V":
93
+ flags_enum |= CoderFlags.video
94
+ if flags[0] == "A":
95
+ flags_enum |= CoderFlags.audio
96
+ if flags[0] == "S":
97
+ flags_enum |= CoderFlags.subtitle
98
+ if flags[1] == "F":
99
+ flags_enum |= CoderFlags.frame_level_multithreading
100
+ if flags[2] == "S":
101
+ flags_enum |= CoderFlags.slice_level_multithreading
102
+ if flags[3] == "X":
103
+ flags_enum |= CoderFlags.experimental
104
+ if flags[4] == "B":
105
+ flags_enum |= CoderFlags.draw_horiz_band
106
+ if flags[5] == "D":
107
+ flags_enum |= CoderFlags.direct_rendering_method_1
108
+ return flags_enum
109
+
110
+
111
+ @dataclass
112
+ class Coder:
113
+ name: str
114
+ flags: CoderFlags
115
+ description: str
116
+
117
+
118
+ def get_coders(codes: str) -> tuple[Coder, ...]:
119
+ codecs_lines = codes.strip().split("\n")
120
+ # Skip header lines until we find the separator
121
+ for i, line in enumerate(codecs_lines):
122
+ if line.startswith(" ------"):
123
+ codecs_lines = codecs_lines[i + 1 :]
124
+ break
125
+ return tuple(
126
+ Coder(
127
+ name=parts[1],
128
+ flags=parse_coder_flags(parts[0]),
129
+ description=parts[2],
130
+ )
131
+ for line in codecs_lines
132
+ for parts in [line.split(None, 3)]
133
+ )
134
+
135
+
136
+ def get_decoders() -> tuple[Coder, ...]:
137
+ args = ["ffmpeg", "-hide_banner", "-decoders"]
138
+ logger.info("Running ffmpeg command: %s", command_line(args))
139
+ p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
140
+ out, err = p.communicate()
141
+
142
+ retcode = p.poll()
143
+ if p.returncode != 0:
144
+ raise FFMpegExecuteError(retcode=retcode, cmd=command_line(args), stdout=out, stderr=err)
145
+
146
+ decoders = out.decode("utf-8")
147
+ return get_coders(decoders)
148
+
149
+
150
+ def get_encoders() -> tuple[Coder, ...]:
151
+ args = ["ffmpeg", "-hide_banner", "-encoders"]
152
+ logger.info("Running ffmpeg command: %s", command_line(args))
153
+ p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
154
+ out, err = p.communicate()
155
+
156
+ retcode = p.poll()
157
+ if p.returncode != 0:
158
+ raise FFMpegExecuteError(retcode=retcode, cmd=command_line(args), stdout=out, stderr=err)
159
+
160
+ encoders = out.decode("utf-8")
161
+ return get_coders(encoders)
@@ -1,12 +1,13 @@
1
+ from dataclasses import asdict
1
2
  from typing import Optional
2
3
 
3
- from syrupy.extensions.image import PNGImageSnapshotExtension
4
+ from syrupy.extensions.json import JSONSnapshotExtension
4
5
  from syrupy.types import PropertyFilter, PropertyMatcher, SerializableData, SerializedData
5
6
 
6
7
  from ..dag.schema import Stream
7
8
 
8
9
 
9
- class DAGSnapshotExtenstion(PNGImageSnapshotExtension):
10
+ class DAGSnapshotExtenstion(JSONSnapshotExtension):
10
11
  """
11
12
  A snapshot extension for the DAG. This extension is used to serialize and match the DAG.
12
13
  """
@@ -20,7 +21,5 @@ class DAGSnapshotExtenstion(PNGImageSnapshotExtension):
20
21
  matcher: Optional["PropertyMatcher"] = None,
21
22
  ) -> "SerializedData":
22
23
  stream = Stream(node=data)
23
- graph_path = stream.view()
24
24
 
25
- with open(graph_path, "rb") as ifile:
26
- return super().serialize(ifile.read())
25
+ return super().serialize(asdict(stream))