typed-ffmpeg-compatible 3.0.2a0__tar.gz → 3.2.1__tar.gz

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.
Files changed (108) hide show
  1. typed_ffmpeg_compatible-3.2.1/MANIFEST.in +41 -0
  2. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/PKG-INFO +36 -17
  3. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/README.md +22 -3
  4. typed_ffmpeg_compatible-3.2.1/pyproject.toml +122 -0
  5. typed_ffmpeg_compatible-3.2.1/setup.cfg +4 -0
  6. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/__init__.py +2 -1
  7. typed_ffmpeg_compatible-3.2.1/src/typed_ffmpeg/_version.py +21 -0
  8. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/compile/compile_cli.py +28 -4
  9. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/compile/compile_python.py +3 -4
  10. typed_ffmpeg_compatible-3.2.1/src/typed_ffmpeg/ffprobe/parse.py +133 -0
  11. typed_ffmpeg_compatible-3.2.1/src/typed_ffmpeg/ffprobe/probe.py +272 -0
  12. typed_ffmpeg_compatible-3.2.1/src/typed_ffmpeg/ffprobe/schema.py +455 -0
  13. typed_ffmpeg_compatible-3.2.1/src/typed_ffmpeg/ffprobe/xml2json.py +70 -0
  14. typed_ffmpeg_compatible-3.2.1/src/typed_ffmpeg/utils/lazy_eval/__init__.py +0 -0
  15. typed_ffmpeg_compatible-3.2.1/src/typed_ffmpeg_compatible.egg-info/PKG-INFO +201 -0
  16. typed_ffmpeg_compatible-3.2.1/src/typed_ffmpeg_compatible.egg-info/SOURCES.txt +61 -0
  17. typed_ffmpeg_compatible-3.2.1/src/typed_ffmpeg_compatible.egg-info/dependency_links.txt +1 -0
  18. typed_ffmpeg_compatible-3.2.1/src/typed_ffmpeg_compatible.egg-info/requires.txt +3 -0
  19. typed_ffmpeg_compatible-3.2.1/src/typed_ffmpeg_compatible.egg-info/top_level.txt +1 -0
  20. typed_ffmpeg_compatible-3.0.2a0/pyproject.toml +0 -102
  21. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/.gitignore +0 -3
  22. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/acrossover.json +0 -6
  23. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/afir.json +0 -9
  24. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/aiir.json +0 -6
  25. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/ainterleave.json +0 -9
  26. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/amerge.json +0 -9
  27. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/amix.json +0 -9
  28. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/amovie.json +0 -6
  29. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/anequalizer.json +0 -6
  30. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/aphasemeter.json +0 -6
  31. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/asegment.json +0 -6
  32. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/aselect.json +0 -6
  33. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/asplit.json +0 -6
  34. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/astreamselect.json +0 -9
  35. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/bm3d.json +0 -6
  36. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/channelsplit.json +0 -6
  37. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/concat.json +0 -9
  38. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/decimate.json +0 -6
  39. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/ebur128.json +0 -6
  40. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/extractplanes.json +0 -6
  41. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/fieldmatch.json +0 -6
  42. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/guided.json +0 -6
  43. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/headphone.json +0 -6
  44. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/hstack.json +0 -9
  45. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/interleave.json +0 -9
  46. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/join.json +0 -9
  47. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/libplacebo.json +0 -9
  48. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/limitdiff.json +0 -6
  49. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/mergeplanes.json +0 -6
  50. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/mix.json +0 -9
  51. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/movie.json +0 -6
  52. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/premultiply.json +0 -6
  53. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/segment.json +0 -6
  54. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/select.json +0 -6
  55. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/signature.json +0 -9
  56. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/split.json +0 -6
  57. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/streamselect.json +0 -9
  58. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/unpremultiply.json +0 -6
  59. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/vstack.json +0 -9
  60. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/xmedian.json +0 -9
  61. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/xstack.json +0 -9
  62. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/list/filters.json +0 -90747
  63. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/common/cache/list/options.json +0 -1694
  64. typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/probe.py +0 -75
  65. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/LICENSE +0 -0
  66. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/base.py +0 -0
  67. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/common/__init__.py +0 -0
  68. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/common/cache.py +0 -0
  69. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/common/schema.py +0 -0
  70. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/common/serialize.py +0 -0
  71. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/compile/__init__.py +0 -0
  72. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/compile/compile_json.py +0 -0
  73. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/compile/context.py +0 -0
  74. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/compile/validate.py +0 -0
  75. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/dag/__init__.py +0 -0
  76. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/dag/factory.py +0 -0
  77. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/dag/global_runnable/__init__.py +0 -0
  78. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/dag/global_runnable/global_args.py +0 -0
  79. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/dag/global_runnable/runnable.py +0 -0
  80. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/dag/io/__init__.py +0 -0
  81. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/dag/io/_input.py +0 -0
  82. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/dag/io/_output.py +0 -0
  83. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/dag/io/output_args.py +0 -0
  84. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/dag/nodes.py +0 -0
  85. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/dag/schema.py +0 -0
  86. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/dag/utils.py +0 -0
  87. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/exceptions.py +0 -0
  88. {typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/utils → typed_ffmpeg_compatible-3.2.1/src/typed_ffmpeg/ffprobe}/__init__.py +0 -0
  89. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/filters.py +0 -0
  90. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/info.py +0 -0
  91. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/py.typed +0 -0
  92. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/schema.py +0 -0
  93. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/sources.py +0 -0
  94. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/streams/__init__.py +0 -0
  95. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/streams/audio.py +0 -0
  96. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/streams/av.py +0 -0
  97. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/streams/channel_layout.py +0 -0
  98. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/streams/video.py +0 -0
  99. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/types.py +0 -0
  100. {typed_ffmpeg_compatible-3.0.2a0/src/typed_ffmpeg/utils/lazy_eval → typed_ffmpeg_compatible-3.2.1/src/typed_ffmpeg/utils}/__init__.py +0 -0
  101. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/utils/escaping.py +0 -0
  102. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/utils/forzendict.py +0 -0
  103. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/utils/lazy_eval/operator.py +0 -0
  104. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/utils/lazy_eval/schema.py +0 -0
  105. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/utils/run.py +0 -0
  106. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/utils/snapshot.py +0 -0
  107. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/utils/typing.py +0 -0
  108. {typed_ffmpeg_compatible-3.0.2a0 → typed_ffmpeg_compatible-3.2.1}/src/typed_ffmpeg/utils/view.py +0 -0
