Metadata-Version: 2.4
Name: cut-fx
Version: 0.1.2
Summary: 559 named video transitions across four render backends
Project-URL: Homepage, https://github.com/tomastimelock/cut-fx
Project-URL: Repository, https://github.com/tomastimelock/cut-fx
Project-URL: Issues, https://github.com/tomastimelock/cut-fx/issues
Author-email: Trollfabriken AITrix AB <dev@trollfabriken.se>
Maintainer: opusmorale
License: MIT
License-File: LICENSE
Keywords: animation,ffmpeg,glsl,opencv,render,transition,video
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: End Users/Desktop
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Multimedia :: Graphics
Classifier: Topic :: Multimedia :: Video
Classifier: Topic :: Multimedia :: Video :: Conversion
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: av>=12.0
Requires-Dist: ffmpeg-python>=0.2
Requires-Dist: numpy>=1.24
Requires-Dist: opencv-python-headless>=4.8
Requires-Dist: pydantic>=2.5
Requires-Dist: video-arrange>=0.1
Provides-Extra: all
Requires-Dist: audio-arrange>=0.1; extra == 'all'
Requires-Dist: librosa>=0.10; extra == 'all'
Requires-Dist: lyric-sync>=0.1; extra == 'all'
Requires-Dist: moderngl-window>=2.4; extra == 'all'
Requires-Dist: moderngl>=5.10; extra == 'all'
Requires-Dist: web-overlay>=0.1; extra == 'all'
Provides-Extra: beats
Requires-Dist: audio-arrange>=0.1; extra == 'beats'
Requires-Dist: librosa>=0.10; extra == 'beats'
Provides-Extra: dev
Requires-Dist: build; extra == 'dev'
Requires-Dist: pillow; extra == 'dev'
Requires-Dist: pytest-cov>=4; extra == 'dev'
Requires-Dist: pytest>=7; extra == 'dev'
Requires-Dist: pyyaml; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Provides-Extra: lyrics
Requires-Dist: lyric-sync>=0.1; extra == 'lyrics'
Provides-Extra: overlay
Requires-Dist: web-overlay>=0.1; extra == 'overlay'
Provides-Extra: shader
Requires-Dist: moderngl-window>=2.4; extra == 'shader'
Requires-Dist: moderngl>=5.10; extra == 'shader'
Description-Content-Type: text/markdown

# cut-fx

559 named video transitions across four render backends, with GPU acceleration when available.

Built at Trollfabriken AITrix AB to make programmatic video rendering match the visual vocabulary
of consumer video editors. Five-hundred-and-fifty-nine named transitions across eleven taxonomy
buckets, mapped to thirty-four hand-written base engines and parametric variants. Four render
backends: ffmpeg filter_complex, NumPy/OpenCV procedural, HTML/CSS via web-overlay, and GLSL
shaders. GPU acceleration when available, CPU fallback when not. One API:
`apply_transition(clip_a, clip_b, transition="atomic_teleport", output=...)`.

---

## What it solves

Programmatic batch video rendering — AIMOS Insight municipal report videos, civic-education
explainers, pipeline-driven content — needs named, repeatable transitions. ffmpeg's `xfade` covers
dissolves. It does not cover 559 named effects. Writing each effect from scratch is not feasible.

| Problem | What cut-fx does |
|---|---|
| Named transitions not in ffmpeg `xfade` | 559 catalog entries map to 34 base engines |
| Different effects need different render tech | Four backends; catalog picks the right one |
| GPU present on some machines, absent on others | Runtime detection; CPU fallback always works |
| LLMs need a machine-readable transition spec | JSON schema export via `cut_fx.schema` |
| Beat-aligned cuts require audio analysis | `cut_fx.beats` wraps `audio-arrange` |
| Lyric-timed transitions | Optional `lyric-sync` integration |

---

## Installation

```bash
pip install cut-fx
```

Runtime requirement: **ffmpeg >= 6.0** must be on `PATH`.

```bash
# Ubuntu / Debian
sudo apt-get install ffmpeg

# macOS
brew install ffmpeg

# Windows
choco install ffmpeg
```

**Extras:**

| Extra | Installs | Enables |
|---|---|---|
| `overlay` | `web-overlay` | HTML/CSS/SVG transitions (light_fx, particle_nature, graphic_stylized) |
| `shader` | `moderngl`, `moderngl-window` | GLSL shader transitions; requires OpenGL 3.3+ |
| `beats` | `audio-arrange`, `librosa` | Beat-synced cut placement |
| `lyrics` | `lyric-sync` | Lyric-timed transitions |
| `all` | all of the above | Full feature set |
| `dev` | pytest, ruff, build, pillow | Development tools |

```bash
pip install "cut-fx[overlay,shader,beats]"
pip install "cut-fx[all]"
```

