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.
- cut_fx-0.1.2/.gitignore +21 -0
- cut_fx-0.1.2/LICENSE +21 -0
- cut_fx-0.1.2/MANIFEST.in +7 -0
- cut_fx-0.1.2/PKG-INFO +420 -0
- cut_fx-0.1.2/README.md +360 -0
- cut_fx-0.1.2/pyproject.toml +111 -0
- cut_fx-0.1.2/src/cut_fx/__init__.py +39 -0
- cut_fx-0.1.2/src/cut_fx/api.py +189 -0
- cut_fx-0.1.2/src/cut_fx/beats/__init__.py +15 -0
- cut_fx-0.1.2/src/cut_fx/beats/detector.py +49 -0
- cut_fx-0.1.2/src/cut_fx/beats/placer.py +76 -0
- cut_fx-0.1.2/src/cut_fx/catalog/__init__.py +19 -0
- cut_fx-0.1.2/src/cut_fx/catalog/loader.py +77 -0
- cut_fx-0.1.2/src/cut_fx/catalog/mapping.py +41 -0
- cut_fx-0.1.2/src/cut_fx/cli.py +330 -0
- cut_fx-0.1.2/src/cut_fx/composite/__init__.py +14 -0
- cut_fx-0.1.2/src/cut_fx/composite/composer.py +222 -0
- cut_fx-0.1.2/src/cut_fx/config.py +72 -0
- cut_fx-0.1.2/src/cut_fx/data/README.md +44 -0
- cut_fx-0.1.2/src/cut_fx/data/engine_mapping.json +7763 -0
- cut_fx-0.1.2/src/cut_fx/data/transitions_catalog.json +4568 -0
- cut_fx-0.1.2/src/cut_fx/engines/__init__.py +14 -0
- cut_fx-0.1.2/src/cut_fx/engines/base.py +49 -0
- cut_fx-0.1.2/src/cut_fx/engines/ffmpeg/__init__.py +109 -0
- cut_fx-0.1.2/src/cut_fx/engines/ffmpeg/filter_builder.py +161 -0
- cut_fx-0.1.2/src/cut_fx/engines/ffmpeg/grid_card.py +35 -0
- cut_fx-0.1.2/src/cut_fx/engines/ffmpeg/motion.py +66 -0
- cut_fx-0.1.2/src/cut_fx/engines/ffmpeg/optical_blur.py +35 -0
- cut_fx-0.1.2/src/cut_fx/engines/ffmpeg/rotation.py +28 -0
- cut_fx-0.1.2/src/cut_fx/engines/ffmpeg/runner.py +115 -0
- cut_fx-0.1.2/src/cut_fx/engines/ffmpeg/xfade.py +73 -0
- cut_fx-0.1.2/src/cut_fx/engines/overlay/__init__.py +224 -0
- cut_fx-0.1.2/src/cut_fx/engines/overlay/graphic.py +40 -0
- cut_fx-0.1.2/src/cut_fx/engines/overlay/light_fx.py +73 -0
- cut_fx-0.1.2/src/cut_fx/engines/overlay/particle.py +51 -0
- cut_fx-0.1.2/src/cut_fx/engines/overlay/renderer.py +164 -0
- cut_fx-0.1.2/src/cut_fx/engines/overlay/shape_mask.py +60 -0
- cut_fx-0.1.2/src/cut_fx/engines/overlay/templates/burst.html +89 -0
- cut_fx-0.1.2/src/cut_fx/engines/overlay/templates/flash.html +60 -0
- cut_fx-0.1.2/src/cut_fx/engines/overlay/templates/particle_sparks.html +140 -0
- cut_fx-0.1.2/src/cut_fx/engines/overlay/templates/shape_circle.html +74 -0
- cut_fx-0.1.2/src/cut_fx/engines/overlay/templates/streak.html +127 -0
- cut_fx-0.1.2/src/cut_fx/engines/procedural/__init__.py +101 -0
- cut_fx-0.1.2/src/cut_fx/engines/procedural/compositor.py +27 -0
- cut_fx-0.1.2/src/cut_fx/engines/procedural/film_retro.py +87 -0
- cut_fx-0.1.2/src/cut_fx/engines/procedural/frame_iterator.py +89 -0
- cut_fx-0.1.2/src/cut_fx/engines/procedural/glitch.py +116 -0
- cut_fx-0.1.2/src/cut_fx/engines/shader/__init__.py +95 -0
- cut_fx-0.1.2/src/cut_fx/engines/shader/context.py +75 -0
- cut_fx-0.1.2/src/cut_fx/engines/shader/glsl/dissolve.glsl +66 -0
- cut_fx-0.1.2/src/cut_fx/engines/shader/glsl/lens_flare.glsl +144 -0
- cut_fx-0.1.2/src/cut_fx/engines/shader/glsl/refraction.glsl +89 -0
- cut_fx-0.1.2/src/cut_fx/engines/shader/glsl/warp.glsl +90 -0
- cut_fx-0.1.2/src/cut_fx/engines/shader/runner.py +114 -0
- cut_fx-0.1.2/src/cut_fx/exceptions.py +43 -0
- cut_fx-0.1.2/src/cut_fx/hardware/__init__.py +15 -0
- cut_fx-0.1.2/src/cut_fx/hardware/ffmpeg_caps.py +104 -0
- cut_fx-0.1.2/src/cut_fx/hardware/gpu_caps.py +37 -0
- cut_fx-0.1.2/src/cut_fx/resolver.py +172 -0
- cut_fx-0.1.2/src/cut_fx/schema/__init__.py +15 -0
- cut_fx-0.1.2/src/cut_fx/schema/generator.py +99 -0
- cut_fx-0.1.2/src/cut_fx/schema/parser.py +64 -0
- cut_fx-0.1.2/tests/__init__.py +0 -0
- cut_fx-0.1.2/tests/conftest.py +38 -0
- cut_fx-0.1.2/tests/test_beat_detection_no_librosa.py +32 -0
- cut_fx-0.1.2/tests/test_catalog_categories_known.py +27 -0
- cut_fx-0.1.2/tests/test_catalog_loads.py +7 -0
- cut_fx-0.1.2/tests/test_catalog_unique_slugs.py +6 -0
- cut_fx-0.1.2/tests/test_cli_apply.py +35 -0
- cut_fx-0.1.2/tests/test_cli_categories.py +20 -0
- cut_fx-0.1.2/tests/test_cli_engines.py +13 -0
- cut_fx-0.1.2/tests/test_cli_gpu.py +21 -0
- cut_fx-0.1.2/tests/test_cli_info.py +19 -0
- cut_fx-0.1.2/tests/test_cli_list.py +15 -0
- cut_fx-0.1.2/tests/test_cli_list_filtered.py +25 -0
- cut_fx-0.1.2/tests/test_cli_preview.py +23 -0
- cut_fx-0.1.2/tests/test_cli_schema.py +22 -0
- cut_fx-0.1.2/tests/test_composite_atomic_teleport.py +41 -0
- cut_fx-0.1.2/tests/test_composite_random_sample.py +60 -0
- cut_fx-0.1.2/tests/test_composite_with_missing_engine.py +38 -0
- cut_fx-0.1.2/tests/test_detect_beats_on_known_track.py +47 -0
- cut_fx-0.1.2/tests/test_engine_mapping_complete.py +15 -0
- cut_fx-0.1.2/tests/test_ffmpeg_grid_card_each_base.py +26 -0
- cut_fx-0.1.2/tests/test_ffmpeg_hardware_detection.py +26 -0
- cut_fx-0.1.2/tests/test_ffmpeg_motion_each_base.py +27 -0
- cut_fx-0.1.2/tests/test_ffmpeg_optical_blur_each_base.py +25 -0
- cut_fx-0.1.2/tests/test_ffmpeg_rotation_each_base.py +26 -0
- cut_fx-0.1.2/tests/test_force_gpu_unavailable.py +30 -0
- cut_fx-0.1.2/tests/test_gpu_fallback.py +45 -0
- cut_fx-0.1.2/tests/test_overlay_graphic_each_base.py +27 -0
- cut_fx-0.1.2/tests/test_overlay_light_fx_each_base.py +36 -0
- cut_fx-0.1.2/tests/test_overlay_particle_each_base.py +28 -0
- cut_fx-0.1.2/tests/test_overlay_shape_mask_each_base.py +33 -0
- cut_fx-0.1.2/tests/test_procedural_film_retro_each_base.py +55 -0
- cut_fx-0.1.2/tests/test_procedural_glitch_each_base.py +27 -0
- cut_fx-0.1.2/tests/test_regression.py +94 -0
- cut_fx-0.1.2/tests/test_resolver_by_display_name.py +11 -0
- cut_fx-0.1.2/tests/test_resolver_by_slug.py +13 -0
- cut_fx-0.1.2/tests/test_resolver_case_insensitive.py +13 -0
- cut_fx-0.1.2/tests/test_resolver_unknown.py +22 -0
- cut_fx-0.1.2/tests/test_schema_generation.py +31 -0
- cut_fx-0.1.2/tests/test_schema_round_trip.py +32 -0
- cut_fx-0.1.2/tests/test_shader_smoke.py +27 -0
- cut_fx-0.1.2/tests/test_transitions_on_beats.py +71 -0
cut_fx-0.1.2/.gitignore
ADDED
|
@@ -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.
|
cut_fx-0.1.2/MANIFEST.in
ADDED
|
@@ -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
|