clipwright-render 0.1.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_render-0.1.0/PKG-INFO +258 -0
- clipwright_render-0.1.0/README.md +244 -0
- clipwright_render-0.1.0/pyproject.toml +85 -0
- clipwright_render-0.1.0/src/clipwright_render/__init__.py +3 -0
- clipwright_render-0.1.0/src/clipwright_render/plan.py +1595 -0
- clipwright_render-0.1.0/src/clipwright_render/py.typed +0 -0
- clipwright_render-0.1.0/src/clipwright_render/render.py +621 -0
- clipwright_render-0.1.0/src/clipwright_render/schemas.py +344 -0
- clipwright_render-0.1.0/src/clipwright_render/server.py +175 -0
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: clipwright-render
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MCP tool to realize OTIO timelines with FFmpeg. Completes segment extraction, concatenation, and trimming in a single transcode pass.
|
|
5
|
+
Author: satoh-y-0323
|
|
6
|
+
Author-email: satoh-y-0323 <shoma.papa.0323@gmail.com>
|
|
7
|
+
License: MIT
|
|
8
|
+
Requires-Dist: clipwright>=0.1.0
|
|
9
|
+
Requires-Dist: mcp[cli]>=1.27.2
|
|
10
|
+
Requires-Dist: opentimelineio>=0.18
|
|
11
|
+
Requires-Dist: pydantic>=2
|
|
12
|
+
Requires-Python: >=3.11
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# clipwright-render
|
|
16
|
+
|
|
17
|
+
MCP tool to realize OTIO timelines with FFmpeg.
|
|
18
|
+
|
|
19
|
+
Clipwright is a toolkit centered on "separation of detection (detect) and application (render)". detect-type tools only return annotations to OTIO without modifying media, and **this single `clipwright-render` tool performs all realization in one pass** (completes segment extraction, concatenation, and trimming in a single transcode pass).
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Prerequisites
|
|
24
|
+
|
|
25
|
+
This tool targets materials and timelines that meet the following conditions. Inputs outside these conditions return errors.
|
|
26
|
+
|
|
27
|
+
| Condition | Details |
|
|
28
|
+
|-----------|---------|
|
|
29
|
+
| Frame rate | CFR (constant frame rate) only. VFR (variable frame rate) not supported |
|
|
30
|
+
| Resolution | Fixed resolution only. Materials with per-frame resolution changes not supported |
|
|
31
|
+
| Source count | Only single source (1 file) in timeline |
|
|
32
|
+
| Video track | Required. No video not supported |
|
|
33
|
+
| Audio track | 0 or 1 stream only. If multiple, first audio stream adopted |
|
|
34
|
+
|
|
35
|
+
### Out of Scope (Planned for Future)
|
|
36
|
+
|
|
37
|
+
- VFR / resolution-changing materials
|
|
38
|
+
- Multiple source file concatenation
|
|
39
|
+
- Subtitle burn-in
|
|
40
|
+
- Transitions
|
|
41
|
+
- 2+ video tracks in timeline
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## FFmpeg Setup
|
|
46
|
+
|
|
47
|
+
**FFmpeg / FFprobe are not bundled with this package**. Install in your environment.
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# macOS (Homebrew)
|
|
51
|
+
brew install ffmpeg
|
|
52
|
+
|
|
53
|
+
# Ubuntu / Debian
|
|
54
|
+
sudo apt install ffmpeg
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
If `ffmpeg` / `ffprobe` are on PATH, it works as-is. In environments where PATH cannot be modified, explicitly specify paths with environment variables.
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
export CLIPWRIGHT_FFMPEG=/usr/local/bin/ffmpeg
|
|
61
|
+
export CLIPWRIGHT_FFPROBE=/usr/local/bin/ffprobe
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
> About license: This wrapper package itself is **MIT** licensed. Since FFmpeg binaries are not bundled, FFmpeg's LGPL / GPL redistribution obligations do not apply to this wrapper. Verify FFmpeg's own license (LGPL v2.1 / GPL v2) in your environment.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Installation
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
uv sync
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Usage
|
|
77
|
+
|
|
78
|
+
### MCP Tool (`clipwright_render`)
|
|
79
|
+
|
|
80
|
+
Invoked from Claude / agents via MCP.
|
|
81
|
+
|
|
82
|
+
```jsonc
|
|
83
|
+
{
|
|
84
|
+
"tool": "clipwright_render",
|
|
85
|
+
"arguments": {
|
|
86
|
+
"timeline": "/path/to/timeline.otio",
|
|
87
|
+
"output": "/path/to/output.mp4",
|
|
88
|
+
"dry_run": false,
|
|
89
|
+
"options": {
|
|
90
|
+
"video_codec": "libx264",
|
|
91
|
+
"audio_codec": "aac",
|
|
92
|
+
"width": 1920,
|
|
93
|
+
"height": 1080,
|
|
94
|
+
"fps": 29.97,
|
|
95
|
+
"crf": 23,
|
|
96
|
+
"overwrite": false
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**Arguments**
|
|
103
|
+
|
|
104
|
+
| Argument | Type | Required | Description |
|
|
105
|
+
|----------|------|----------|-------------|
|
|
106
|
+
| `timeline` | string | yes | Input OTIO file path |
|
|
107
|
+
| `output` | string | yes | Output file path (`.mp4` / `.mkv` / `.mov` / `.webm`) |
|
|
108
|
+
| `dry_run` | bool | optional (default `false`) | If `true`, returns plan without actual rendering |
|
|
109
|
+
| `options` | object | optional | Output options (see RenderOptions below) |
|
|
110
|
+
|
|
111
|
+
**RenderOptions**
|
|
112
|
+
|
|
113
|
+
| Field | Type | Description |
|
|
114
|
+
|-------|------|-------------|
|
|
115
|
+
| `video_codec` | string \| null | Video codec (e.g. `libx264`, default: inherit from source) |
|
|
116
|
+
| `audio_codec` | string \| null | Audio codec (e.g. `aac`, default: inherit from source) |
|
|
117
|
+
| `width` | int \| null | Output width (must be set with `height`) |
|
|
118
|
+
| `height` | int \| null | Output height (must be set with `width`) |
|
|
119
|
+
| `fps` | float \| null | Output frame rate |
|
|
120
|
+
| `crf` | int \| null | Quality CRF value (0-51) |
|
|
121
|
+
| `overwrite` | bool | If `true`, overwrite existing output file (default `false`) |
|
|
122
|
+
|
|
123
|
+
`width` / `height` must both be specified or both `null`. Specifying only one is an error.
|
|
124
|
+
|
|
125
|
+
**Return Value (Success)**
|
|
126
|
+
|
|
127
|
+
```jsonc
|
|
128
|
+
{
|
|
129
|
+
"ok": true,
|
|
130
|
+
"summary": "2 clips → 45.2 sec / 42.1 MB / outputs/out.mp4",
|
|
131
|
+
"data": {
|
|
132
|
+
"output_path": "/path/to/output.mp4",
|
|
133
|
+
"duration_sec": 45.2,
|
|
134
|
+
"size_bytes": 44150784,
|
|
135
|
+
"clip_count": 2
|
|
136
|
+
},
|
|
137
|
+
"artifacts": ["/path/to/output.mp4"],
|
|
138
|
+
"warnings": []
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**Return Value (dry_run)**
|
|
143
|
+
|
|
144
|
+
```jsonc
|
|
145
|
+
{
|
|
146
|
+
"ok": true,
|
|
147
|
+
"summary": "dry_run: 2 segments / estimated 45.2 sec / approx 42.1 MB",
|
|
148
|
+
"data": {
|
|
149
|
+
"dry_run": true,
|
|
150
|
+
"clip_count": 2,
|
|
151
|
+
"estimated_duration_sec": 45.2,
|
|
152
|
+
"estimated_size_bytes": 44150784,
|
|
153
|
+
"ffmpeg_args": ["ffmpeg", "-i", "source.mp4", "-filter_complex", "..."]
|
|
154
|
+
},
|
|
155
|
+
"artifacts": [],
|
|
156
|
+
"warnings": []
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
`estimated_size_bytes` is calculated from source bitrate obtained by FFprobe and output duration. If bitrate cannot be obtained, it is `null` with reason in `warnings`. If any of `video_codec` / `width` / `height` / `fps` / `crf` are specified, estimation based on source bitrate may differ significantly from actual, so `warnings` includes a note.
|
|
161
|
+
|
|
162
|
+
**Return Value (Error)**
|
|
163
|
+
|
|
164
|
+
```jsonc
|
|
165
|
+
{
|
|
166
|
+
"ok": false,
|
|
167
|
+
"error": {
|
|
168
|
+
"code": "FILE_NOT_FOUND",
|
|
169
|
+
"message": "Timeline file not found: /path/to/timeline.otio",
|
|
170
|
+
"hint": "Verify the file path"
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Main error codes:
|
|
176
|
+
|
|
177
|
+
| Code | Meaning |
|
|
178
|
+
|------|---------|
|
|
179
|
+
| `FILE_NOT_FOUND` | Timeline / source / output directory does not exist |
|
|
180
|
+
| `INVALID_INPUT` | Invalid extension / existing output with overwrite=false / empty timeline |
|
|
181
|
+
| `PATH_NOT_ALLOWED` | Output path is same as input source |
|
|
182
|
+
| `UNSUPPORTED_OPERATION` | No video / multiple sources / Transition / 2+ video tracks |
|
|
183
|
+
| `PROBE_FAILED` | FFprobe analysis failed |
|
|
184
|
+
| `SUBPROCESS_FAILED` | FFmpeg exit code non-zero |
|
|
185
|
+
| `SUBPROCESS_TIMEOUT` | FFmpeg timeout (`max(300, duration_sec × 10)` seconds) |
|
|
186
|
+
| `DEPENDENCY_MISSING` | ffmpeg / ffprobe not found in PATH or environment variables |
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
### CLI (`clipwright-render`)
|
|
191
|
+
|
|
192
|
+
Can be run directly from command line. Shares same logic as MCP tool.
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
clipwright-render <timeline> <output> [options]
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
**Arguments**
|
|
199
|
+
|
|
200
|
+
```
|
|
201
|
+
clipwright-render <timeline> <output>
|
|
202
|
+
[--dry-run]
|
|
203
|
+
[--video-codec C]
|
|
204
|
+
[--audio-codec C]
|
|
205
|
+
[--width W --height H]
|
|
206
|
+
[--fps F]
|
|
207
|
+
[--crf N]
|
|
208
|
+
[--overwrite]
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**Example: Verify plan with dry_run before rendering**
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
# Verify plan first
|
|
215
|
+
clipwright-render timeline.otio out.mp4 --dry-run
|
|
216
|
+
|
|
217
|
+
# Render if OK
|
|
218
|
+
clipwright-render timeline.otio out.mp4 --video-codec libx264 --crf 23
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**Example: Render with specified resolution**
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
clipwright-render timeline.otio out.mp4 --width 1280 --height 720 --fps 29.97
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## Testing
|
|
230
|
+
|
|
231
|
+
### Unit Tests (FFmpeg Not Required)
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
uv run --package clipwright-render pytest clipwright-render/tests/ -m "not integration"
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Integration Tests (FFmpeg Required)
|
|
238
|
+
|
|
239
|
+
Tests that verify single-source concatenation and output using actual FFmpeg. Automatically skipped in environments without FFmpeg.
|
|
240
|
+
|
|
241
|
+
Set environment variables before running:
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
export CLIPWRIGHT_FFMPEG=/path/to/ffmpeg
|
|
245
|
+
export CLIPWRIGHT_FFPROBE=/path/to/ffprobe
|
|
246
|
+
|
|
247
|
+
uv run --package clipwright-render pytest clipwright-render/tests/ -m integration
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
> Integration tests skip if `CLIPWRIGHT_FFMPEG` / `CLIPWRIGHT_FFPROBE` are not set. Set these variables when running in CI.
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## License
|
|
255
|
+
|
|
256
|
+
This wrapper package itself is **MIT** licensed.
|
|
257
|
+
|
|
258
|
+
Since FFmpeg binaries are not bundled, FFmpeg's LGPL v2.1 / GPL v2 redistribution obligations do not apply to this package.
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# clipwright-render
|
|
2
|
+
|
|
3
|
+
MCP tool to realize OTIO timelines with FFmpeg.
|
|
4
|
+
|
|
5
|
+
Clipwright is a toolkit centered on "separation of detection (detect) and application (render)". detect-type tools only return annotations to OTIO without modifying media, and **this single `clipwright-render` tool performs all realization in one pass** (completes segment extraction, concatenation, and trimming in a single transcode pass).
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Prerequisites
|
|
10
|
+
|
|
11
|
+
This tool targets materials and timelines that meet the following conditions. Inputs outside these conditions return errors.
|
|
12
|
+
|
|
13
|
+
| Condition | Details |
|
|
14
|
+
|-----------|---------|
|
|
15
|
+
| Frame rate | CFR (constant frame rate) only. VFR (variable frame rate) not supported |
|
|
16
|
+
| Resolution | Fixed resolution only. Materials with per-frame resolution changes not supported |
|
|
17
|
+
| Source count | Only single source (1 file) in timeline |
|
|
18
|
+
| Video track | Required. No video not supported |
|
|
19
|
+
| Audio track | 0 or 1 stream only. If multiple, first audio stream adopted |
|
|
20
|
+
|
|
21
|
+
### Out of Scope (Planned for Future)
|
|
22
|
+
|
|
23
|
+
- VFR / resolution-changing materials
|
|
24
|
+
- Multiple source file concatenation
|
|
25
|
+
- Subtitle burn-in
|
|
26
|
+
- Transitions
|
|
27
|
+
- 2+ video tracks in timeline
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## FFmpeg Setup
|
|
32
|
+
|
|
33
|
+
**FFmpeg / FFprobe are not bundled with this package**. Install in your environment.
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# macOS (Homebrew)
|
|
37
|
+
brew install ffmpeg
|
|
38
|
+
|
|
39
|
+
# Ubuntu / Debian
|
|
40
|
+
sudo apt install ffmpeg
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
If `ffmpeg` / `ffprobe` are on PATH, it works as-is. In environments where PATH cannot be modified, explicitly specify paths with environment variables.
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
export CLIPWRIGHT_FFMPEG=/usr/local/bin/ffmpeg
|
|
47
|
+
export CLIPWRIGHT_FFPROBE=/usr/local/bin/ffprobe
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
> About license: This wrapper package itself is **MIT** licensed. Since FFmpeg binaries are not bundled, FFmpeg's LGPL / GPL redistribution obligations do not apply to this wrapper. Verify FFmpeg's own license (LGPL v2.1 / GPL v2) in your environment.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Installation
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
uv sync
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Usage
|
|
63
|
+
|
|
64
|
+
### MCP Tool (`clipwright_render`)
|
|
65
|
+
|
|
66
|
+
Invoked from Claude / agents via MCP.
|
|
67
|
+
|
|
68
|
+
```jsonc
|
|
69
|
+
{
|
|
70
|
+
"tool": "clipwright_render",
|
|
71
|
+
"arguments": {
|
|
72
|
+
"timeline": "/path/to/timeline.otio",
|
|
73
|
+
"output": "/path/to/output.mp4",
|
|
74
|
+
"dry_run": false,
|
|
75
|
+
"options": {
|
|
76
|
+
"video_codec": "libx264",
|
|
77
|
+
"audio_codec": "aac",
|
|
78
|
+
"width": 1920,
|
|
79
|
+
"height": 1080,
|
|
80
|
+
"fps": 29.97,
|
|
81
|
+
"crf": 23,
|
|
82
|
+
"overwrite": false
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**Arguments**
|
|
89
|
+
|
|
90
|
+
| Argument | Type | Required | Description |
|
|
91
|
+
|----------|------|----------|-------------|
|
|
92
|
+
| `timeline` | string | yes | Input OTIO file path |
|
|
93
|
+
| `output` | string | yes | Output file path (`.mp4` / `.mkv` / `.mov` / `.webm`) |
|
|
94
|
+
| `dry_run` | bool | optional (default `false`) | If `true`, returns plan without actual rendering |
|
|
95
|
+
| `options` | object | optional | Output options (see RenderOptions below) |
|
|
96
|
+
|
|
97
|
+
**RenderOptions**
|
|
98
|
+
|
|
99
|
+
| Field | Type | Description |
|
|
100
|
+
|-------|------|-------------|
|
|
101
|
+
| `video_codec` | string \| null | Video codec (e.g. `libx264`, default: inherit from source) |
|
|
102
|
+
| `audio_codec` | string \| null | Audio codec (e.g. `aac`, default: inherit from source) |
|
|
103
|
+
| `width` | int \| null | Output width (must be set with `height`) |
|
|
104
|
+
| `height` | int \| null | Output height (must be set with `width`) |
|
|
105
|
+
| `fps` | float \| null | Output frame rate |
|
|
106
|
+
| `crf` | int \| null | Quality CRF value (0-51) |
|
|
107
|
+
| `overwrite` | bool | If `true`, overwrite existing output file (default `false`) |
|
|
108
|
+
|
|
109
|
+
`width` / `height` must both be specified or both `null`. Specifying only one is an error.
|
|
110
|
+
|
|
111
|
+
**Return Value (Success)**
|
|
112
|
+
|
|
113
|
+
```jsonc
|
|
114
|
+
{
|
|
115
|
+
"ok": true,
|
|
116
|
+
"summary": "2 clips → 45.2 sec / 42.1 MB / outputs/out.mp4",
|
|
117
|
+
"data": {
|
|
118
|
+
"output_path": "/path/to/output.mp4",
|
|
119
|
+
"duration_sec": 45.2,
|
|
120
|
+
"size_bytes": 44150784,
|
|
121
|
+
"clip_count": 2
|
|
122
|
+
},
|
|
123
|
+
"artifacts": ["/path/to/output.mp4"],
|
|
124
|
+
"warnings": []
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Return Value (dry_run)**
|
|
129
|
+
|
|
130
|
+
```jsonc
|
|
131
|
+
{
|
|
132
|
+
"ok": true,
|
|
133
|
+
"summary": "dry_run: 2 segments / estimated 45.2 sec / approx 42.1 MB",
|
|
134
|
+
"data": {
|
|
135
|
+
"dry_run": true,
|
|
136
|
+
"clip_count": 2,
|
|
137
|
+
"estimated_duration_sec": 45.2,
|
|
138
|
+
"estimated_size_bytes": 44150784,
|
|
139
|
+
"ffmpeg_args": ["ffmpeg", "-i", "source.mp4", "-filter_complex", "..."]
|
|
140
|
+
},
|
|
141
|
+
"artifacts": [],
|
|
142
|
+
"warnings": []
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
`estimated_size_bytes` is calculated from source bitrate obtained by FFprobe and output duration. If bitrate cannot be obtained, it is `null` with reason in `warnings`. If any of `video_codec` / `width` / `height` / `fps` / `crf` are specified, estimation based on source bitrate may differ significantly from actual, so `warnings` includes a note.
|
|
147
|
+
|
|
148
|
+
**Return Value (Error)**
|
|
149
|
+
|
|
150
|
+
```jsonc
|
|
151
|
+
{
|
|
152
|
+
"ok": false,
|
|
153
|
+
"error": {
|
|
154
|
+
"code": "FILE_NOT_FOUND",
|
|
155
|
+
"message": "Timeline file not found: /path/to/timeline.otio",
|
|
156
|
+
"hint": "Verify the file path"
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Main error codes:
|
|
162
|
+
|
|
163
|
+
| Code | Meaning |
|
|
164
|
+
|------|---------|
|
|
165
|
+
| `FILE_NOT_FOUND` | Timeline / source / output directory does not exist |
|
|
166
|
+
| `INVALID_INPUT` | Invalid extension / existing output with overwrite=false / empty timeline |
|
|
167
|
+
| `PATH_NOT_ALLOWED` | Output path is same as input source |
|
|
168
|
+
| `UNSUPPORTED_OPERATION` | No video / multiple sources / Transition / 2+ video tracks |
|
|
169
|
+
| `PROBE_FAILED` | FFprobe analysis failed |
|
|
170
|
+
| `SUBPROCESS_FAILED` | FFmpeg exit code non-zero |
|
|
171
|
+
| `SUBPROCESS_TIMEOUT` | FFmpeg timeout (`max(300, duration_sec × 10)` seconds) |
|
|
172
|
+
| `DEPENDENCY_MISSING` | ffmpeg / ffprobe not found in PATH or environment variables |
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
### CLI (`clipwright-render`)
|
|
177
|
+
|
|
178
|
+
Can be run directly from command line. Shares same logic as MCP tool.
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
clipwright-render <timeline> <output> [options]
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
**Arguments**
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
clipwright-render <timeline> <output>
|
|
188
|
+
[--dry-run]
|
|
189
|
+
[--video-codec C]
|
|
190
|
+
[--audio-codec C]
|
|
191
|
+
[--width W --height H]
|
|
192
|
+
[--fps F]
|
|
193
|
+
[--crf N]
|
|
194
|
+
[--overwrite]
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**Example: Verify plan with dry_run before rendering**
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
# Verify plan first
|
|
201
|
+
clipwright-render timeline.otio out.mp4 --dry-run
|
|
202
|
+
|
|
203
|
+
# Render if OK
|
|
204
|
+
clipwright-render timeline.otio out.mp4 --video-codec libx264 --crf 23
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**Example: Render with specified resolution**
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
clipwright-render timeline.otio out.mp4 --width 1280 --height 720 --fps 29.97
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## Testing
|
|
216
|
+
|
|
217
|
+
### Unit Tests (FFmpeg Not Required)
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
uv run --package clipwright-render pytest clipwright-render/tests/ -m "not integration"
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Integration Tests (FFmpeg Required)
|
|
224
|
+
|
|
225
|
+
Tests that verify single-source concatenation and output using actual FFmpeg. Automatically skipped in environments without FFmpeg.
|
|
226
|
+
|
|
227
|
+
Set environment variables before running:
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
export CLIPWRIGHT_FFMPEG=/path/to/ffmpeg
|
|
231
|
+
export CLIPWRIGHT_FFPROBE=/path/to/ffprobe
|
|
232
|
+
|
|
233
|
+
uv run --package clipwright-render pytest clipwright-render/tests/ -m integration
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
> Integration tests skip if `CLIPWRIGHT_FFMPEG` / `CLIPWRIGHT_FFPROBE` are not set. Set these variables when running in CI.
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## License
|
|
241
|
+
|
|
242
|
+
This wrapper package itself is **MIT** licensed.
|
|
243
|
+
|
|
244
|
+
Since FFmpeg binaries are not bundled, FFmpeg's LGPL v2.1 / GPL v2 redistribution obligations do not apply to this package.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "clipwright-render"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "MCP tool to realize OTIO timelines with FFmpeg. Completes segment extraction, concatenation, and trimming in a single transcode pass."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = { text = "MIT" }
|
|
7
|
+
authors = [
|
|
8
|
+
{ name = "satoh-y-0323", email = "shoma.papa.0323@gmail.com" }
|
|
9
|
+
]
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"clipwright>=0.1.0",
|
|
13
|
+
"mcp[cli]>=1.27.2",
|
|
14
|
+
"opentimelineio>=0.18",
|
|
15
|
+
"pydantic>=2",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[project.scripts]
|
|
19
|
+
clipwright-render = "clipwright_render.server:main"
|
|
20
|
+
|
|
21
|
+
[build-system]
|
|
22
|
+
requires = ["uv_build>=0.11.19,<0.12.0"]
|
|
23
|
+
build-backend = "uv_build"
|
|
24
|
+
|
|
25
|
+
[dependency-groups]
|
|
26
|
+
dev = [
|
|
27
|
+
"mypy>=2.1.0",
|
|
28
|
+
"pytest>=9.0.3",
|
|
29
|
+
"pytest-cov>=7.1.0",
|
|
30
|
+
"pytest-mock>=3.15.1",
|
|
31
|
+
"ruff>=0.15.16",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
# Resolve clipwright (core) within workspace by path reference
|
|
35
|
+
[tool.uv.sources]
|
|
36
|
+
clipwright = { workspace = true }
|
|
37
|
+
|
|
38
|
+
# --- Ruff ---
|
|
39
|
+
[tool.ruff]
|
|
40
|
+
target-version = "py311"
|
|
41
|
+
line-length = 88
|
|
42
|
+
|
|
43
|
+
[tool.ruff.lint]
|
|
44
|
+
select = ["E", "F", "W", "I", "UP", "B", "C4", "SIM"]
|
|
45
|
+
ignore = []
|
|
46
|
+
|
|
47
|
+
[tool.ruff.lint.per-file-ignores]
|
|
48
|
+
# Allow E501 for English docstrings/comments in test files (same rules as wrap/silence)
|
|
49
|
+
"tests/*.py" = ["E501"]
|
|
50
|
+
|
|
51
|
+
[tool.ruff.format]
|
|
52
|
+
# Default ruff formatter is OK
|
|
53
|
+
|
|
54
|
+
# --- mypy ---
|
|
55
|
+
[tool.mypy]
|
|
56
|
+
python_version = "3.11"
|
|
57
|
+
strict = true
|
|
58
|
+
warn_return_any = true
|
|
59
|
+
warn_unused_configs = true
|
|
60
|
+
disallow_untyped_defs = true
|
|
61
|
+
disallow_any_generics = true
|
|
62
|
+
|
|
63
|
+
# opentimelineio has no stubs, ignored with mypy strict
|
|
64
|
+
[[tool.mypy.overrides]]
|
|
65
|
+
module = "opentimelineio.*"
|
|
66
|
+
ignore_missing_imports = true
|
|
67
|
+
|
|
68
|
+
# --- pytest ---
|
|
69
|
+
[tool.pytest.ini_options]
|
|
70
|
+
testpaths = ["tests"]
|
|
71
|
+
addopts = "--strict-markers -q"
|
|
72
|
+
markers = [
|
|
73
|
+
"integration: integration test requiring actual ffmpeg/ffprobe binaries",
|
|
74
|
+
"slow: test with long execution time",
|
|
75
|
+
"e2e: e2e test using actual ffmpeg binary (loudness normalization etc)",
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
# --- coverage ---
|
|
79
|
+
[tool.coverage.run]
|
|
80
|
+
source = ["clipwright_render"]
|
|
81
|
+
omit = ["tests/*"]
|
|
82
|
+
|
|
83
|
+
[tool.coverage.report]
|
|
84
|
+
show_missing = true
|
|
85
|
+
skip_covered = false
|