---

## Quick start

```python
from cut_fx import apply_transition

apply_transition(
    clip_a="intro.mp4",
    clip_b="main.mp4",
    transition="atomic_teleport",
    output="combined.mp4",
    overlap_seconds=0.8,
)
```

List what is available:

```python
from cut_fx import list_transitions, list_categories

list_categories()
# ['motion', 'rotation', 'shape_mask', 'optical_blur', 'light_fx',
#  'particle_nature', 'graphic_stylized', 'digital_glitch',
#  'grid_card_layout', 'film_retro', 'general_fx']

list_transitions(category="digital_glitch")
# ['Capture Glitch 2', 'Console Buzz', 'Cyber Beehive', 'Cyber Flare',
#  'Cyber Phantom', 'Hologram Shred', ...]
```

Inspect a specific transition:

```python
from cut_fx import get_transition_info

info = get_transition_info("cyber_phantom")
# TransitionInfo(
#     name='Cyber Phantom',
#     slug='cyber_phantom',
#     category='digital_glitch',
#     base_engine='glitch_scan',
#     parameters={'intensity': 0.7, 'scan_lines': 12, 'color_shift': True},
#     backends=['procedural', 'shader'],
# )
```

---

## The catalog

The catalog (`cut_fx/data/transitions_catalog.json`) lists 559 transition names across 11 taxonomy
buckets. Behind those names are 34 base engine implementations. Each base engine accepts parameters:
direction, easing, intensity, colour, shape, count, duration curve.

**The 559 → 34 design:** a named transition is a `(base_engine, parameter_set)` tuple. Adding a new
named variant means adding one JSON entry — no new Python code.

| Bucket | Transitions | Base engines | Example named variants |
|---|---:|---:|---|
| `motion` | 119 | 4 | 180 Wipe, Block Slides, Push Right |
| `light_fx` | 93 | 5 | Basic Flash, Bright Flash, Crescent Sparks |
| `grid_card_layout` | 47 | 3 | 3-block Swing, 3D Card 2, Bizarre Rubik |
| `rotation` | 46 | 3 | 3-flap Turn, 3D Flip, Calendar Flip |
| `shape_mask` | 44 | 4 | Circle, Comic Cutout, Blast Door |
| `film_retro` | 35 | 3 | B&W Arrows, Black Smoke, Black Fade |
| `optical_blur` | 31 | 3 | Auto Focus, Blur & Zoom, Blur Focus |
| `particle_nature` | 22 | 3 | Bubble Blur, Crystal Energy, Dust Flurry |
| `digital_glitch` | 16 | 4 | Cyber Phantom, Hologram Shred, Console Buzz |
| `graphic_stylized` | 12 | 2 | Comic Cut, Glowing Graffiti, Light Graffiti I |
| `general_fx` | 94 | (composed) | 3D Glass, Atomic Teleport, Acrylic Fades |

The `general_fx` bucket entries are composed from existing base engines. "Atomic Teleport" is a
light_fx flash + shape_mask radial + motion zoom-out, assembled in sequence.

Inspect catalog totals at runtime:

```python
from cut_fx.catalog import catalog_summary

print(catalog_summary())
# {'total': 559, 'categories': 11, 'base_engines': 34, ...}
```

---

## Render engines

Each catalog entry specifies which backend handles it. The dispatch is automatic.

| Engine | Handles | Implementation |
|---|---|---|
| **ffmpeg** | `motion`, `rotation`, `optical_blur`, simple wipes | `filter_complex` with crop/scale/translate/rotate expressions; `xfade` for cross-dissolves |
| **procedural** | `digital_glitch`, `film_retro`, custom pixel math | NumPy + OpenCV; frame-by-frame in Python |
| **overlay** | `light_fx`, `particle_nature`, `graphic_stylized`, `shape_mask` | HTML/CSS/SVG via `web-overlay`; alpha-composited over ffmpeg base |
| **shader** | Premium `light_fx` variants, refraction, fluid sim | GLSL via `moderngl`; optional; CPU fallback to procedural when absent |

The `overlay` engine requires `pip install "cut-fx[overlay]"` and a headless Chromium install
(`playwright install chromium`). The `shader` engine requires `pip install "cut-fx[shader]"` and
OpenGL 3.3+ context support.

Force a specific backend:

```python
apply_transition(
    clip_a="a.mp4",
    clip_b="b.mp4",
    transition="basic_flash",
    output="out.mp4",
    backend="procedural",  # override automatic selection
)
```

---

## Hardware acceleration

At import time, `cut_fx` probes for hardware encoders and GPU context.

