typed-ffmpeg-compatible 2.5.0__py3-none-any.whl → 2.6.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
typed_ffmpeg/__init__.py CHANGED
@@ -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
  ]
typed_ffmpeg/dag/nodes.py CHANGED
@@ -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]
@@ -453,7 +455,6 @@ class GlobalNode(Node):
453
455
  commands += [f"-{key}"]
454
456
  elif value is False:
455
457
  commands += [f"-no{key}"]
456
- # NOTE: the -no{key} format since not really for global options
457
458
  else:
458
459
  commands += [f"-{key}", str(value)]
459
460
  return commands
typed_ffmpeg/info.py ADDED
@@ -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))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: typed-ffmpeg-compatible
3
- Version: 2.5.0
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,4 +1,4 @@
1
- typed_ffmpeg/__init__.py,sha256=WnVr15iPyHWL5ZXtAKah50Jf-rR4QEA8qisBWhMY3KI,596
1
+ typed_ffmpeg/__init__.py,sha256=122K8EWO5F-1re6YjI_VrTo8tlGGkNbT9Dqlppt5bUI,711
2
2
  typed_ffmpeg/base.py,sha256=aa0-lPC_P_P61nnQRK0VR9a-b-XhNCPJUdtYWkQA6yE,3169
3
3
  typed_ffmpeg/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  typed_ffmpeg/common/schema.py,sha256=9oiURyju0YDYPdhg0afrSQ1nOyAK-0v1xSkA6S7Psok,9460
@@ -14,12 +14,13 @@ typed_ffmpeg/dag/io/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
14
14
  typed_ffmpeg/dag/io/_input.py,sha256=xz5MWVmQJNd2ryFoDNy094hIfj2RCngIxu12AwPwnUU,7162
15
15
  typed_ffmpeg/dag/io/_output.py,sha256=OCY7XxEdEOfvVqjEsXq5SWPt02Kkx8GkYphIWiAkLts,12415
16
16
  typed_ffmpeg/dag/io/output_args.py,sha256=tilJd5xssVahZP88J8A2urZ3PHJG80Lj6kv1QS8WZzE,13875
17
- typed_ffmpeg/dag/nodes.py,sha256=hdgwepV3U-GU2rHCgWor2pYuNyCJcCL2mJtuOWgSGuw,14449
17
+ typed_ffmpeg/dag/nodes.py,sha256=BJ-fQfiwJrz7i0F5CQ-ZFVyb014_h7XRTOUtcxRSAuU,14425
18
18
  typed_ffmpeg/dag/schema.py,sha256=WfY6gJtjnOxJV-Z5hdBcrKUYzpbHv4dT7mYs0Zv9lS0,5926
19
19
  typed_ffmpeg/dag/utils.py,sha256=ooGrCOnsvRjjUMhC4JSCc3T2FRL1JLz0CeVSiE9f1hI,1141
20
20
  typed_ffmpeg/dag/validate.py,sha256=smkTzh4ex6Omqcm5iUWeKX5-B8c4e0oTTVOGWSl9i5I,5786
21
21
  typed_ffmpeg/exceptions.py,sha256=Zz-lKGildItY9a3dD1NHqx5b1U9hO6splbtA8JGce0U,968
22
22
  typed_ffmpeg/filters.py,sha256=OEIG8nWGMHBenm5EJmng6nHYCL-_t5KVun5quF4GhLE,107702
23
+ typed_ffmpeg/info.py,sha256=vgJo0QVxxZeXIe3GUjo060MQ7ligzlNmxbRXgogHsVw,4724
23
24
  typed_ffmpeg/probe.py,sha256=srLG1HIK4V4bX3GdgETiLrkc7LJuTAoG7B36L6htteQ,1408
24
25
  typed_ffmpeg/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
26
  typed_ffmpeg/schema.py,sha256=5ITK1t7g_OLZRyuN69DI920wERsxZZxrV3mV57tql7A,534
@@ -35,11 +36,11 @@ typed_ffmpeg/utils/lazy_eval/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NM
35
36
  typed_ffmpeg/utils/lazy_eval/operator.py,sha256=AFUUdse1M5QvyO6i78xO3e3hxG2lwITSmgywsQbDWNs,2787
36
37
  typed_ffmpeg/utils/lazy_eval/schema.py,sha256=JCiwxNElHzeSm5ehfgpwgV1fNbZ7cuQzs2u605zEw5Q,4840
37
38
  typed_ffmpeg/utils/run.py,sha256=OotkfIFCU9KD_SzhigCtUeqqvcEWlFiMUsBcxms1k3M,709
38
- typed_ffmpeg/utils/snapshot.py,sha256=OTEH0Eyzam_r1i5oIC0nKD3hGpVmq4aV5TB2qd5hKuM,813
39
+ typed_ffmpeg/utils/snapshot.py,sha256=0kWMjuwxhwaMtiXwHb4qH_ejsuAYpJyj9gHcLE2Rxps,752
39
40
  typed_ffmpeg/utils/typing.py,sha256=sx4IN38NGQxgMs5GOtPouM02TcHINxvTreXPUbAbGRc,316
40
41
  typed_ffmpeg/utils/view.py,sha256=rS8gC_uwlUs9Y5UQFj1ur2NjVbZXkZGdGHND0btJpuM,1705
41
- typed_ffmpeg_compatible-2.5.0.dist-info/LICENSE,sha256=8Aaya5i_09Cou2i3QMxTwz6uHGzi_fGA4uhkco07-A4,1066
42
- typed_ffmpeg_compatible-2.5.0.dist-info/METADATA,sha256=bhXG9rmI7iIzYn0yRU6JVeP1k3FTxHsa_AznmKgSe9E,7249
43
- typed_ffmpeg_compatible-2.5.0.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
44
- typed_ffmpeg_compatible-2.5.0.dist-info/entry_points.txt,sha256=KfZmNsM16GT_lF1otASIN6E3i6xXHXoB1gMeEdlptjA,44
45
- typed_ffmpeg_compatible-2.5.0.dist-info/RECORD,,
42
+ typed_ffmpeg_compatible-2.6.0.dist-info/LICENSE,sha256=8Aaya5i_09Cou2i3QMxTwz6uHGzi_fGA4uhkco07-A4,1066
43
+ typed_ffmpeg_compatible-2.6.0.dist-info/METADATA,sha256=08agGVp8FMLf-GrIHbmlRI_HyBx_EbEnXMMM2gN8dN0,7249
44
+ typed_ffmpeg_compatible-2.6.0.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
45
+ typed_ffmpeg_compatible-2.6.0.dist-info/entry_points.txt,sha256=KfZmNsM16GT_lF1otASIN6E3i6xXHXoB1gMeEdlptjA,44
46
+ typed_ffmpeg_compatible-2.6.0.dist-info/RECORD,,