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.
@@ -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
@@ -0,0 +1,3 @@
1
+ """clipwright-render: MCP tool that materialises an OTIO timeline via FFmpeg."""
2
+
3
+ __version__ = "0.1.0"