cut-fx 0.1.2__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 (104) hide show
  1. cut_fx-0.1.2/.gitignore +21 -0
  2. cut_fx-0.1.2/LICENSE +21 -0
  3. cut_fx-0.1.2/MANIFEST.in +7 -0
  4. cut_fx-0.1.2/PKG-INFO +420 -0
  5. cut_fx-0.1.2/README.md +360 -0
  6. cut_fx-0.1.2/pyproject.toml +111 -0
  7. cut_fx-0.1.2/src/cut_fx/__init__.py +39 -0
  8. cut_fx-0.1.2/src/cut_fx/api.py +189 -0
  9. cut_fx-0.1.2/src/cut_fx/beats/__init__.py +15 -0
  10. cut_fx-0.1.2/src/cut_fx/beats/detector.py +49 -0
  11. cut_fx-0.1.2/src/cut_fx/beats/placer.py +76 -0
  12. cut_fx-0.1.2/src/cut_fx/catalog/__init__.py +19 -0
  13. cut_fx-0.1.2/src/cut_fx/catalog/loader.py +77 -0
  14. cut_fx-0.1.2/src/cut_fx/catalog/mapping.py +41 -0
  15. cut_fx-0.1.2/src/cut_fx/cli.py +330 -0
  16. cut_fx-0.1.2/src/cut_fx/composite/__init__.py +14 -0
  17. cut_fx-0.1.2/src/cut_fx/composite/composer.py +222 -0
  18. cut_fx-0.1.2/src/cut_fx/config.py +72 -0
  19. cut_fx-0.1.2/src/cut_fx/data/README.md +44 -0
  20. cut_fx-0.1.2/src/cut_fx/data/engine_mapping.json +7763 -0
  21. cut_fx-0.1.2/src/cut_fx/data/transitions_catalog.json +4568 -0
  22. cut_fx-0.1.2/src/cut_fx/engines/__init__.py +14 -0
  23. cut_fx-0.1.2/src/cut_fx/engines/base.py +49 -0
  24. cut_fx-0.1.2/src/cut_fx/engines/ffmpeg/__init__.py +109 -0
  25. cut_fx-0.1.2/src/cut_fx/engines/ffmpeg/filter_builder.py +161 -0
  26. cut_fx-0.1.2/src/cut_fx/engines/ffmpeg/grid_card.py +35 -0
  27. cut_fx-0.1.2/src/cut_fx/engines/ffmpeg/motion.py +66 -0
  28. cut_fx-0.1.2/src/cut_fx/engines/ffmpeg/optical_blur.py +35 -0
  29. cut_fx-0.1.2/src/cut_fx/engines/ffmpeg/rotation.py +28 -0
  30. cut_fx-0.1.2/src/cut_fx/engines/ffmpeg/runner.py +115 -0
  31. cut_fx-0.1.2/src/cut_fx/engines/ffmpeg/xfade.py +73 -0
  32. cut_fx-0.1.2/src/cut_fx/engines/overlay/__init__.py +224 -0
  33. cut_fx-0.1.2/src/cut_fx/engines/overlay/graphic.py +40 -0
  34. cut_fx-0.1.2/src/cut_fx/engines/overlay/light_fx.py +73 -0
  35. cut_fx-0.1.2/src/cut_fx/engines/overlay/particle.py +51 -0
  36. cut_fx-0.1.2/src/cut_fx/engines/overlay/renderer.py +164 -0
  37. cut_fx-0.1.2/src/cut_fx/engines/overlay/shape_mask.py +60 -0
  38. cut_fx-0.1.2/src/cut_fx/engines/overlay/templates/burst.html +89 -0
  39. cut_fx-0.1.2/src/cut_fx/engines/overlay/templates/flash.html +60 -0
  40. cut_fx-0.1.2/src/cut_fx/engines/overlay/templates/particle_sparks.html +140 -0
  41. cut_fx-0.1.2/src/cut_fx/engines/overlay/templates/shape_circle.html +74 -0
  42. cut_fx-0.1.2/src/cut_fx/engines/overlay/templates/streak.html +127 -0
  43. cut_fx-0.1.2/src/cut_fx/engines/procedural/__init__.py +101 -0
  44. cut_fx-0.1.2/src/cut_fx/engines/procedural/compositor.py +27 -0
  45. cut_fx-0.1.2/src/cut_fx/engines/procedural/film_retro.py +87 -0
  46. cut_fx-0.1.2/src/cut_fx/engines/procedural/frame_iterator.py +89 -0
  47. cut_fx-0.1.2/src/cut_fx/engines/procedural/glitch.py +116 -0
  48. cut_fx-0.1.2/src/cut_fx/engines/shader/__init__.py +95 -0
  49. cut_fx-0.1.2/src/cut_fx/engines/shader/context.py +75 -0
  50. cut_fx-0.1.2/src/cut_fx/engines/shader/glsl/dissolve.glsl +66 -0
  51. cut_fx-0.1.2/src/cut_fx/engines/shader/glsl/lens_flare.glsl +144 -0
  52. cut_fx-0.1.2/src/cut_fx/engines/shader/glsl/refraction.glsl +89 -0
  53. cut_fx-0.1.2/src/cut_fx/engines/shader/glsl/warp.glsl +90 -0
  54. cut_fx-0.1.2/src/cut_fx/engines/shader/runner.py +114 -0
  55. cut_fx-0.1.2/src/cut_fx/exceptions.py +43 -0
  56. cut_fx-0.1.2/src/cut_fx/hardware/__init__.py +15 -0
  57. cut_fx-0.1.2/src/cut_fx/hardware/ffmpeg_caps.py +104 -0
  58. cut_fx-0.1.2/src/cut_fx/hardware/gpu_caps.py +37 -0
  59. cut_fx-0.1.2/src/cut_fx/resolver.py +172 -0
  60. cut_fx-0.1.2/src/cut_fx/schema/__init__.py +15 -0
  61. cut_fx-0.1.2/src/cut_fx/schema/generator.py +99 -0
  62. cut_fx-0.1.2/src/cut_fx/schema/parser.py +64 -0
  63. cut_fx-0.1.2/tests/__init__.py +0 -0
  64. cut_fx-0.1.2/tests/conftest.py +38 -0
  65. cut_fx-0.1.2/tests/test_beat_detection_no_librosa.py +32 -0
  66. cut_fx-0.1.2/tests/test_catalog_categories_known.py +27 -0
  67. cut_fx-0.1.2/tests/test_catalog_loads.py +7 -0
  68. cut_fx-0.1.2/tests/test_catalog_unique_slugs.py +6 -0
  69. cut_fx-0.1.2/tests/test_cli_apply.py +35 -0
  70. cut_fx-0.1.2/tests/test_cli_categories.py +20 -0
  71. cut_fx-0.1.2/tests/test_cli_engines.py +13 -0
  72. cut_fx-0.1.2/tests/test_cli_gpu.py +21 -0
  73. cut_fx-0.1.2/tests/test_cli_info.py +19 -0
  74. cut_fx-0.1.2/tests/test_cli_list.py +15 -0
  75. cut_fx-0.1.2/tests/test_cli_list_filtered.py +25 -0
  76. cut_fx-0.1.2/tests/test_cli_preview.py +23 -0
  77. cut_fx-0.1.2/tests/test_cli_schema.py +22 -0
  78. cut_fx-0.1.2/tests/test_composite_atomic_teleport.py +41 -0
  79. cut_fx-0.1.2/tests/test_composite_random_sample.py +60 -0
  80. cut_fx-0.1.2/tests/test_composite_with_missing_engine.py +38 -0
  81. cut_fx-0.1.2/tests/test_detect_beats_on_known_track.py +47 -0
  82. cut_fx-0.1.2/tests/test_engine_mapping_complete.py +15 -0
  83. cut_fx-0.1.2/tests/test_ffmpeg_grid_card_each_base.py +26 -0
  84. cut_fx-0.1.2/tests/test_ffmpeg_hardware_detection.py +26 -0
  85. cut_fx-0.1.2/tests/test_ffmpeg_motion_each_base.py +27 -0
  86. cut_fx-0.1.2/tests/test_ffmpeg_optical_blur_each_base.py +25 -0
  87. cut_fx-0.1.2/tests/test_ffmpeg_rotation_each_base.py +26 -0
  88. cut_fx-0.1.2/tests/test_force_gpu_unavailable.py +30 -0
  89. cut_fx-0.1.2/tests/test_gpu_fallback.py +45 -0
  90. cut_fx-0.1.2/tests/test_overlay_graphic_each_base.py +27 -0
  91. cut_fx-0.1.2/tests/test_overlay_light_fx_each_base.py +36 -0
  92. cut_fx-0.1.2/tests/test_overlay_particle_each_base.py +28 -0
  93. cut_fx-0.1.2/tests/test_overlay_shape_mask_each_base.py +33 -0
  94. cut_fx-0.1.2/tests/test_procedural_film_retro_each_base.py +55 -0
  95. cut_fx-0.1.2/tests/test_procedural_glitch_each_base.py +27 -0
  96. cut_fx-0.1.2/tests/test_regression.py +94 -0
  97. cut_fx-0.1.2/tests/test_resolver_by_display_name.py +11 -0
  98. cut_fx-0.1.2/tests/test_resolver_by_slug.py +13 -0
  99. cut_fx-0.1.2/tests/test_resolver_case_insensitive.py +13 -0
  100. cut_fx-0.1.2/tests/test_resolver_unknown.py +22 -0
  101. cut_fx-0.1.2/tests/test_schema_generation.py +31 -0
  102. cut_fx-0.1.2/tests/test_schema_round_trip.py +32 -0
  103. cut_fx-0.1.2/tests/test_shader_smoke.py +27 -0
  104. cut_fx-0.1.2/tests/test_transitions_on_beats.py +71 -0
