clipwright 0.2.0__tar.gz → 0.3.0__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.
- {clipwright-0.2.0 → clipwright-0.3.0}/PKG-INFO +72 -1
- {clipwright-0.2.0 → clipwright-0.3.0}/README.md +71 -0
- {clipwright-0.2.0 → clipwright-0.3.0}/pyproject.toml +2 -2
- {clipwright-0.2.0 → clipwright-0.3.0}/src/clipwright/__init__.py +1 -1
- {clipwright-0.2.0 → clipwright-0.3.0}/src/clipwright/otio_utils.py +56 -0
- {clipwright-0.2.0 → clipwright-0.3.0}/src/clipwright/cli_io.py +0 -0
- {clipwright-0.2.0 → clipwright-0.3.0}/src/clipwright/envelope.py +0 -0
- {clipwright-0.2.0 → clipwright-0.3.0}/src/clipwright/errors.py +0 -0
- {clipwright-0.2.0 → clipwright-0.3.0}/src/clipwright/media.py +0 -0
- {clipwright-0.2.0 → clipwright-0.3.0}/src/clipwright/operations.py +0 -0
- {clipwright-0.2.0 → clipwright-0.3.0}/src/clipwright/process.py +0 -0
- {clipwright-0.2.0 → clipwright-0.3.0}/src/clipwright/project.py +0 -0
- {clipwright-0.2.0 → clipwright-0.3.0}/src/clipwright/py.typed +0 -0
- {clipwright-0.2.0 → clipwright-0.3.0}/src/clipwright/schemas.py +0 -0
- {clipwright-0.2.0 → clipwright-0.3.0}/src/clipwright/server.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: clipwright
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: MCP server group wrapping FFmpeg/OTIO. Provides primitives to manipulate video editing workflows from AI agents.
|
|
5
5
|
Author: satoh-y-0323
|
|
6
6
|
Author-email: satoh-y-0323 <shoma.papa.0323@gmail.com>
|
|
@@ -50,6 +50,38 @@ export CLIPWRIGHT_FFMPEG="C:/Users/<user>/AppData/Local/Microsoft/WinGet/Package
|
|
|
50
50
|
|
|
51
51
|
---
|
|
52
52
|
|
|
53
|
+
## Prerequisite: clipwright-transcribe (whisper-cli)
|
|
54
|
+
|
|
55
|
+
`clipwright-transcribe` requires the **whisper.cpp** binary (`whisper-cli`) and a ggml
|
|
56
|
+
model file. They are not installed via pip — obtain them separately.
|
|
57
|
+
|
|
58
|
+
### whisper-cli Binary
|
|
59
|
+
|
|
60
|
+
| Platform | How to install |
|
|
61
|
+
|---|---|
|
|
62
|
+
| **Windows** | Download the pre-built binary from [whisper.cpp Releases](https://github.com/ggerganov/whisper.cpp/releases) → `whisper-bin-x64.zip` (CPU) or `whisper-cublas-*-bin-x64.zip` (CUDA). Extract and place `whisper-cli.exe` in a directory on `PATH`, or set `CLIPWRIGHT_WHISPER`. |
|
|
63
|
+
| **macOS** | `brew install whisper-cpp` — installs `whisper-cli` on PATH automatically. |
|
|
64
|
+
| **Linux** | Build from source: `git clone https://github.com/ggerganov/whisper.cpp && cd whisper.cpp && cmake -B build && cmake --build build -j --config Release` — binary is at `build/bin/whisper-cli`. |
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# If whisper-cli is not on PATH, set the full path:
|
|
68
|
+
export CLIPWRIGHT_WHISPER=/path/to/whisper-cli
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### ggml Model File
|
|
72
|
+
|
|
73
|
+
Download a model (e.g. `ggml-base.bin`) from [Hugging Face](https://huggingface.co/ggerganov/whisper.cpp).
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
export CLIPWRIGHT_WHISPER_MODEL=/path/to/ggml-base.bin
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
If neither `CLIPWRIGHT_WHISPER` nor a `whisper-cli` binary on PATH is found, the
|
|
80
|
+
`clipwright_transcribe` tool returns `DEPENDENCY_MISSING` and integration tests are
|
|
81
|
+
automatically skipped.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
53
85
|
## Development Environment Setup
|
|
54
86
|
|
|
55
87
|
```bash
|
|
@@ -189,6 +221,26 @@ For details, see [docs/clipwright-spec.md](docs/clipwright-spec.md).
|
|
|
189
221
|
|
|
190
222
|
---
|
|
191
223
|
|
|
224
|
+
## Available Tools
|
|
225
|
+
|
|
226
|
+
| Package | MCP Tool | Description |
|
|
227
|
+
|---------|----------|-------------|
|
|
228
|
+
| `clipwright` (core) | `clipwright_inspect_media` | Probe a media file and return codec / duration / stream info |
|
|
229
|
+
| `clipwright` (core) | `clipwright_init_project` | Initialize a project directory with an empty OTIO timeline |
|
|
230
|
+
| `clipwright` (core) | `clipwright_read_timeline` | Read an OTIO timeline file and return its structure |
|
|
231
|
+
| `clipwright` (core) | `clipwright_write_timeline` | Write an OTIO timeline back to disk |
|
|
232
|
+
| `clipwright-silence` | `clipwright_detect_silence` | Detect silent regions in audio via FFmpeg `silencedetect` and annotate OTIO markers |
|
|
233
|
+
| `clipwright-loudness` | `clipwright_measure_loudness` | Measure EBU R128 loudness (integrated LUFS / true-peak) via FFmpeg |
|
|
234
|
+
| `clipwright-noise` | `clipwright_reduce_noise` | Annotate OTIO timeline with FFmpeg `afftdn` noise-reduction settings |
|
|
235
|
+
| `clipwright-transcribe` | `clipwright_transcribe` | Transcribe audio to text via whisper-cli and write word-level OTIO markers |
|
|
236
|
+
| `clipwright-bgm` | `clipwright_place_bgm` | Write BGM placement annotations (volume / fade / ducking) to OTIO timeline |
|
|
237
|
+
| `clipwright-render` | `clipwright_render` | Realize OTIO edit operations (trim / concat / filters) to an output media file via FFmpeg |
|
|
238
|
+
| `clipwright-wrap` | `clipwright_wrap_text` | Wrap long text lines with line-break annotations in OTIO timeline |
|
|
239
|
+
| `clipwright-scene` | `clipwright_detect_scenes` | Detect shot boundaries via FFmpeg `scdet` or PySceneDetect and write OTIO markers |
|
|
240
|
+
| `clipwright-frames` | `clipwright_extract_frames` | Extract still frames from video at specified times, scene boundaries, or fixed intervals; writes images, OTIO markers, and a JSON manifest |
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
192
244
|
## MCP Client Registration
|
|
193
245
|
|
|
194
246
|
Each clipwright tool is a standalone MCP server. Register them in your MCP client configuration (`.mcp.json` / `claude_desktop_config.json`):
|
|
@@ -208,11 +260,30 @@ Each clipwright tool is a standalone MCP server. Register them in your MCP clien
|
|
|
208
260
|
"env": {
|
|
209
261
|
"CLIPWRIGHT_FFMPEG": "/path/to/ffmpeg"
|
|
210
262
|
}
|
|
263
|
+
},
|
|
264
|
+
"clipwright-bgm": {
|
|
265
|
+
"command": "clipwright-bgm"
|
|
266
|
+
},
|
|
267
|
+
"clipwright-scene": {
|
|
268
|
+
"command": "clipwright-scene",
|
|
269
|
+
"env": {
|
|
270
|
+
"CLIPWRIGHT_FFMPEG": "/path/to/ffmpeg",
|
|
271
|
+
"CLIPWRIGHT_FFPROBE": "/path/to/ffprobe"
|
|
272
|
+
}
|
|
273
|
+
},
|
|
274
|
+
"clipwright-frames": {
|
|
275
|
+
"command": "clipwright-frames",
|
|
276
|
+
"env": {
|
|
277
|
+
"CLIPWRIGHT_FFMPEG": "/path/to/ffmpeg",
|
|
278
|
+
"CLIPWRIGHT_FFPROBE": "/path/to/ffprobe"
|
|
279
|
+
}
|
|
211
280
|
}
|
|
212
281
|
}
|
|
213
282
|
}
|
|
214
283
|
```
|
|
215
284
|
|
|
285
|
+
> Note: `clipwright-frames` lists both `CLIPWRIGHT_FFMPEG` (frame extraction) and `CLIPWRIGHT_FFPROBE` (video-stream detection and duration probing via `inspect_media`), so both variables must be configured.
|
|
286
|
+
|
|
216
287
|
Set `CLIPWRIGHT_FFMPEG` and `CLIPWRIGHT_FFPROBE` environment variables if ffmpeg is not in `PATH`.
|
|
217
288
|
|
|
218
289
|
---
|
|
@@ -37,6 +37,38 @@ export CLIPWRIGHT_FFMPEG="C:/Users/<user>/AppData/Local/Microsoft/WinGet/Package
|
|
|
37
37
|
|
|
38
38
|
---
|
|
39
39
|
|
|
40
|
+
## Prerequisite: clipwright-transcribe (whisper-cli)
|
|
41
|
+
|
|
42
|
+
`clipwright-transcribe` requires the **whisper.cpp** binary (`whisper-cli`) and a ggml
|
|
43
|
+
model file. They are not installed via pip — obtain them separately.
|
|
44
|
+
|
|
45
|
+
### whisper-cli Binary
|
|
46
|
+
|
|
47
|
+
| Platform | How to install |
|
|
48
|
+
|---|---|
|
|
49
|
+
| **Windows** | Download the pre-built binary from [whisper.cpp Releases](https://github.com/ggerganov/whisper.cpp/releases) → `whisper-bin-x64.zip` (CPU) or `whisper-cublas-*-bin-x64.zip` (CUDA). Extract and place `whisper-cli.exe` in a directory on `PATH`, or set `CLIPWRIGHT_WHISPER`. |
|
|
50
|
+
| **macOS** | `brew install whisper-cpp` — installs `whisper-cli` on PATH automatically. |
|
|
51
|
+
| **Linux** | Build from source: `git clone https://github.com/ggerganov/whisper.cpp && cd whisper.cpp && cmake -B build && cmake --build build -j --config Release` — binary is at `build/bin/whisper-cli`. |
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# If whisper-cli is not on PATH, set the full path:
|
|
55
|
+
export CLIPWRIGHT_WHISPER=/path/to/whisper-cli
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### ggml Model File
|
|
59
|
+
|
|
60
|
+
Download a model (e.g. `ggml-base.bin`) from [Hugging Face](https://huggingface.co/ggerganov/whisper.cpp).
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
export CLIPWRIGHT_WHISPER_MODEL=/path/to/ggml-base.bin
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
If neither `CLIPWRIGHT_WHISPER` nor a `whisper-cli` binary on PATH is found, the
|
|
67
|
+
`clipwright_transcribe` tool returns `DEPENDENCY_MISSING` and integration tests are
|
|
68
|
+
automatically skipped.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
40
72
|
## Development Environment Setup
|
|
41
73
|
|
|
42
74
|
```bash
|
|
@@ -176,6 +208,26 @@ For details, see [docs/clipwright-spec.md](docs/clipwright-spec.md).
|
|
|
176
208
|
|
|
177
209
|
---
|
|
178
210
|
|
|
211
|
+
## Available Tools
|
|
212
|
+
|
|
213
|
+
| Package | MCP Tool | Description |
|
|
214
|
+
|---------|----------|-------------|
|
|
215
|
+
| `clipwright` (core) | `clipwright_inspect_media` | Probe a media file and return codec / duration / stream info |
|
|
216
|
+
| `clipwright` (core) | `clipwright_init_project` | Initialize a project directory with an empty OTIO timeline |
|
|
217
|
+
| `clipwright` (core) | `clipwright_read_timeline` | Read an OTIO timeline file and return its structure |
|
|
218
|
+
| `clipwright` (core) | `clipwright_write_timeline` | Write an OTIO timeline back to disk |
|
|
219
|
+
| `clipwright-silence` | `clipwright_detect_silence` | Detect silent regions in audio via FFmpeg `silencedetect` and annotate OTIO markers |
|
|
220
|
+
| `clipwright-loudness` | `clipwright_measure_loudness` | Measure EBU R128 loudness (integrated LUFS / true-peak) via FFmpeg |
|
|
221
|
+
| `clipwright-noise` | `clipwright_reduce_noise` | Annotate OTIO timeline with FFmpeg `afftdn` noise-reduction settings |
|
|
222
|
+
| `clipwright-transcribe` | `clipwright_transcribe` | Transcribe audio to text via whisper-cli and write word-level OTIO markers |
|
|
223
|
+
| `clipwright-bgm` | `clipwright_place_bgm` | Write BGM placement annotations (volume / fade / ducking) to OTIO timeline |
|
|
224
|
+
| `clipwright-render` | `clipwright_render` | Realize OTIO edit operations (trim / concat / filters) to an output media file via FFmpeg |
|
|
225
|
+
| `clipwright-wrap` | `clipwright_wrap_text` | Wrap long text lines with line-break annotations in OTIO timeline |
|
|
226
|
+
| `clipwright-scene` | `clipwright_detect_scenes` | Detect shot boundaries via FFmpeg `scdet` or PySceneDetect and write OTIO markers |
|
|
227
|
+
| `clipwright-frames` | `clipwright_extract_frames` | Extract still frames from video at specified times, scene boundaries, or fixed intervals; writes images, OTIO markers, and a JSON manifest |
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
179
231
|
## MCP Client Registration
|
|
180
232
|
|
|
181
233
|
Each clipwright tool is a standalone MCP server. Register them in your MCP client configuration (`.mcp.json` / `claude_desktop_config.json`):
|
|
@@ -195,11 +247,30 @@ Each clipwright tool is a standalone MCP server. Register them in your MCP clien
|
|
|
195
247
|
"env": {
|
|
196
248
|
"CLIPWRIGHT_FFMPEG": "/path/to/ffmpeg"
|
|
197
249
|
}
|
|
250
|
+
},
|
|
251
|
+
"clipwright-bgm": {
|
|
252
|
+
"command": "clipwright-bgm"
|
|
253
|
+
},
|
|
254
|
+
"clipwright-scene": {
|
|
255
|
+
"command": "clipwright-scene",
|
|
256
|
+
"env": {
|
|
257
|
+
"CLIPWRIGHT_FFMPEG": "/path/to/ffmpeg",
|
|
258
|
+
"CLIPWRIGHT_FFPROBE": "/path/to/ffprobe"
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
"clipwright-frames": {
|
|
262
|
+
"command": "clipwright-frames",
|
|
263
|
+
"env": {
|
|
264
|
+
"CLIPWRIGHT_FFMPEG": "/path/to/ffmpeg",
|
|
265
|
+
"CLIPWRIGHT_FFPROBE": "/path/to/ffprobe"
|
|
266
|
+
}
|
|
198
267
|
}
|
|
199
268
|
}
|
|
200
269
|
}
|
|
201
270
|
```
|
|
202
271
|
|
|
272
|
+
> Note: `clipwright-frames` lists both `CLIPWRIGHT_FFMPEG` (frame extraction) and `CLIPWRIGHT_FFPROBE` (video-stream detection and duration probing via `inspect_media`), so both variables must be configured.
|
|
273
|
+
|
|
203
274
|
Set `CLIPWRIGHT_FFMPEG` and `CLIPWRIGHT_FFPROBE` environment variables if ffmpeg is not in `PATH`.
|
|
204
275
|
|
|
205
276
|
---
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "clipwright"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.3.0"
|
|
4
4
|
description = "MCP server group wrapping FFmpeg/OTIO. Provides primitives to manipulate video editing workflows from AI agents."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "MIT" }
|
|
@@ -84,4 +84,4 @@ skip_covered = false
|
|
|
84
84
|
|
|
85
85
|
# --- uv workspace ---
|
|
86
86
|
[tool.uv.workspace]
|
|
87
|
-
members = ["clipwright-bgm", "clipwright-loudness", "clipwright-noise", "clipwright-render", "clipwright-silence", "clipwright-transcribe", "clipwright-wrap"]
|
|
87
|
+
members = ["clipwright-bgm", "clipwright-frames", "clipwright-loudness", "clipwright-noise", "clipwright-render", "clipwright-scene", "clipwright-silence", "clipwright-transcribe", "clipwright-wrap"]
|
|
@@ -6,6 +6,7 @@ Time conversions use to_otio_time / from_otio_time imported from schemas.py.
|
|
|
6
6
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
|
+
import collections.abc
|
|
9
10
|
import contextlib
|
|
10
11
|
import os
|
|
11
12
|
import tempfile
|
|
@@ -198,6 +199,61 @@ def get_clipwright_metadata(obj: Any) -> dict[str, Any]:
|
|
|
198
199
|
return dict(obj.metadata.get("clipwright", {}))
|
|
199
200
|
|
|
200
201
|
|
|
202
|
+
# ===========================================================================
|
|
203
|
+
# Marker queries
|
|
204
|
+
# ===========================================================================
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def get_markers(
|
|
208
|
+
timeline: otio.schema.Timeline,
|
|
209
|
+
kind: str | None = None,
|
|
210
|
+
) -> list[otio.schema.Marker]:
|
|
211
|
+
"""Collect Marker objects from all tracks and clips in a Timeline.
|
|
212
|
+
|
|
213
|
+
Traversal order: tracks in track order → track markers first, then clip
|
|
214
|
+
markers in item order → individual markers in marker list order.
|
|
215
|
+
Time-based sorting is intentionally omitted so that the caller controls
|
|
216
|
+
ordering and round-trip stability is preserved even when two markers share
|
|
217
|
+
the same timestamp.
|
|
218
|
+
|
|
219
|
+
When *kind* is None all markers are returned. When *kind* is specified,
|
|
220
|
+
only markers whose ``metadata["clipwright"]["kind"]`` equals *kind* are
|
|
221
|
+
returned. Markers that have no ``metadata["clipwright"]`` dict, or where
|
|
222
|
+
that value is not a dict, are excluded when *kind* is given (defensive
|
|
223
|
+
pattern matching ``_marker_to_dict``).
|
|
224
|
+
"""
|
|
225
|
+
result: list[otio.schema.Marker] = []
|
|
226
|
+
for track in timeline.tracks:
|
|
227
|
+
# Track-level markers first
|
|
228
|
+
for marker in track.markers:
|
|
229
|
+
if _marker_matches_kind(marker, kind):
|
|
230
|
+
result.append(marker)
|
|
231
|
+
# Clip-level markers in item order
|
|
232
|
+
for item in track:
|
|
233
|
+
if isinstance(item, otio.schema.Clip):
|
|
234
|
+
for marker in item.markers:
|
|
235
|
+
if _marker_matches_kind(marker, kind):
|
|
236
|
+
result.append(marker)
|
|
237
|
+
return result
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def _marker_matches_kind(marker: otio.schema.Marker, kind: str | None) -> bool:
|
|
241
|
+
"""Return True when *marker* matches the given kind filter.
|
|
242
|
+
|
|
243
|
+
When *kind* is None every marker matches.
|
|
244
|
+
When *kind* is set, the marker must have metadata["clipwright"]["kind"] == kind.
|
|
245
|
+
Missing or non-dict clipwright metadata is treated as no-match (exclusive).
|
|
246
|
+
"""
|
|
247
|
+
if kind is None:
|
|
248
|
+
return True
|
|
249
|
+
cw_meta = marker.metadata.get("clipwright", {})
|
|
250
|
+
# OTIO stores metadata values as AnyDictionary, not a plain dict,
|
|
251
|
+
# so use Mapping to accept both types.
|
|
252
|
+
if not isinstance(cw_meta, collections.abc.Mapping):
|
|
253
|
+
return False
|
|
254
|
+
return cw_meta.get("kind") == kind
|
|
255
|
+
|
|
256
|
+
|
|
201
257
|
# ===========================================================================
|
|
202
258
|
# Timeline summary (§13.5 DC-AM-001 re: return all items, no truncation)
|
|
203
259
|
# ===========================================================================
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|