@@ -0,0 +1,41 @@
1
+ global-exclude **/tests/**
2
+ global-exclude **/scripts/**
3
+ global-exclude **/__snapshots__/**
4
+ global-exclude **/test_*
5
+ global-exclude **/*_test.py
6
+ global-exclude **/*.test.py
7
+ global-exclude **/conftest.py
8
+
9
+ # Exclude development and documentation files
10
+ global-exclude .coveragerc
11
+ global-exclude .eslintrc.json
12
+ global-exclude .gitignore
13
+ global-exclude .gitmodules
14
+ global-exclude .pre-commit-config.yaml
15
+ global-exclude .prettierignore
16
+ global-exclude .prettierrc
17
+ global-exclude blog.md
18
+ global-exclude codecov.yml
19
+ global-exclude mkdocs.yml
20
+ global-exclude pyproject-compatible.toml
21
+ global-exclude requirements.in
22
+ global-exclude requirements.txt
23
+ global-exclude tox.ini
24
+ global-exclude uv.lock
25
+ global-exclude README.ipynb
26
+
27
+ # Exclude development container files
28
+ global-exclude .devcontainer/**
29
+ global-exclude .github/**
30
+
31
+ # Exclude documentation
32
+ global-exclude docs/**
33
+
34
+ # Exclude uv configuration
35
+ global-exclude .uv/**
36
+
37
+ # Include only necessary files
38
+ include LICENSE
39
+ include README.md
40
+ include pyproject.toml
41
+ include MANIFEST.in
@@ -1,15 +1,13 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: typed-ffmpeg-compatible
3
- Version: 3.0.2a0
3
+ Version: 3.2.1
4
4
  Summary: Modern Python FFmpeg wrappers offer comprehensive support for complex filters, complete with detailed typing and documentation.
5
- Home-page: https://livingbio.github.io/typed-ffmpeg/
6
- License: MIT
5
+ Author-email: lucemia <lucemia@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://livingbio.github.io/typed-ffmpeg/
8
+ Project-URL: Repository, https://github.com/livingbio/typed-ffmpeg
7
9
  Keywords: ffmpeg,video,audio,multimedia,filter
8
- Author: lucemia
9
- Author-email: lucemia@gmail.com
10
- Requires-Python: >=3.10,<4.0
11
10
  Classifier: Intended Audience :: Developers
12
- Classifier: License :: OSI Approved :: MIT License
13
11
  Classifier: Natural Language :: English
14
12
  Classifier: Operating System :: OS Independent
15
13
  Classifier: Programming Language :: Python
@@ -17,15 +15,18 @@ Classifier: Programming Language :: Python :: 3
17
15
  Classifier: Programming Language :: Python :: 3.10
18
16
  Classifier: Programming Language :: Python :: 3.11
19
17
  Classifier: Programming Language :: Python :: 3.12
20
- Classifier: Topic :: Multimedia :: Sound/Audio
18
+ Classifier: Programming Language :: Python :: 3.13
21
19
  Classifier: Topic :: Multimedia :: Video
20
+ Classifier: Topic :: Multimedia :: Sound/Audio
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
22
  Classifier: Topic :: Software Development :: Libraries
23
23
  Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
24
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
25
- Provides-Extra: graph
26
- Requires-Dist: graphviz ; extra == "graph"
27
- Project-URL: Repository, https://github.com/livingbio/typed-ffmpeg
24
+ Requires-Python: >=3.10
28
25
  Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Provides-Extra: graph
28
+ Requires-Dist: graphviz; extra == "graph"
29
+ Dynamic: license-file
29
30
 
30
31
  ## typed-ffmpeg
31
32
 
@@ -44,6 +45,7 @@ Description-Content-Type: text/markdown
44
45
  - [Installation](#installation)
45
46
  - [Quick Usage](#quick-usage)
46
47
  - [Documentation](https://livingbio.github.io/typed-ffmpeg/)
48
+ - [Interactive Playground](#interactive-playground)
47
49
  - [Acknowledgements](#acknowledgements)
48
50
 
49
51
  ---
@@ -63,6 +65,7 @@ Description-Content-Type: text/markdown
63
65
  - **Validation and Auto-correction:** Assists in identifying and fixing errors within filter graphs.
64
66
  - **Input and Output Options Support:** Provide a more comprehensive interface for input and output options, including support for additional codecs and formats.
65
67
  - **Partial Evaluation:** Enhance the flexibility of filter graphs by enabling partial evaluation, allowing for modular construction and reuse.
68
+ - **Media File Analysis:** Built-in support for analyzing media files using FFmpeg's ffprobe utility, providing detailed metadata extraction with both dictionary and dataclass interfaces.
66
69
 
67
70
  ### Planned Features
68
71
 
@@ -101,12 +104,14 @@ Note: This requires Graphviz to be installed on your system.
101
104
 
102
105
  Here's how to quickly start using `typed-ffmpeg`:
103
106
 
104
-
105
-
106
-
107
107
  ```python
108
108
  import ffmpeg
109
109
 
110
+ # Analyze a media file
111
+ info = ffmpeg.probe("video.mp4")
112
+ print(f"Duration: {float(info['format']['duration']):.2f} seconds")
113
+ print(f"Streams: {len(info['streams'])}")
114
+
110
115
  # Flip video horizontally and output
111
116
  f = (
112
117
  ffmpeg
@@ -163,6 +168,21 @@ f
163
168
 
164
169
  See the [Usage](https://livingbio.github.io/typed-ffmpeg/usage/typed/) section in our documentation for more examples and detailed guides.
165
170
 
171
+ ---
172
+
173
+ ## Interactive Playground
174
+
175
+ Try out `typed-ffmpeg` directly in your browser with our [Interactive Playground](https://livingbio.github.io/typed-ffmpeg-playground/)! The playground provides a live environment where you can:
176
+
177
+ ![Interactive Playground](https://raw.githubusercontent.com/livingbio/typed-ffmpeg/main/docs/media/playground-screenshot.png)
178
+
179
+ - Experiment with FFmpeg filters and commands
180
+ - Visualize filter graphs in real-time
181
+ - Test different input/output configurations
182
+ - Learn through interactive examples
183
+ - Share your filter graphs with others
184
+
185
+ The playground is perfect for learning and prototyping FFmpeg filter chains without setting up a local environment.
166
186
 
167
187
  ---
168
188
 
@@ -179,4 +199,3 @@ This project is dedicated to my son, Austin, on his seventh birthday (February 2
179
199
  ---
180
200
 
181
201
  Feel free to check the [Documentation](https://livingbio.github.io/typed-ffmpeg/) for detailed information and more advanced features.
182
-
@@ -15,6 +15,7 @@
15
15
  - [Installation](#installation)
16
16
  - [Quick Usage](#quick-usage)
17
17
  - [Documentation](https://livingbio.github.io/typed-ffmpeg/)
18
+ - [Interactive Playground](#interactive-playground)
18
19
  - [Acknowledgements](#acknowledgements)
19
20
 
20
21
  ---
@@ -34,6 +35,7 @@
34
35
  - **Validation and Auto-correction:** Assists in identifying and fixing errors within filter graphs.
35
36
  - **Input and Output Options Support:** Provide a more comprehensive interface for input and output options, including support for additional codecs and formats.
36
37
  - **Partial Evaluation:** Enhance the flexibility of filter graphs by enabling partial evaluation, allowing for modular construction and reuse.
38
+ - **Media File Analysis:** Built-in support for analyzing media files using FFmpeg's ffprobe utility, providing detailed metadata extraction with both dictionary and dataclass interfaces.
37
39
 
38
40
  ### Planned Features
39
41
 
@@ -72,12 +74,14 @@ Note: This requires Graphviz to be installed on your system.
72
74
 
73
75
  Here's how to quickly start using `typed-ffmpeg`:
74
76
 
75
-
76
-
77
-
78
77
  ```python
79
78
  import ffmpeg
80
79
 
80
+ # Analyze a media file
81
+ info = ffmpeg.probe("video.mp4")
82
+ print(f"Duration: {float(info['format']['duration']):.2f} seconds")
83
+ print(f"Streams: {len(info['streams'])}")
84
+
81
85
  # Flip video horizontally and output
82
86
  f = (
83
87
  ffmpeg
@@ -134,6 +138,21 @@ f
134
138
 
135
139
  See the [Usage](https://livingbio.github.io/typed-ffmpeg/usage/typed/) section in our documentation for more examples and detailed guides.
136
140
 
141
+ ---
142
+
143
+ ## Interactive Playground
144
+
145
+ Try out `typed-ffmpeg` directly in your browser with our [Interactive Playground](https://livingbio.github.io/typed-ffmpeg-playground/)! The playground provides a live environment where you can:
146
+
147
+ ![Interactive Playground](https://raw.githubusercontent.com/livingbio/typed-ffmpeg/main/docs/media/playground-screenshot.png)
148
+
149
+ - Experiment with FFmpeg filters and commands
150
+ - Visualize filter graphs in real-time
151
+ - Test different input/output configurations
152
+ - Learn through interactive examples
153
+ - Share your filter graphs with others
154
+
155
+ The playground is perfect for learning and prototyping FFmpeg filter chains without setting up a local environment.
137
156
 
138
157
  ---
139
158
 
@@ -0,0 +1,122 @@
1
+ [project]
2
+ name = "typed-ffmpeg-compatible"
3
+ dynamic = ["version"]
4
+ description = "Modern Python FFmpeg wrappers offer comprehensive support for complex filters, complete with detailed typing and documentation."
5
+ authors = [
6
+ {name = "lucemia", email = "lucemia@gmail.com"}
7
+ ]
8
+ readme = "README.md"
9
+ requires-python = ">=3.10"
10
+ license = "MIT"
11
+ keywords = [
12
+ "ffmpeg",
13
+ "video",
14
+ "audio",
15
+ "multimedia",
16
+ "filter",
17
+ ]
18
+ classifiers = [
19
+ "Intended Audience :: Developers",
20
+ "Natural Language :: English",
21
+ "Operating System :: OS Independent",
22
+ "Programming Language :: Python",
23
+ "Programming Language :: Python :: 3",
24
+ "Programming Language :: Python :: 3.10",
25
+ "Programming Language :: Python :: 3.11",
26
+ "Programming Language :: Python :: 3.12",
27
+ "Programming Language :: Python :: 3.13",
28
+ "Topic :: Multimedia :: Video",
29
+ "Topic :: Multimedia :: Sound/Audio",
30
+ "Topic :: Software Development :: Libraries :: Python Modules",
31
+ "Topic :: Software Development :: Libraries",
32
+ "Topic :: Software Development :: Libraries :: Application Frameworks",
33
+ ]
34
+
35
+ [project.urls]
36
+ Homepage = "https://livingbio.github.io/typed-ffmpeg/"
37
+ Repository = "https://github.com/livingbio/typed-ffmpeg"
38
+
39
+ [project.optional-dependencies]
40
+ graph = ["graphviz"]
41
+
42
+ [build-system]
43
+ requires = ["setuptools>=61.0", "setuptools-scm>=6.2"]
44
+ build-backend = "setuptools.build_meta"
45
+
46
+ [tool.setuptools]
47
+ package-dir = {"" = "src"}
48
+
49
+ [tool.setuptools.packages.find]
50
+ where = ["src"]
51
+ include = ["typed_ffmpeg*"]
52
+ exclude = [
53
+ "**/tests/**",
54
+ "**/scripts/**",
55
+ "**/__snapshots__/**",
56
+ "**/test_*",
57
+ "**/*_test.py",
58
+ "**/*.test.py",
59
+ "**/conftest.py"
60
+ ]
61
+
62
+ [tool.setuptools.package-data]
63
+ "ffmpeg" = ["py.typed"]
64
+ "ffmpeg.common" = ["*"]
65
+ "ffmpeg.common.cache" = ["*"]
66
+ "ffmpeg.common.cache.list" = ["*"]
67
+ "ffmpeg.common.cache.FFMpegFilterManuallyDefined" = ["*"]
68
+ "ffmpeg.utils" = ["*"]
69
+ "ffmpeg.utils.lazy_eval" = ["*"]
70
+ exclude = [
71
+ "**/tests/**",
72
+ "**/scripts/**",
73
+ "**/__snapshots__/**",
74
+ "**/test_*",
75
+ "**/*_test.py",
76
+ "**/*.test.py",
77
+ "**/conftest.py"
78
+ ]
79
+
80
+ [tool.setuptools_scm]
81
+ version_scheme = "post-release"
82
+ local_scheme = "node-and-timestamp"
83
+ write_to = "src/typed_ffmpeg/_version.py"
84
+
85
+ [tool.mypy]
86
+ exclude = "^(migrations|commands|sandbox|samples|sdk)/"
87
+ show_error_codes = true
88
+ implicit_optional = true
89
+ follow_imports = "silent"
90
+ warn_redundant_casts = true
91
+ warn_unused_ignores = true
92
+ disallow_any_generics = true
93
+ check_untyped_defs = true
94
+ no_implicit_reexport = true
95
+ disallow_untyped_defs = true
96
+
97
+ [tool.ruff]
98
+ line-length = 88
99
+ exclude = ["**/cassettes/", "**/__snapshots__/"]
100
+ src = ["src"]
101
+
102
+ [tool.ruff.lint]
103
+ select = [
104
+ "E", # pycodestyle errors
105
+ "W", # pycodestyle warnings
106
+ "F", # pyflakes
107
+ "I", # isort
108
+ "B006", # flake8-bugbear: Do not use mutable data structures for argument defaults
109
+ "C4", # flake8-comprehensions
110
+ "UP", # pyupgrade
111
+ ]
112
+ ignore = [
113
+ "E741", # Ambiguous variable name: `I`
114
+ "E501", # Line too long
115
+ "E402", # Module level import not at top of file
116
+ "E712", # not work for pandas
117
+ "F811", # FIXME: we relay on it to share fixture across app
118
+ ]
119
+
120
+ [tool.ruff.format]
121
+ quote-style = "double"
122
+ docstring-code-format = true
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -26,8 +26,8 @@ from . import compile, dag, filters, sources
26
26
  from .base import afilter, filter_multi_output, input, merge_outputs, output, vfilter