@@ -0,0 +1,21 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ build/
4
+ dist/
5
+ *.egg-info/
6
+ .coverage
7
+ .pytest_cache/
8
+ .ruff_cache/
9
+ .idea/
10
+ .vscode/
11
+ .env
12
+ .venv
13
+ venv/
14
+ ms-playwright/
15
+ **/playwright-browsers/
16
+
17
+ # Test artifacts
18
+ tests/fixtures/*.mp4
19
+ tests/fixtures/*.png
20
+ tests/fixtures/*.webm
21
+ /tmp_renders/
cut_fx-0.1.2/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Trollfabriken AITrix AB
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,7 @@
1
+ include README.md
2
+ include LICENSE
3
+ include pyproject.toml
4
+ recursive-include src *.py *.pyi
5
+ recursive-include src/cut_fx/data *.json
6
+ recursive-include src/cut_fx/engines/overlay/templates *.html *.css *.svg
7
+ recursive-include src/cut_fx/engines/shader/glsl *.glsl *.frag *.vert
cut_fx-0.1.2/PKG-INFO ADDED
@@ -0,0 +1,420 @@
1
+ Metadata-Version: 2.4
2
+ Name: cut-fx
3
+ Version: 0.1.2
4
+ Summary: 559 named video transitions across four render backends
5
+ Project-URL: Homepage, https://github.com/tomastimelock/cut-fx
6
+ Project-URL: Repository, https://github.com/tomastimelock/cut-fx
7
+ Project-URL: Issues, https://github.com/tomastimelock/cut-fx/issues
8
+ Author-email: Trollfabriken AITrix AB <dev@trollfabriken.se>
9
+ Maintainer: opusmorale
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: animation,ffmpeg,glsl,opencv,render,transition,video
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: End Users/Desktop
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Multimedia :: Graphics
24
+ Classifier: Topic :: Multimedia :: Video
25
+ Classifier: Topic :: Multimedia :: Video :: Conversion
26
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
27
+ Classifier: Typing :: Typed
28
+ Requires-Python: >=3.10
29
+ Requires-Dist: av>=12.0
30
+ Requires-Dist: ffmpeg-python>=0.2
31
+ Requires-Dist: numpy>=1.24
32
+ Requires-Dist: opencv-python-headless>=4.8
33
+ Requires-Dist: pydantic>=2.5
34
+ Requires-Dist: video-arrange>=0.1
35
+ Provides-Extra: all
36
+ Requires-Dist: audio-arrange>=0.1; extra == 'all'
37
+ Requires-Dist: librosa>=0.10; extra == 'all'
38
+ Requires-Dist: lyric-sync>=0.1; extra == 'all'
39
+ Requires-Dist: moderngl-window>=2.4; extra == 'all'
40
+ Requires-Dist: moderngl>=5.10; extra == 'all'
41
+ Requires-Dist: web-overlay>=0.1; extra == 'all'
42
+ Provides-Extra: beats
43
+ Requires-Dist: audio-arrange>=0.1; extra == 'beats'
44
+ Requires-Dist: librosa>=0.10; extra == 'beats'
45
+ Provides-Extra: dev
46
+ Requires-Dist: build; extra == 'dev'
47
+ Requires-Dist: pillow; extra == 'dev'
48
+ Requires-Dist: pytest-cov>=4; extra == 'dev'
49
+ Requires-Dist: pytest>=7; extra == 'dev'
50
+ Requires-Dist: pyyaml; extra == 'dev'
51
+ Requires-Dist: ruff>=0.4; extra == 'dev'
52
+ Provides-Extra: lyrics
53
+ Requires-Dist: lyric-sync>=0.1; extra == 'lyrics'
54
+ Provides-Extra: overlay
55
+ Requires-Dist: web-overlay>=0.1; extra == 'overlay'
56
+ Provides-Extra: shader
57
+ Requires-Dist: moderngl-window>=2.4; extra == 'shader'
58
+ Requires-Dist: moderngl>=5.10; extra == 'shader'
59
+ Description-Content-Type: text/markdown
60
+
61
+ # cut-fx
62
+
63
+ 559 named video transitions across four render backends, with GPU acceleration when available.
64
+
65
+ Built at Trollfabriken AITrix AB to make programmatic video rendering match the visual vocabulary
66
+ of consumer video editors. Five-hundred-and-fifty-nine named transitions across eleven taxonomy
67
+ buckets, mapped to thirty-four hand-written base engines and parametric variants. Four render
68
+ backends: ffmpeg filter_complex, NumPy/OpenCV procedural, HTML/CSS via web-overlay, and GLSL
69
+ shaders. GPU acceleration when available, CPU fallback when not. One API:
70
+ `apply_transition(clip_a, clip_b, transition="atomic_teleport", output=...)`.
71
+
72
+ ---
73
+
74
+ ## What it solves
75
+
76
+ Programmatic batch video rendering — AIMOS Insight municipal report videos, civic-education
77
+ explainers, pipeline-driven content — needs named, repeatable transitions. ffmpeg's `xfade` covers
78
+ dissolves. It does not cover 559 named effects. Writing each effect from scratch is not feasible.
79
+
80
+ | Problem | What cut-fx does |
81
+ |---|---|
82
+ | Named transitions not in ffmpeg `xfade` | 559 catalog entries map to 34 base engines |
83
+ | Different effects need different render tech | Four backends; catalog picks the right one |
84
+ | GPU present on some machines, absent on others | Runtime detection; CPU fallback always works |
85
+ | LLMs need a machine-readable transition spec | JSON schema export via `cut_fx.schema` |
86
+ | Beat-aligned cuts require audio analysis | `cut_fx.beats` wraps `audio-arrange` |
87
+ | Lyric-timed transitions | Optional `lyric-sync` integration |
88
+
89
+ ---
90
+
91
+ ## Installation
92
+
93
+ ```bash
94
+ pip install cut-fx
95
+ ```
96
+
97
+ Runtime requirement: **ffmpeg >= 6.0** must be on `PATH`.
98
+
99
+ ```bash
100
+ # Ubuntu / Debian
101
+ sudo apt-get install ffmpeg
102
+
103
+ # macOS
104
+ brew install ffmpeg
105
+
106
+ # Windows
107
+ choco install ffmpeg
108
+ ```
109
+
110
+ **Extras:**
111
+
112
+ | Extra | Installs | Enables |
113
+ |---|---|---|
114
+ | `overlay` | `web-overlay` | HTML/CSS/SVG transitions (light_fx, particle_nature, graphic_stylized) |
115
+ | `shader` | `moderngl`, `moderngl-window` | GLSL shader transitions; requires OpenGL 3.3+ |
116
+ | `beats` | `audio-arrange`, `librosa` | Beat-synced cut placement |
117
+ | `lyrics` | `lyric-sync` | Lyric-timed transitions |
118
+ | `all` | all of the above | Full feature set |
119
+ | `dev` | pytest, ruff, build, pillow | Development tools |
120
+
121
+ ```bash
122
+ pip install "cut-fx[overlay,shader,beats]"
123
+ pip install "cut-fx[all]"
124
+ ```
125
+
126
+ ---
127
+
128
+ ## Quick start
129
+
130
+ ```python
131
+ from cut_fx import apply_transition
132
+
133
+ apply_transition(
134
+ clip_a="intro.mp4",
135
+ clip_b="main.mp4",
136
+ transition="atomic_teleport",
137
+ output="combined.mp4",
138
+ overlap_seconds=0.8,
139
+ )
140
+ ```
141
+
142
+ List what is available:
143
+
144
+ ```python
145
+ from cut_fx import list_transitions, list_categories
146
+
147
+ list_categories()
148
+ # ['motion', 'rotation', 'shape_mask', 'optical_blur', 'light_fx',
149
+ # 'particle_nature', 'graphic_stylized', 'digital_glitch',
150
+ # 'grid_card_layout', 'film_retro', 'general_fx']
151
+
152
+ list_transitions(category="digital_glitch")
153
+ # ['Capture Glitch 2', 'Console Buzz', 'Cyber Beehive', 'Cyber Flare',
154
+ # 'Cyber Phantom', 'Hologram Shred', ...]
155
+ ```
156
+
157
+ Inspect a specific transition:
158
+
159
+ ```python
160
+ from cut_fx import get_transition_info
161
+
162
+ info = get_transition_info("cyber_phantom")
163
+ # TransitionInfo(
164
+ # name='Cyber Phantom',
165
+ # slug='cyber_phantom',
166
+ # category='digital_glitch',
167
+ # base_engine='glitch_scan',
168
+ # parameters={'intensity': 0.7, 'scan_lines': 12, 'color_shift': True},
169
+ # backends=['procedural', 'shader'],
170
+ # )
171
+ ```
172
+
173
+ ---
174
+
175
+ ## The catalog
176
+
177
+ The catalog (`cut_fx/data/transitions_catalog.json`) lists 559 transition names across 11 taxonomy
178
+ buckets. Behind those names are 34 base engine implementations. Each base engine accepts parameters:
179
+ direction, easing, intensity, colour, shape, count, duration curve.
180
+
181
+ **The 559 → 34 design:** a named transition is a `(base_engine, parameter_set)` tuple. Adding a new
182
+ named variant means adding one JSON entry — no new Python code.
183
+
184
+ | Bucket | Transitions | Base engines | Example named variants |
185
+ |---|---:|---:|---|
186
+ | `motion` | 119 | 4 | 180 Wipe, Block Slides, Push Right |
187
+ | `light_fx` | 93 | 5 | Basic Flash, Bright Flash, Crescent Sparks |
188
+ | `grid_card_layout` | 47 | 3 | 3-block Swing, 3D Card 2, Bizarre Rubik |
189
+ | `rotation` | 46 | 3 | 3-flap Turn, 3D Flip, Calendar Flip |
190
+ | `shape_mask` | 44 | 4 | Circle, Comic Cutout, Blast Door |
191
+ | `film_retro` | 35 | 3 | B&W Arrows, Black Smoke, Black Fade |
192
+ | `optical_blur` | 31 | 3 | Auto Focus, Blur & Zoom, Blur Focus |
193
+ | `particle_nature` | 22 | 3 | Bubble Blur, Crystal Energy, Dust Flurry |
194
+ | `digital_glitch` | 16 | 4 | Cyber Phantom, Hologram Shred, Console Buzz |
195
+ | `graphic_stylized` | 12 | 2 | Comic Cut, Glowing Graffiti, Light Graffiti I |
196
+ | `general_fx` | 94 | (composed) | 3D Glass, Atomic Teleport, Acrylic Fades |
197
+
198
+ The `general_fx` bucket entries are composed from existing base engines. "Atomic Teleport" is a
199
+ light_fx flash + shape_mask radial + motion zoom-out, assembled in sequence.
200
+
201
+ Inspect catalog totals at runtime:
202
+
203
+ ```python
204
+ from cut_fx.catalog import catalog_summary
205
+
206
+ print(catalog_summary())
207
+ # {'total': 559, 'categories': 11, 'base_engines': 34, ...}
208
+ ```
209
+
210
+ ---
211
+
212
+ ## Render engines
213
+
214
+ Each catalog entry specifies which backend handles it. The dispatch is automatic.
215
+
216
+ | Engine | Handles | Implementation |
217
+ |---|---|---|
218
+ | **ffmpeg** | `motion`, `rotation`, `optical_blur`, simple wipes | `filter_complex` with crop/scale/translate/rotate expressions; `xfade` for cross-dissolves |
219
+ | **procedural** | `digital_glitch`, `film_retro`, custom pixel math | NumPy + OpenCV; frame-by-frame in Python |
220
+ | **overlay** | `light_fx`, `particle_nature`, `graphic_stylized`, `shape_mask` | HTML/CSS/SVG via `web-overlay`; alpha-composited over ffmpeg base |
221
+ | **shader** | Premium `light_fx` variants, refraction, fluid sim | GLSL via `moderngl`; optional; CPU fallback to procedural when absent |
222
+
223
+ The `overlay` engine requires `pip install "cut-fx[overlay]"` and a headless Chromium install
224
+ (`playwright install chromium`). The `shader` engine requires `pip install "cut-fx[shader]"` and
225
+ OpenGL 3.3+ context support.
226
+
227
+ Force a specific backend:
228
+
229
+ ```python
230
+ apply_transition(
231
+ clip_a="a.mp4",
232
+ clip_b="b.mp4",
233
+ transition="basic_flash",
234
+ output="out.mp4",
235
+ backend="procedural", # override automatic selection
236
+ )
237
+ ```
238
+
239
+ ---
240
+
241
+ ## Hardware acceleration
242
+
243
+ At import time, `cut_fx` probes for hardware encoders and GPU context.
244
+
245
+ ```python
246
+ from cut_fx.gpu import gpu_status
247
+
248
+ print(gpu_status())
249
+ # {
250
+ # 'ffmpeg_hwaccel': 'nvenc', # or 'videotoolbox', 'qsv', None
251
+ # 'opengl_available': True,
252
+ # 'opengl_renderer': 'NVIDIA GeForce RTX 3080',
253
+ # 'shader_backend': 'moderngl', # or 'procedural' (fallback)
254
+ # }
255
+ ```
256
+
257
+ **Acceleration paths:**
258
+
259
+ - ffmpeg hardware encoders: NVENC (NVIDIA), VideoToolbox (Apple), QSV (Intel). Detected via
260
+ `ffmpeg -hwaccels`. Falls back to libx264/libx265 when none is found.
261
+ - GLSL shader engine: requires a real GPU context. Falls back to the procedural (NumPy/OpenCV)
262
+ implementation automatically — output is identical, speed is lower.
263
+
264
+ Print a summary from the CLI:
265
+
266
+ ```bash
267
+ cut-fx gpu
268
+ ```
269
+
270
+ No GPU is required. All 559 transitions render on CPU.
271
+
272
+ ---
273
+
274
+ ## Beat-synced cuts
275
+
276
+ Requires `pip install "cut-fx[beats]"`.
277
+
278
+ ```python
279
+ from cut_fx.beats import transitions_on_beats
280
+
281
+ transitions_on_beats(
282
+ clips=["clip1.mp4", "clip2.mp4", "clip3.mp4", "clip4.mp4"],
283
+ audio="track.mp3",
284
+ transition="cyber_phantom",
285
+ output="beat_video.mp4",
286
+ beats_per_cut=2, # place a cut every 2 beats
287
+ overlap_seconds=0.4,
288
+ )
289
+ ```
290
+
291
+ `transitions_on_beats` uses `audio-arrange` to extract beat timestamps, then places transitions
292
+ at beat boundaries. The `beats_per_cut` parameter controls cut density. Fractional values are
293
+ supported: `beats_per_cut=0.5` cuts on every half-beat.
294
+
295
+ For lyric-timed cuts, add `pip install "cut-fx[lyrics]"`:
296
+
297
+ ```python
298
+ from cut_fx.beats import transitions_on_lyrics
299
+
300
+ transitions_on_lyrics(
301
+ clips=["verse.mp4", "chorus.mp4"],
302
+ lyrics_file="song.lrc",
303
+ transition="light_burst",
304
+ output="lyric_video.mp4",
305
+ )
306
+ ```
307
+
308
+ ---
309
+
310
+ ## LLM integration
311
+
312
+ `cut_fx.schema` exports a JSON Schema describing every valid transition call. Pass it to an LLM
313
+ as a tool definition.
314
+
315
+ ```python
316
+ from cut_fx.schema import transition_spec_schema
317
+
318
+ schema = transition_spec_schema()
319
+ # {
320
+ # "$schema": "https://json-schema.org/draft/2020-12/schema",
321
+ # "title": "TransitionSpec",
322
+ # "type": "object",
323
+ # "properties": {
324
+ # "transition": {"type": "string", "enum": ["atomic_teleport", "cyber_phantom", ...]},
325
+ # "overlap_seconds": {"type": "number", "minimum": 0.1, "maximum": 5.0},
326
+ # "backend": {"type": "string", "enum": ["auto", "ffmpeg", "procedural", "overlay", "shader"]},
327
+ # ...
328
+ # },
329
+ # "required": ["transition"]
330
+ # }
331
+ ```
332
+
333
+ Feed the schema to an LLM as a function spec. The LLM returns a valid `TransitionSpec` object.
334
+ Pass it directly to `apply_transition`:
335
+
336
+ ```python
337
+ from cut_fx import apply_transition
338
+ from cut_fx.config import TransitionConfig
339
+
340
+ spec = TransitionConfig(**llm_response)
341
+ apply_transition(clip_a="a.mp4", clip_b="b.mp4", output="out.mp4", **spec.model_dump())
342
+ ```
343
+
344
+ ---
345
+
346
+ ## CLI
347
+
348
+ Apply a transition between two clips:
349
+
350
+ ```bash
351
+ cut-fx apply intro.mp4 main.mp4 --transition atomic_teleport --output combined.mp4
352
+ ```
353
+
354
+ List all transitions in a category:
355
+
356
+ ```bash
357
+ cut-fx list --category digital_glitch
358
+ ```
359
+
360
+ List all categories with transition counts:
361
+
362
+ ```bash
363
+ cut-fx categories
364
+ ```
365
+
366
+ Show detail for a named transition:
367
+
368
+ ```bash
369
+ cut-fx info cyber_phantom
370
+ ```
371
+
372
+ Render a short preview (3-second synthetic clips):
373
+
374
+ ```bash
375
+ cut-fx preview atomic_teleport --output preview.mp4
376
+ ```
377
+
378
+ Show GPU and hardware encoder status:
379
+
380
+ ```bash
381
+ cut-fx gpu
382
+ ```
383
+
384
+ ---
385
+
386
+ ## Package structure
387
+
388
+ ```
389
+ cut-fx/
390
+ ├── src/
391
+ │ └── cut_fx/
392
+ │ ├── __init__.py # public API re-exports
393
+ │ ├── api.py # apply_transition, apply_sequence, list_*
394
+ │ ├── catalog.py # loads transitions_catalog.json
395
+ │ ├── config.py # TransitionConfig (pydantic model)
396
+ │ ├── schema.py # JSON schema export for LLM tool use
397
+ │ ├── gpu.py # hardware detection
398
+ │ ├── beats.py # beat-synced and lyric-synced cuts
399
+ │ ├── cli.py # cut-fx command-line entry point
400
+ │ ├── data/
401
+ │ │ ├── transitions_catalog.json # 559 entries
402
+ │ │ └── engine_mapping.json # slug → base_engine lookup
403
+ │ └── engines/
404
+ │ ├── base.py # abstract BaseEngine
405
+ │ ├── dispatcher.py # selects engine per catalog entry
406
+ │ ├── ffmpeg/ # filter_complex builder
407
+ │ ├── procedural/ # NumPy + OpenCV per-frame
408
+ │ ├── overlay/
409
+ │ │ └── templates/ # HTML/CSS/SVG transition templates
410
+ │ └── shader/
411
+ │ └── glsl/ # .vert / .frag shader sources
412
+ ├── tests/
413
+ ├── pyproject.toml
414
+ ├── README.md
415
+ └── LICENSE
416
+ ```
417
+
418
+ ---
419
+
420
+ © Trollfabriken AITrix AB — MIT licensed