```python
from cut_fx.gpu import gpu_status

print(gpu_status())
# {
#   'ffmpeg_hwaccel': 'nvenc',       # or 'videotoolbox', 'qsv', None
#   'opengl_available': True,
#   'opengl_renderer': 'NVIDIA GeForce RTX 3080',
#   'shader_backend': 'moderngl',    # or 'procedural' (fallback)
# }
```

**Acceleration paths:**

- ffmpeg hardware encoders: NVENC (NVIDIA), VideoToolbox (Apple), QSV (Intel). Detected via
  `ffmpeg -hwaccels`. Falls back to libx264/libx265 when none is found.
- GLSL shader engine: requires a real GPU context. Falls back to the procedural (NumPy/OpenCV)
  implementation automatically — output is identical, speed is lower.

Print a summary from the CLI:

```bash
cut-fx gpu
```

No GPU is required. All 559 transitions render on CPU.

---

## Beat-synced cuts

Requires `pip install "cut-fx[beats]"`.

```python
from cut_fx.beats import transitions_on_beats

transitions_on_beats(
    clips=["clip1.mp4", "clip2.mp4", "clip3.mp4", "clip4.mp4"],
    audio="track.mp3",
    transition="cyber_phantom",
    output="beat_video.mp4",
    beats_per_cut=2,       # place a cut every 2 beats
    overlap_seconds=0.4,
)
```

`transitions_on_beats` uses `audio-arrange` to extract beat timestamps, then places transitions
at beat boundaries. The `beats_per_cut` parameter controls cut density. Fractional values are
supported: `beats_per_cut=0.5` cuts on every half-beat.

For lyric-timed cuts, add `pip install "cut-fx[lyrics]"`:

```python
from cut_fx.beats import transitions_on_lyrics

transitions_on_lyrics(
    clips=["verse.mp4", "chorus.mp4"],
    lyrics_file="song.lrc",
    transition="light_burst",
    output="lyric_video.mp4",
)
```

---

## LLM integration

`cut_fx.schema` exports a JSON Schema describing every valid transition call. Pass it to an LLM
as a tool definition.

```python
from cut_fx.schema import transition_spec_schema

schema = transition_spec_schema()
# {
#   "$schema": "https://json-schema.org/draft/2020-12/schema",
#   "title": "TransitionSpec",
#   "type": "object",
#   "properties": {
#     "transition": {"type": "string", "enum": ["atomic_teleport", "cyber_phantom", ...]},
#     "overlap_seconds": {"type": "number", "minimum": 0.1, "maximum": 5.0},
#     "backend": {"type": "string", "enum": ["auto", "ffmpeg", "procedural", "overlay", "shader"]},
#     ...
#   },
#   "required": ["transition"]
# }
```

Feed the schema to an LLM as a function spec. The LLM returns a valid `TransitionSpec` object.
Pass it directly to `apply_transition`:

```python
from cut_fx import apply_transition
from cut_fx.config import TransitionConfig

spec = TransitionConfig(**llm_response)
apply_transition(clip_a="a.mp4", clip_b="b.mp4", output="out.mp4", **spec.model_dump())
```

---

## CLI

Apply a transition between two clips:

```bash
cut-fx apply intro.mp4 main.mp4 --transition atomic_teleport --output combined.mp4
```

List all transitions in a category:

```bash
cut-fx list --category digital_glitch
```

List all categories with transition counts:

```bash
cut-fx categories
```

Show detail for a named transition:

```bash
cut-fx info cyber_phantom
```

Render a short preview (3-second synthetic clips):

```bash
cut-fx preview atomic_teleport --output preview.mp4
```

Show GPU and hardware encoder status:

```bash
cut-fx gpu
```

---

## Package structure

```
cut-fx/
├── src/
│   └── cut_fx/
│       ├── __init__.py          # public API re-exports
│       ├── api.py               # apply_transition, apply_sequence, list_*
│       ├── catalog.py           # loads transitions_catalog.json
│       ├── config.py            # TransitionConfig (pydantic model)
│       ├── schema.py            # JSON schema export for LLM tool use
│       ├── gpu.py               # hardware detection
│       ├── beats.py             # beat-synced and lyric-synced cuts
│       ├── cli.py               # cut-fx command-line entry point
│       ├── data/
│       │   ├── transitions_catalog.json   # 559 entries
│       │   └── engine_mapping.json        # slug → base_engine lookup
│       └── engines/
│           ├── base.py          # abstract BaseEngine
│           ├── dispatcher.py    # selects engine per catalog entry
│           ├── ffmpeg/          # filter_complex builder
│           ├── procedural/      # NumPy + OpenCV per-frame
│           ├── overlay/
│           │   └── templates/   # HTML/CSS/SVG transition templates
│           └── shader/
│               └── glsl/        # .vert / .frag shader sources
├── tests/
├── pyproject.toml
├── README.md
└── LICENSE
```

---

© Trollfabriken AITrix AB — MIT licensed