27
27
  from .dag import Stream
28
28
  from .exceptions import FFMpegExecuteError, FFMpegTypeError, FFMpegValueError
29
+ from .ffprobe.probe import probe, probe_obj
29
30
  from .info import get_codecs, get_decoders, get_encoders
30
- from .probe import probe
31
31
  from .streams import AudioStream, AVStream, VideoStream
32
32
 
33
33
  __all__ = [
@@ -41,6 +41,7 @@ __all__ = [
41
41
  "FFMpegValueError",
42
42
  "Stream",
43
43
  "probe",
44
+ "probe_obj",
44
45
  "compile",
45
46
  "AudioStream",
46
47
  "VideoStream",
@@ -0,0 +1,21 @@
1
+ # file generated by setuptools-scm
2
+ # don't change, don't track in version control
3
+
4
+ __all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
5
+
6
+ TYPE_CHECKING = False
7
+ if TYPE_CHECKING:
8
+ from typing import Tuple
9
+ from typing import Union
10
+
11
+ VERSION_TUPLE = Tuple[Union[int, str], ...]
12
+ else:
13
+ VERSION_TUPLE = object
14
+
15
+ version: str
16
+ __version__: str
17
+ __version_tuple__: VERSION_TUPLE
18
+ version_tuple: VERSION_TUPLE
19
+
20
+ __version__ = version = '3.2.1'
21
+ __version_tuple__ = version_tuple = (3, 2, 1)
@@ -21,13 +21,10 @@ import shlex
21
21
  from collections import defaultdict
22
22
  from collections.abc import Mapping
23
23
 
24
- from ffmpeg.dag.factory import filter_node_factory
25
- from ffmpeg.streams.audio import AudioStream
26
- from ffmpeg.streams.video import VideoStream
27
-
28
24
  from ..base import input, merge_outputs, output
29
25
  from ..common.cache import load
30
26
  from ..common.schema import FFMpegFilter, FFMpegFilterDef, FFMpegOption, StreamType
27
+ from ..dag.factory import filter_node_factory
31
28
  from ..dag.nodes import (
32
29
  FilterableStream,
33
30
  FilterNode,
@@ -39,7 +36,9 @@ from ..dag.nodes import (
39
36
  from ..dag.schema import Node, Stream
40
37
  from ..exceptions import FFMpegValueError
41
38
  from ..schema import Default
39
+ from ..streams.audio import AudioStream
42
40
  from ..streams.av import AVStream
41
+ from ..streams.video import VideoStream
43
42
  from ..utils.escaping import escape
44
43
  from ..utils.lazy_eval.schema import LazyValue
45
44
  from ..utils.run import command_line
@@ -153,6 +152,15 @@ def parse_output(
153
152
  stream = parse_stream_selector(map_option, in_streams)
154
153
  inputs.append(stream)
155
154
 
155
+ if not inputs:
156
+ # NOTE: if there is no inputs, and there is only one input node
157
+ if len([k for k in in_streams if isinstance(in_streams[k], AVStream)]) == 1:
158
+ inputs = [
159
+ in_streams[k]
160
+ for k in in_streams
161
+ if isinstance(in_streams[k], AVStream)
162
+ ]
163
+
156
164
  assert inputs, f"No inputs found for output {filename}"
157
165
  export.append(output(*inputs, filename=filename, extra_options=options))
158
166
  buffer = []
@@ -677,6 +685,22 @@ def get_args_output_node(node: OutputNode, context: DAGContext) -> list[str]:
677
685
  if context:
678
686
  for input in node.inputs:
679
687
  if isinstance(input.node, InputNode):
688
+ # NOTE: specially rules,
689
+ # if there is only one input node,
690
+ # only one output node,
691
+ # the output node has only one input,
692
+ # and the stream selector is not specified,
693
+ # then the map can be ignore.
694
+ if (
695
+ input.index is None
696
+ and isinstance(input, AVStream)
697
+ and len([k for k in context.all_nodes if isinstance(k, InputNode)])
698
+ == 1
699
+ and len([k for k in context.all_nodes if isinstance(k, OutputNode)])
700
+ == 1
701
+ and len(node.inputs) == 1
702
+ ):
703
+ continue
680
704
  commands += ["-map", get_stream_label(input, context)]
681
705
  else:
682
706
  commands += ["-map", f"[{get_stream_label(input, context)}]"]
@@ -3,10 +3,6 @@ from __future__ import annotations
3
3
  from collections.abc import Mapping
4
4
  from typing import Any
5
5
 
6
- from ffmpeg.streams.audio import AudioStream
7
- from ffmpeg.streams.av import AVStream
8
- from ffmpeg.streams.video import VideoStream
9
-
10
6
  from ..common.cache import load
11
7
  from ..common.schema import FFMpegFilter
12
8
  from ..dag.nodes import (
@@ -19,6 +15,9 @@ from ..dag.nodes import (
19
15
  OutputStream,
20
16
  )
21
17
  from ..dag.schema import Node, Stream
18
+ from ..streams.audio import AudioStream
19
+ from ..streams.av import AVStream
20
+ from ..streams.video import VideoStream
22
21
  from .context import DAGContext
23
22
  from .validate import validate
24
23
 
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import json
4
+ import types
5
+ from dataclasses import is_dataclass
6
+ from typing import (
7
+ Any,
8
+ TypeGuard,
9
+ TypeVar,
10
+ Union,
11
+ cast,
12
+ get_args,
13
+ get_origin,
14
+ get_type_hints,
15
+ )
16
+
17
+ from .schema import ffprobeType, registered_types
18
+ from .xml2json import xml_string_to_json
19
+
20
+ T = TypeVar("T")
21
+
22
+
23
+ def _get_actual_type(type_hint: Any) -> type[Any]:
24
+ """
25
+ Get the actual type from a type hint, handling Optional and Union (including | syntax).
26
+
27
+ Args:
28
+ type_hint: The type hint to get the actual type from
29
+
30
+ Returns:
31
+ The actual type
32
+ """
33
+ # If type_hint is a string, evaluate it in the schema's module context
34
+ if isinstance(type_hint, str):
35
+ type_hint = registered_types[type_hint]
36
+
37
+ origin = get_origin(type_hint)
38
+ # Handle typing.Union and types.UnionType (for int | None in Python 3.10+)
39
+ if origin is Union or (hasattr(types, "UnionType") and origin is types.UnionType):
40
+ non_none_types = [t for t in get_args(type_hint) if t is not type(None)]
41
+ if non_none_types:
42
+ return _get_actual_type(non_none_types[0])
43
+ # Handle direct types.UnionType (for int | None in Python 3.10+)
44
+ if hasattr(types, "UnionType") and isinstance(type_hint, types.UnionType):
45
+ non_none_types = [t for t in type_hint.__args__ if t is not type(None)]
46
+ if non_none_types:
47
+ return _get_actual_type(non_none_types[0])
48
+ return type_hint
49
+
50
+
51
+ def is_dataclass_type(obj: type[Any]) -> TypeGuard[type[T]]:
52
+ """
53
+ Check if an object is a dataclass type.
54
+
55
+ Args:
56
+ obj: The object to check
57
+
58
+ Returns:
59
+ True if the object is a dataclass type, False otherwise
60
+ """
61
+ return is_dataclass(obj)
62
+
63
+
64
+ def _parse_obj_from_dict(data: Any, cls: type[T]) -> T | None:
65
+ """
66
+ Parse a dictionary into a dataclass instance.
67
+
68
+ Args:
69
+ data: The dictionary to parse
70
+ cls: The dataclass to parse into
71
+
72
+ Returns:
73
+ The parsed dataclass instance
74
+ """
75
+
76
+ if data is None:
77
+ return None
78
+
79
+ if isinstance(cls, type):
80
+ if cls is str:
81
+ return cast(T, str(data))
82
+ elif cls is int:
83
+ return cast(T, int(data))
84
+ elif cls is float:
85
+ return cast(T, float(data))
86
+ elif cls is bool:
87
+ return cast(T, bool(data))
88
+
89
+ if not isinstance(data, dict):
90
+ return cls()
91
+
92
+ if isinstance(cls, str): # NOTE: python 3.10
93
+ cls = registered_types[cls]
94
+
95
+ type_hints = get_type_hints(cls)
96
+ kwargs: dict[str, Any] = {}
97
+
98
+ for field_name, field_type in type_hints.items():
99
+ actual_type = _get_actual_type(field_type)
100
+
101
+ if get_origin(actual_type) is tuple:
102
+ tuple_args = get_args(actual_type)
103
+ if not tuple_args:
104
+ continue
105
+ item_type = tuple_args[0]
106
+ value = data.get(field_name, [])
107
+ if not isinstance(value, list):
108
+ value = [value]
109
+ kwargs[field_name] = tuple(
110
+ _parse_obj_from_dict(item, item_type) for item in value
111
+ )
112
+ continue
113
+
114
+ kwargs[field_name] = _parse_obj_from_dict(data.get(field_name), actual_type)
115
+
116
+ return cls(**kwargs)
117
+
118
+
119
+ def parse_ffprobe(xml_string: str) -> ffprobeType:
120
+ """
121
+ Parse ffprobe XML output into ffprobeType dataclass using JSON dict.
122
+
123
+ Args:
124
+ xml_string: The XML string to parse
125
+
126
+ Returns:
127
+ The parsed ffprobeType instance
128
+ """
129
+ json_str = xml_string_to_json(xml_string)
130
+ json_dict = json.loads(json_str)
131
+ # The root key is 'ffprobe'
132
+ root_data = json_dict.get("ffprobe", json_dict)
133
+ return _parse_obj_from_dict(root_data, ffprobeType) or ffprobeType()