typed-ffmpeg-compatible 3.2.5__py3-none-any.whl → 3.3__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/_version.py CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '3.2.5'
21
- __version_tuple__ = version_tuple = (3, 2, 5)
20
+ __version__ = version = '3.3'
21
+ __version_tuple__ = version_tuple = (3, 3)
@@ -20,6 +20,7 @@ import re
20
20
  import shlex
21
21
  from collections import defaultdict
22
22
  from collections.abc import Mapping
23
+ from dataclasses import replace
23
24
 
24
25
  from ..base import input, merge_outputs, output
25
26
  from ..common.cache import load
@@ -141,6 +142,12 @@ def parse_stream_selector(
141
142
  stream = mapping[stream_label]
142
143
 
143
144
  if isinstance(stream, AVStream):
145
+ if "?" in selector:
146
+ optional = True
147
+ selector = selector.strip("?")
148
+ else:
149
+ optional = False
150
+
144
151
  if ":" in selector:
145
152
  if selector.count(":") == 1:
146
153
  stream_label, stream_type = selector.split(":", 1)
@@ -151,13 +158,14 @@ def parse_stream_selector(
151
158
 
152
159
  match stream_type:
153
160
  case "v":
154
- return stream.video_stream(stream_index)
161
+ return stream.video_stream(stream_index, optional)
155
162
  case "a":
156
- return stream.audio_stream(stream_index)
163
+ return stream.audio_stream(stream_index, optional)
157
164
  case "s":
158
- return stream.subtitle_stream(stream_index)
165
+ return stream.subtitle_stream(stream_index, optional)
159
166
  case _:
160
167
  raise FFMpegValueError(f"Unknown stream type: {stream_type}")
168
+ return replace(stream, optional=optional)
161
169
  return stream
162
170
 
163
171
 
@@ -812,7 +820,10 @@ def get_args_output_node(node: OutputNode, context: DAGContext) -> list[str]:
812
820
  and len(node.inputs) == 1
813
821
  ):
814
822
  continue
815
- commands += ["-map", get_stream_label(input, context)]
823
+ if not input.optional:
824
+ commands += ["-map", get_stream_label(input, context)]
825
+ else:
826
+ commands += ["-map", f"{get_stream_label(input, context)}?"]
816
827
  else:
817
828
  commands += ["-map", f"[{get_stream_label(input, context)}]"]
818
829
 
@@ -77,10 +77,7 @@ def get_input_var_name(
77
77
  case VideoStream():
78
78
  match stream.node:
79
79
  case InputNode():
80
- if stream.index is not None:
81
- return f"{get_output_var_name(stream.node, context)}.video_stream({stream.index})"
82
- else:
83
- return f"{get_output_var_name(stream.node, context)}.video"
80
+ return f"{get_output_var_name(stream.node, context)}.video_stream({stream.index}, optional={stream.optional})"
84
81
  case FilterNode():
85
82
  if filter_data_dict[stream.node.name].is_dynamic_output:
86
83
  return f"{get_output_var_name(stream.node, context)}.video({filter_stream_typed_index(stream, context)})"
@@ -94,10 +91,7 @@ def get_input_var_name(
94
91
  case AudioStream():
95
92
  match stream.node:
96
93
  case InputNode():
97
- if stream.index is not None:
98
- return f"{get_output_var_name(stream.node, context)}.audio_stream({stream.index})"
99
- else:
100
- return f"{get_output_var_name(stream.node, context)}.audio"
94
+ return f"{get_output_var_name(stream.node, context)}.audio_stream({stream.index}, optional={stream.optional})"
101
95
  case FilterNode():
102
96
  if filter_data_dict[stream.node.name].is_dynamic_output:
103
97
  return f"{get_output_var_name(stream.node, context)}.audio({filter_stream_typed_index(stream, context)})"
@@ -111,10 +105,7 @@ def get_input_var_name(
111
105
  case SubtitleStream():
112
106
  match stream.node:
113
107
  case InputNode():
114
- if stream.index is not None:
115
- return f"{get_output_var_name(stream.node, context)}.subtitle_stream({stream.index})"
116
- else:
117
- return f"{get_output_var_name(stream.node, context)}.subtitle"
108
+ return f"{get_output_var_name(stream.node, context)}.subtitle_stream({stream.index}, optional={stream.optional})"
118
109
  case OutputStream():
119
110
  return f"{get_output_var_name(stream.node, context)}"
120
111
  case GlobalStream():
@@ -49,6 +49,14 @@ class Stream(HashableBaseModel):
49
49
  See Also: [Stream specifiers](https://ffmpeg.org/ffmpeg.html#Stream-specifiers-1) `stream_index`
50
50
  """
51
51
 
52
+ optional: bool = False
53
+ """
54
+ Represents whether the stream is optional.
55
+
56
+ Note:
57
+ See Also: [Advanced options](https://ffmpeg.org/ffmpeg.html#Advanced-options)
58
+ """
59
+
52
60
  def view(self, format: Literal["png", "svg", "dot"] = "png") -> str:
53
61
  """
54
62
  Visualize the stream.
@@ -41,38 +41,47 @@ class AVStream(AudioStream, VideoStream):
41
41
  """
42
42
  return SubtitleStream(node=self.node, index=self.index)
43
43
 
44
- def video_stream(self, index: int | None = None) -> VideoStream:
44
+ def video_stream(
45
+ self, index: int | None = None, optional: bool = False
46
+ ) -> VideoStream:
45
47
  """
46
48
  Get the video stream from the input node with a specified index.
47
49
 
48
50
  Args:
49
51
  index: The index of the video stream.
52
+ optional: Whether the video stream is optional.
50
53
 
51
54
  Returns:
52
55
  VideoStream: The video stream from the input node.
53
56
  """
54
- return VideoStream(node=self.node, index=index)
57
+ return VideoStream(node=self.node, index=index, optional=optional)
55
58
 
56
- def audio_stream(self, index: int | None = None) -> AudioStream:
59
+ def audio_stream(
60
+ self, index: int | None = None, optional: bool = False
61
+ ) -> AudioStream:
57
62
  """
58
63
  Get the audio stream from the input node with a specified index.
59
64
 
60
65
  Args:
61
66
  index: The index of the audio stream.
67
+ optional: Whether the audio stream is optional.
62
68
 
63
69
  Returns:
64
70
  AudioStream: The audio stream from the input node.
65
71
  """
66
- return AudioStream(node=self.node, index=index)
72
+ return AudioStream(node=self.node, index=index, optional=optional)
67
73
 
68
- def subtitle_stream(self, index: int | None = None) -> SubtitleStream:
74
+ def subtitle_stream(
75
+ self, index: int | None = None, optional: bool = False
76
+ ) -> SubtitleStream:
69
77
  """
70
78
  Get the subtitle stream from the input node with a specified index.
71
79
 
72
80
  Args:
73
81
  index: The index of the subtitle stream.
82
+ optional: Whether the subtitle stream is optional.
74
83
 
75
84
  Returns:
76
85
  SubtitleStream: The subtitle stream from the input node.
77
86
  """
78
- return SubtitleStream(node=self.node, index=index)
87
+ return SubtitleStream(node=self.node, index=index, optional=optional)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: typed-ffmpeg-compatible
3
- Version: 3.2.5
3
+ Version: 3.3
4
4
  Summary: Modern Python FFmpeg wrappers offer comprehensive support for complex filters, complete with detailed typing and documentation.
5
5
  Author-email: lucemia <lucemia@gmail.com>
6
6
  License-Expression: MIT
@@ -1,5 +1,5 @@
1
1
  typed_ffmpeg/__init__.py,sha256=n1SZfENc9xhZ0eA3ZzkBNPuIY-Pt3-rOwB8-uUj5olU,1592
2
- typed_ffmpeg/_version.py,sha256=VJzOHz1iq5b2MvgVxeYYraqogSncMB5aG1qIW2TvKks,511
2
+ typed_ffmpeg/_version.py,sha256=lgTPmPhRGLqWrHHDFV9z5QiSuA5Dn5Dch5YbYnpAWj4,506
3
3
  typed_ffmpeg/base.py,sha256=C5Tqbx2I0c-09D7aXKZoGkspu-lAAeAhuOns5zr3PXQ,6304
4
4
  typed_ffmpeg/exceptions.py,sha256=D4SID6WOwkjVV8O8mAjrEDHWn-8BRDnK_jteaDof1SY,2474
5
5
  typed_ffmpeg/filters.py,sha256=_lBpGZgPHK3KgGVrw-TCdQEsBRuEXVIgwggYNGd80MU,110076
@@ -13,15 +13,15 @@ typed_ffmpeg/common/cache.py,sha256=j0JvfX7jewLpdJWxgo7Pwze0BkUJdYGHX2uGR8BZ-9M,
13
13
  typed_ffmpeg/common/schema.py,sha256=qM8yfMX9UU3EAQSNsTrr-SAmyqKx8eQCXTtu3RJWkEk,19673
14
14
  typed_ffmpeg/common/serialize.py,sha256=dLim0DBP5CdJ1JiMV9xEmmh1XMSIhBOWs61EopAL15s,7719
15
15
  typed_ffmpeg/compile/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
- typed_ffmpeg/compile/compile_cli.py,sha256=4XCkBC2WGP-P03XWVI3tgoBs--oQLvhuqyocSk8HhMw,32491
16
+ typed_ffmpeg/compile/compile_cli.py,sha256=Xuz4gAp4kqeywOxBNgPXH_Ie8lrmwfEdAl-HxMGo9kI,32892
17
17
  typed_ffmpeg/compile/compile_json.py,sha256=YCiTyfAnUVSbFr7BiQpmJYs13K5sa-xo77Iih33mb6I,992
18
- typed_ffmpeg/compile/compile_python.py,sha256=zxaG9DC0r8lO_cOV9HBN6cnxy3-wCZyvo3-4M47F84E,11914
18
+ typed_ffmpeg/compile/compile_python.py,sha256=YnnRRHE8TEUiqFF9DsqkYOwIcA2ejCYw12k-O5n825A,11506
19
19
  typed_ffmpeg/compile/context.py,sha256=macQ3HhEJ73j_WbWYtU9GCQCzcB_KQGAPimcuU-WOac,10946
20
20
  typed_ffmpeg/compile/validate.py,sha256=QsWksdvlRwWw6hnatFo-ABakms1qDXRbEmvMQGRLrD8,9579
21
21
  typed_ffmpeg/dag/__init__.py,sha256=qAApSNqjbZ1DtUaV5bSku9RwG7MpMPa1HJO764cSBt4,849
22
22
  typed_ffmpeg/dag/factory.py,sha256=2IMVKP_2UaTrlGXBg8YDx5KXBqhpScJiJQ87PRrppzY,3147
23
23
  typed_ffmpeg/dag/nodes.py,sha256=lfHChT8JqRs3UUDWtgrWnnXn845HXSD5S1QmHpIUT4U,20526
24
- typed_ffmpeg/dag/schema.py,sha256=dSq0o8e9qFazyd55k2yXcxbjoKZJEtqeROd91w1O3Wo,5728
24
+ typed_ffmpeg/dag/schema.py,sha256=xsVT46XgT3Usgb4N9OWdjP74MG9dBrXgZGbjsYhsUEM,5916
25
25
  typed_ffmpeg/dag/utils.py,sha256=hydh7_kjpOCw8WEGhXMxIXR4Ek-3DeoOt6esInuK2Xw,1941
26
26
  typed_ffmpeg/dag/global_runnable/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
27
  typed_ffmpeg/dag/global_runnable/global_args.py,sha256=ehLtx4v-XxqkmODhpE_gik0r79hs4Sa8TJnRsH9Fj1o,8043
@@ -37,7 +37,7 @@ typed_ffmpeg/ffprobe/schema.py,sha256=PBgBWYGO8wKnI0Lae9oCJ1Nprhv2ciPkdLrumzPVll
37
37
  typed_ffmpeg/ffprobe/xml2json.py,sha256=1TSuxR7SYc-M_-JmE-1khHGbXCapgW0Oap9kzL0nwNg,2455
38
38
  typed_ffmpeg/streams/__init__.py,sha256=Nt9uWpcVI1sQLl5Qt_kBCBcWOGZA1vczCQ0qvFbSko0,141
39
39
  typed_ffmpeg/streams/audio.py,sha256=2oNRhb5UetWtlPG3NW73AjUZoFPl303yMv-6W1sGWt0,259054
40
- typed_ffmpeg/streams/av.py,sha256=q7zqNyr44vBT7tiR2nKUGTf3g93HtNVA_WSwCiysmgI,2240
40
+ typed_ffmpeg/streams/av.py,sha256=qrsO692BG6OVMWVwIeuCKPntBGTR685lc2ULbGPTSkQ,2594
41
41
  typed_ffmpeg/streams/channel_layout.py,sha256=hGagaoc1tnWS8K1yiITp4f_92qq5e7C_zB15bHOL0DI,1162
42
42
  typed_ffmpeg/streams/subtitle.py,sha256=zhbRKUYH0C972Hurm4hK_dqLvT4Y6v_VNZFzQrkCm9A,141
43
43
  typed_ffmpeg/streams/video.py,sha256=cQwHfew75YO_dbZjmpUYd-nXt1JHN0M7suFKKe5UH5s,453576
@@ -51,9 +51,9 @@ typed_ffmpeg/utils/view.py,sha256=QCSlQoQkRBI-T0sWjiywGgM9DlKd8Te3CB2ZYX-pEVU,34
51
51
  typed_ffmpeg/utils/lazy_eval/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
52
52
  typed_ffmpeg/utils/lazy_eval/operator.py,sha256=QWybd-UH3VdDa8kgWkqAMi3WV0b0WF1d1JixQr6is2E,4136
53
53
  typed_ffmpeg/utils/lazy_eval/schema.py,sha256=WSg-E3MS3itN1AT6Dq4Z9btnRHEReuN3o6zruXou7h4,9623
54
- typed_ffmpeg_compatible-3.2.5.dist-info/licenses/LICENSE,sha256=8Aaya5i_09Cou2i3QMxTwz6uHGzi_fGA4uhkco07-A4,1066
55
- typed_ffmpeg_compatible-3.2.5.dist-info/METADATA,sha256=GSSr2eBSGWwgMrZZBx4wgAcRx6bas71nQjLxF8GVJPA,8385
56
- typed_ffmpeg_compatible-3.2.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
57
- typed_ffmpeg_compatible-3.2.5.dist-info/entry_points.txt,sha256=kUQvZ27paV-07qtkIFV-emKsYtjFOTw9kknBRSXPs04,45
58
- typed_ffmpeg_compatible-3.2.5.dist-info/top_level.txt,sha256=vuASJGVRQiNmhWY1pt0RXESWSNkknWXqWLIRAU7H_L4,13
59
- typed_ffmpeg_compatible-3.2.5.dist-info/RECORD,,
54
+ typed_ffmpeg_compatible-3.3.dist-info/licenses/LICENSE,sha256=8Aaya5i_09Cou2i3QMxTwz6uHGzi_fGA4uhkco07-A4,1066
55
+ typed_ffmpeg_compatible-3.3.dist-info/METADATA,sha256=FtrDI-yit60_yKnw5CfgYotZX9MLBIMiNtqCXBnDPmA,8383
56
+ typed_ffmpeg_compatible-3.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
57
+ typed_ffmpeg_compatible-3.3.dist-info/entry_points.txt,sha256=kUQvZ27paV-07qtkIFV-emKsYtjFOTw9kknBRSXPs04,45
58
+ typed_ffmpeg_compatible-3.3.dist-info/top_level.txt,sha256=vuASJGVRQiNmhWY1pt0RXESWSNkknWXqWLIRAU7H_L4,13
59
+ typed_ffmpeg_compatible-3.3.dist-info/RECORD,,