This is page 4 of 5. Use http://codebase.md/samuelgursky/davinci-resolve-mcp?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .cursorrules
├── .gitignore
├── CHANGELOG.md
├── CHANGES.md
├── config
│ ├── cursor-mcp-example.json
│ ├── macos
│ │ ├── claude-desktop-config.template.json
│ │ └── cursor-mcp-config.template.json
│ ├── mcp-project-template.json
│ ├── README.md
│ ├── sample_config.json
│ └── windows
│ ├── claude-desktop-config.template.json
│ └── cursor-mcp-config.template.json
├── docs
│ ├── CHANGELOG.md
│ ├── COMMIT_MESSAGE.txt
│ ├── FEATURES.md
│ ├── PROJECT_MCP_SETUP.md
│ ├── TOOLS_README.md
│ └── VERSION.md
├── examples
│ ├── getting_started.py
│ ├── markers
│ │ ├── add_spaced_markers.py
│ │ ├── add_timecode_marker.py
│ │ ├── alternating_markers.py
│ │ ├── clear_add_markers.py
│ │ ├── README.md
│ │ └── test_marker_frames.py
│ ├── media
│ │ ├── import_folder.py
│ │ └── README.md
│ ├── README.md
│ └── timeline
│ ├── README.md
│ ├── timeline_check.py
│ └── timeline_info.py
├── INSTALL.md
├── LICENSE
├── logs
│ └── .gitkeep
├── README.md
├── requirements.txt
├── resolve_mcp_server.py
├── run-now.bat
├── run-now.sh
├── scripts
│ ├── batch_automation.py
│ ├── check-resolve-ready.bat
│ ├── check-resolve-ready.ps1
│ ├── check-resolve-ready.sh
│ ├── create_app_shortcut.sh
│ ├── create-release-zip.bat
│ ├── create-release-zip.sh
│ ├── launch.sh
│ ├── mcp_resolve_launcher.sh
│ ├── mcp_resolve-claude_start
│ ├── mcp_resolve-cursor_start
│ ├── README.md
│ ├── resolve_mcp_server.py
│ ├── restart-server.bat
│ ├── restart-server.sh
│ ├── run-now.bat
│ ├── run-now.sh
│ ├── run-server.sh
│ ├── server.sh
│ ├── setup
│ │ ├── install.bat
│ │ └── install.sh
│ ├── setup.sh
│ ├── utils.sh
│ ├── verify-installation.bat
│ └── verify-installation.sh
├── src
│ ├── __init__.py
│ ├── api
│ │ ├── __init__.py
│ │ ├── color_operations.py
│ │ ├── delivery_operations.py
│ │ ├── media_operations.py
│ │ ├── project_operations.py
│ │ └── timeline_operations.py
│ ├── bin
│ │ └── __init__.py
│ ├── main.py
│ ├── resolve_mcp_server.py
│ └── utils
│ ├── __init__.py
│ ├── app_control.py
│ ├── cloud_operations.py
│ ├── layout_presets.py
│ ├── object_inspection.py
│ ├── platform.py
│ ├── project_properties.py
│ └── resolve_connection.py
└── tests
├── benchmark_server.py
├── create_test_timeline.py
├── test_custom_timeline.py
├── test_improvements.py
├── test-after-restart.bat
└── test-after-restart.sh
```
# Files
--------------------------------------------------------------------------------
/docs/FEATURES.md:
--------------------------------------------------------------------------------
```markdown
1 | # DaVinci Resolve MCP Server Features
2 |
3 | This document tracks the implementation status of features in the DaVinci Resolve MCP (Multi-Client Protocol) Server. It is organized by feature categories and provides details on implementation status, compatibility with clients, and any known issues.
4 |
5 | ## Implementation Status
6 |
7 | The MCP server implements nearly all features from the DaVinci Resolve scripting API, but our testing has revealed that while we have implemented 202 features (100%), only a small percentage have been verified working on macOS (8%), with many features still needing verification (82%) or having known issues (10%).
8 |
9 | Testing has primarily been conducted on macOS, with Windows support implemented but requiring thorough testing. Each feature in this document is marked with symbols indicating its current status:
10 |
11 | **Status Key:**
12 | - ✅ - Implemented and verified working
13 | - ⚠️ - Implemented but needs testing/verification
14 | - 🐞 - Implemented but has known issues
15 | - 🟡 - Planned feature
16 | - 🚫 - Not implemented/supported
17 |
18 | The compatibility columns indicate whether a feature is known to work with specific clients (Cursor/Claude) on specific platforms (Mac/Windows).
19 |
20 | ## Feature Categories
21 |
22 | ## Status Definitions
23 |
24 | ✅ - **Implemented & Verified**: Feature is fully implemented and verified working
25 | ⚠️ - **Implemented with Limitations**: Feature works but has known limitations or requirements
26 | 🔄 - **In progress**: Feature is in development or testing phase
27 | 🟡 - **Planned**: Feature is planned but not yet implemented
28 | ❌ - **Not implemented**: Feature will not be implemented
29 | 🚫 - **Not applicable**: Feature is not applicable to the current platform
30 | 🐞 - **Implementation Issues**: Feature is implemented but has known bugs
31 |
32 | ## Client/Platform Compatibility Update
33 |
34 | | Client | macOS | Windows | Linux |
35 | |--------|-------|---------|-------|
36 | | Cursor | ✅ Stable | ⚠️ Needs Testing | ❌ |
37 | | Claude Desktop | ✅ Stable | ⚠️ Needs Testing | ❌ |
38 |
39 | ## Implementation Methods
40 |
41 | | Method | Status | Notes |
42 | |--------|--------|-------|
43 | | MCP Framework | 🐞 | Original implementation - connection issues |
44 | | Direct JSON-RPC | ✅ | Current implementation - more reliable |
45 |
46 | ## Feature Statistics
47 |
48 | | Category | Total Features | Implemented | Verified (Mac) | Verified (Win) | Not Verified | Failed |
49 | |----------|----------------|-------------|----------------|----------------|--------------|--------|
50 | | Core Features | 9 | 9 (100%) | 4 (44%) | 0 (0%) | 3 (33%) | 2 (22%) |
51 | | General Resolve API | 14 | 14 (100%) | 6 (43%) | 0 (0%) | 5 (36%) | 3 (21%) |
52 | | Project Management | 18 | 18 (100%) | 2 (11%) | 0 (0%) | 15 (83%) | 1 (6%) |
53 | | Timeline Operations | 12 | 12 (100%) | 2 (17%) | 0 (0%) | 8 (67%) | 2 (16%) |
54 | | Media Pool Operations | 18 | 18 (100%) | 0 (0%) | 0 (0%) | 16 (89%) | 2 (11%) |
55 | | Color Page Operations | 16 | 16 (100%) | 0 (0%) | 0 (0%) | 14 (88%) | 2 (12%) |
56 | | Delivery Page Operations | 12 | 12 (100%) | 1 (8%) | 0 (0%) | 10 (84%) | 1 (8%) |
57 | | Fusion Page Operations | 0 | 0 (0%) | 0 (0%) | 0 (0%) | 0 (0%) | 0 (0%) |
58 | | Fairlight Page Operations | 0 | 0 (0%) | 0 (0%) | 0 (0%) | 0 (0%) | 0 (0%) |
59 | | Media Storage Operations | 0 | 0 (0%) | 0 (0%) | 0 (0%) | 0 (0%) | 0 (0%) |
60 | | Audio Sync | 4 | 4 (100%) | 0 (0%) | 0 (0%) | 4 (100%) | 0 (0%) |
61 | | Cache Management | 3 | 3 (100%) | 1 (33%) | 0 (0%) | 2 (67%) | 0 (0%) |
62 | | Proxy Media Management | 6 | 6 (100%) | 0 (0%) | 0 (0%) | 5 (83%) | 1 (17%) |
63 | | Transcription Services | 6 | 6 (100%) | 0 (0%) | 0 (0%) | 5 (83%) | 1 (17%) |
64 | | Object Methods | 84 | 84 (100%) | 1 (1%) | 0 (0%) | 79 (94%) | 4 (5%) |
65 | | **TOTAL** | **202** | **202 (100%)** | **17 (8%)** | **0 (0%)** | **166 (82%)** | **19 (10%)** |
66 |
67 | **Status Key:**
68 | - ✅ - Implemented and verified working
69 | - ⚠️ - Implemented but needs testing/verification
70 | - 🐞 - Implemented but has known issues
71 | - 🟡 - Planned feature
72 | - 🚫 - Not implemented/supported
73 |
74 | ## Core Features
75 |
76 | | Feature | Implementation | Cursor (Mac) | Claude (Mac) | Cursor (Win) | Claude (Win) | Notes |
77 | |---------|---------------|--------------|--------------|--------------|--------------|-------|
78 | | Connect to Resolve | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Establish connection to DaVinci Resolve |
79 | | Switch to Page | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Switch between Media, Edit, Color, etc. - Verified working |
80 | | Get Current Page | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Get current active page |
81 | | Get Resolve Version | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Get DaVinci Resolve version |
82 | | Get Product Name | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Get product name (Studio or free) |
83 | | Object Inspection | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Introspect API objects, methods, and properties |
84 | | Error Handling | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Error messages exist but could be more informative |
85 |
86 | ### Project Management
87 |
88 | | Feature | Implementation | Cursor (Mac) | Claude (Mac) | Cursor (Win) | Claude (Win) | Notes |
89 | |---------|---------------|--------------|--------------|--------------|--------------|-------|
90 | | List Projects | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Get list of available projects |
91 | | Get Current Project Name | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Get name of currently open project |
92 | | Open Project | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Open project by name - Verified working |
93 | | Create Project | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Create new project - Cannot recreate existing projects |
94 | | Save Project | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Save current project |
95 | | Close Project | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Close current project |
96 | | Project Properties | 🐞 | 🐞 | 🐞 | ⚠️ | ⚠️ | Get and set project settings - Parameter type issues |
97 | | SuperScale Settings | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Control super scale quality - Not verified |
98 | | Timeline Frame Rate | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Control timeline frame rates - Not verified |
99 | | Export/Import Project | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Import/export project files - Not verified |
100 | | Archive Project | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Archive projects with media - Not verified |
101 | | Cloud Project Operations | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Create and manage cloud projects - Not verified |
102 | | Project Folders | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Create and navigate project folders - Not verified |
103 | | Project Presets | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Apply and manage project presets - Not verified |
104 | | Load Time/Performance | 🟡 | - | - | - | - | Project load time and performance metrics |
105 | | Project Analytics | 🟡 | - | - | - | - | Project usage and statistics |
106 | | Collaborative Projects | 🟡 | - | - | - | - | Manage collaborative workflows |
107 | | Database Management | 🟡 | - | - | - | - | PostgreSQL and local database operations |
108 | | Project Templates | 🟡 | - | - | - | - | Save and load project templates |
109 |
110 | ### Timeline Operations
111 |
112 | | Feature | Implementation | Cursor (Mac) | Claude (Mac) | Cursor (Win) | Claude (Win) | Notes |
113 | |---------|---------------|--------------|--------------|--------------|--------------|-------|
114 | | Create Timeline | 🐞 | 🐞 | 🐞 | ⚠️ | ⚠️ | Create timeline - Failed with existing names without clear error |
115 | | List Timelines | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Get all timelines in project - Verified working |
116 | | Get Current Timeline | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Get current active timeline |
117 | | Set Current Timeline | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Switch to specified timeline - Verified working |
118 | | Add Timeline Marker | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Add marker at position - Requires valid frame within timeline bounds |
119 | | Delete Timeline Marker | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Delete marker at position - Not verified |
120 | | Manage Timeline Tracks | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Add/remove video and audio tracks - Not verified |
121 | | Get Timeline Items | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Get clips in timeline - Not verified |
122 | | Timecode Operations | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Get/set current timecode - Not verified |
123 | | Timeline Settings | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Manage timeline settings - Not verified |
124 | | Timeline Generators | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Insert generators into timeline - Not verified |
125 | | Timeline OFX | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Insert OFX plugins into timeline - Not verified |
126 | | Timeline Import/Export | 🟡 | - | - | - | - | Import/export timeline formats |
127 | | Scene Detection | 🟡 | - | - | - | - | Detect scene cuts automatically |
128 | | Auto Subtitle Creation | 🟡 | - | - | - | - | Generate subtitles from audio |
129 |
130 | ### Media Pool Operations
131 |
132 | | Feature | Implementation | Cursor (Mac) | Claude (Mac) | Cursor (Win) | Claude (Win) | Notes |
133 | |---------|---------------|--------------|--------------|--------------|--------------|-------|
134 | | Import Media | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Import media files |
135 | | List Media | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | List media pool clips |
136 | | Create Bins | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Create folders in media pool - Verified working |
137 | | Organize Media | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Move clips between folders |
138 | | Add to Timeline | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Add clips to timeline |
139 | | Clip Properties | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Get/set clip properties |
140 | | Clip Markers | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Add/manage clip markers |
141 | | Metadata Management | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Get/set clip metadata |
142 | | Media Relinking | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Relink/unlink media files |
143 | | Audio Sync | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Sync audio between clips |
144 | | Proxy Media | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Link/unlink proxy media |
145 | | Clip Transcription | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Transcribe audio in clips |
146 | | Bulk Import | 🟡 | - | - | - | - | Batch import operations |
147 | | Smart Bins | 🟡 | - | - | - | - | Create/manage smart bins |
148 |
149 | ### Media Storage Operations
150 |
151 | | Feature | Implementation | Cursor (Mac) | Claude (Mac) | Cursor (Win) | Claude (Win) | Notes |
152 | |---------|---------------|--------------|--------------|--------------|--------------|-------|
153 | | Get Mounted Volumes | 🟡 | - | - | - | - | List mounted storage devices |
154 | | Browse Folders | 🟡 | - | - | - | - | Navigate folder structure |
155 | | List Media Files | 🟡 | - | - | - | - | List media in folders |
156 | | Reveal in Storage | 🟡 | - | - | - | - | Highlight file in browser |
157 |
158 | ### Color Page Operations
159 |
160 | | Feature | Implementation | Cursor (Mac) | Claude (Mac) | Cursor (Win) | Claude (Win) | Notes |
161 | |---------|---------------|--------------|--------------|--------------|--------------|-------|
162 | | Apply LUTs | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Apply LUTs to clips |
163 | | Color Correction | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Adjust color parameters |
164 | | Get/Set Grades | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Manage color grades |
165 | | Node Management | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Work with node graph - Note: May require clips with existing grade objects |
166 | | Gallery Operations | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Save/load looks from gallery |
167 | | Color Wheels | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Adjust lift/gamma/gain - Note: Requires clips with existing grade objects |
168 | | Grade Versions | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Manage color versions |
169 | | Export Grades | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Export grades as files |
170 | | Color Groups | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Group clips for color |
171 | | Node Cache | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Control node caching |
172 | | Flag Management | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Add/remove clip flags |
173 | | Color Space | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Color space controls |
174 | | Magic Mask | 🟡 | - | - | - | - | AI-based masking |
175 | | Track/Window | 🟡 | - | - | - | - | Motion tracking operations |
176 | | HDR Grading | 🟡 | - | - | - | - | High dynamic range controls |
177 | | Face Refinement | 🟡 | - | - | - | - | Automated face enhancement |
178 |
179 | ### Delivery Page Operations
180 |
181 | | Feature | Implementation | Cursor (Mac) | Claude (Mac) | Cursor (Win) | Claude (Win) | Notes |
182 | |---------|---------------|--------------|--------------|--------------|--------------|-------|
183 | | Add Render Job | 🐞 | 🐞 | 🐞 | ⚠️ | ⚠️ | Add job to render queue - Failed with "'NoneType' object is not callable" |
184 | | Start Rendering | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Start render process - Not verified |
185 | | List Render Jobs | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Get all queued render jobs - Not verified |
186 | | Delete Render Jobs | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Remove jobs from queue - Not verified |
187 | | Clear Render Queue | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Clear render queue - Verified working |
188 | | Get Render Presets | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | List available presets - Not verified |
189 | | Render Status | 🟡 | - | - | - | - | Check render progress |
190 | | Export Settings | 🟡 | - | - | - | - | Configure render settings |
191 | | Format Control | 🟡 | - | - | - | - | Control output format/codec |
192 | | Quick Export | 🟡 | - | - | - | - | RenderWithQuickExport |
193 | | Batch Rendering | 🟡 | - | - | - | - | Manage multiple render jobs |
194 |
195 | ### Specialized Features
196 |
197 | #### Object Inspection
198 |
199 | | Feature | Implementation | Cursor (Mac) | Claude (Mac) | Cursor (Win) | Claude (Win) | Notes |
200 | |---------|---------------|--------------|--------------|--------------|--------------|-------|
201 | | Get Object Properties | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Get object properties - Not verified |
202 | | List Available Methods | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | List API methods for object - Not verified |
203 | | Get API Version | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Get DaVinci Resolve API version - Not verified |
204 | | Get Supported Objects | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | List supported API object types - Not verified |
205 | | Interactive Inspection | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Testing/debugging interface - Not verified |
206 |
207 | #### Layout Presets
208 |
209 | | Feature | Implementation | Cursor (Mac) | Claude (Mac) | Cursor (Win) | Claude (Win) | Notes |
210 | |---------|---------------|--------------|--------------|--------------|--------------|-------|
211 | | Get UI Layout Presets | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | List available layout presets - Not verified |
212 | | Set UI Layout Preset | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Switch to a specific UI layout - Not verified |
213 | | Save Current Layout | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Save current UI as layout preset - Not verified |
214 | | Delete Layout Preset | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Remove a custom layout preset - Not verified |
215 |
216 | #### App Control
217 |
218 | | Feature | Implementation | Cursor (Mac) | Claude (Mac) | Cursor (Win) | Claude (Win) | Notes |
219 | |---------|---------------|--------------|--------------|--------------|--------------|-------|
220 | | Quit Application | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Safely close DaVinci Resolve - Not verified (not testing to avoid closing app) |
221 | | Restart Application | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Restart DaVinci Resolve - Not verified (not testing to avoid disruption) |
222 | | Save All Projects | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Save all open projects - Not verified |
223 | | Check Application Status | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Verify if application is running - Not verified |
224 |
225 | #### Cloud Project Operations
226 |
227 | | Feature | Implementation | Cursor (Mac) | Claude (Mac) | Cursor (Win) | Claude (Win) | Notes |
228 | |---------|---------------|--------------|--------------|--------------|--------------|-------|
229 | | List Cloud Projects | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | List projects in cloud library - Not verified |
230 | | Create Cloud Project | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Create new project in cloud - Not verified |
231 | | Open Cloud Project | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Open project from cloud library - Not verified |
232 | | Delete Cloud Project | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Remove project from cloud - Not verified |
233 | | Export Project to Cloud | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Upload local project to cloud - Not verified |
234 | | Import Project from Cloud | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Download cloud project locally - Not verified |
235 |
236 | #### Audio Sync Features
237 |
238 | | Feature | Implementation | Cursor (Mac) | Claude (Mac) | Cursor (Win) | Claude (Win) | Notes |
239 | |---------|---------------|--------------|--------------|--------------|--------------|-------|
240 | | Auto-sync Audio | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Automatic audio synchronization - Not verified |
241 | | Waveform Analysis | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Sync based on waveform matching - Not verified |
242 | | Timecode Sync | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Sync based on embedded timecode - Not verified |
243 | | Multi-clip Sync | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Sync multiple clips simultaneously - Not verified |
244 | | Append Track Mode | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Option to append or replace audio - Not verified |
245 | | Manual Offset Adjustment | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Fine-tune sync with manual offset - Not verified |
246 |
247 | #### Proxy Media Management
248 |
249 | | Feature | Implementation | Cursor (Mac) | Claude (Mac) | Cursor (Win) | Claude (Win) | Notes |
250 | |---------|---------------|--------------|--------------|--------------|--------------|-------|
251 | | Link Proxy Media | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Link proxy files to clips - Not verified |
252 | | Unlink Proxy Media | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Remove proxy file associations - Not verified |
253 | | Set Proxy Mode | 🐞 | 🐞 | 🐞 | ⚠️ | ⚠️ | Toggle between proxy/original - Failed during testing |
254 | | Set Proxy Quality | 🐞 | 🐞 | 🐞 | ⚠️ | ⚠️ | Configure proxy resolution - Failed with "Failed to set proxy quality" |
255 | | Proxy Generation | 🟡 | - | - | - | - | Generate proxy media files |
256 | | Batch Proxy Operations | 🟡 | - | - | - | - | Process multiple clips at once |
257 |
258 | #### Cache Management
259 |
260 | | Feature | Implementation | Cursor (Mac) | Claude (Mac) | Cursor (Win) | Claude (Win) | Notes |
261 | |---------|---------------|--------------|--------------|--------------|--------------|-------|
262 | | Set Cache Mode | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Control cache utilization - Note: May require specific project setup |
263 | | Set Optimized Media Mode | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Toggle optimized media usage - Note: May require specific project setup |
264 | | Set Proxy Mode | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Toggle proxy mode - Note: May require specific project setup |
265 | | Set Proxy Quality | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Configure proxy quality |
266 | | Clear Cache | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Delete cached render files |
267 | | Cache Settings | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Configure cache parameters |
268 | | Generate Optimized Media | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Create optimized media |
269 | | Delete Optimized Media | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Remove optimized media files |
270 |
271 | #### Transcription Services
272 |
273 | | Feature | Implementation | Cursor (Mac) | Claude (Mac) | Cursor (Win) | Claude (Win) | Notes |
274 | |---------|---------------|--------------|--------------|--------------|--------------|-------|
275 | | Transcribe Audio | 🐞 | 🐞 | 🐞 | ⚠️ | ⚠️ | Generate text from audio - Failed with clip not found error |
276 | | Clear Transcription | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Remove existing transcription - Not verified |
277 | | Set Transcription Language | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Select language for transcription - Not verified |
278 | | Export Transcription | 🟡 | - | - | - | - | Save transcription to file |
279 | | Transcribe Multiple Clips | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Batch transcription processing - Not verified |
280 | | Edit Transcription | 🟡 | - | - | - | - | Modify generated text |
281 |
282 | ## Object-Specific Methods
283 |
284 | ### Timeline Object Methods
285 |
286 | | Method | Implementation | Cursor (Mac) | Claude (Mac) | Cursor (Win) | Claude (Win) | Notes |
287 | |--------|---------------|--------------|--------------|--------------|--------------|-------|
288 | | GetName | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Get timeline name - Not verified |
289 | | GetStartFrame | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Get first frame number - Not verified |
290 | | GetEndFrame | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Get last frame number - Not verified |
291 | | GetTrackCount | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Count tracks by type - Not verified |
292 | | GetItemListInTrack | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Get clips in track - Not verified |
293 | | AddMarker | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Add marker at frame - Not verified |
294 | | GetMarkers | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Get all timeline markers - Not verified |
295 | | DeleteMarkerAtFrame | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Remove marker at position - Not verified |
296 | | DeleteMarkersByColor | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Remove markers by color - Not verified |
297 | | DeleteAllMarkers | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Clear all markers - Not verified |
298 | | ApplyGradeFromDRX | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Apply grade from file - Not verified |
299 | | GetSetting | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Get timeline setting - Not verified |
300 | | SetSetting | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Change timeline setting - Not verified |
301 | | InsertGeneratorIntoTimeline | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Add generator clip - Not verified |
302 | | InsertOFXGeneratorIntoTimeline | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Add OFX generator - Not verified |
303 | | InsertFusionGeneratorIntoTimeline | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Add Fusion generator - Not verified |
304 | | InsertTitleIntoTimeline | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Add title clip - Not verified |
305 | | InsertFusionTitleIntoTimeline | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Add Fusion title - Not verified |
306 | | InsertOFXTitleIntoTimeline | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Add OFX title - Not verified |
307 | | DuplicateTimeline | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Create timeline copy - Not verified |
308 | | CreateCompoundClip | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Group clips together - Not verified |
309 | | CreateFusionClip | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Convert to Fusion clip - Not verified |
310 | | ImportIntoTimeline | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Import timeline file - Not verified |
311 | | Export | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Export timeline file - Not verified |
312 |
313 | ### TimelineItem Object Methods
314 |
315 | | Method | Implementation | Cursor (Mac) | Claude (Mac) | Cursor (Win) | Claude (Win) | Notes |
316 | |--------|---------------|--------------|--------------|--------------|--------------|-------|
317 | | GetName | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Get clip name - Not verified |
318 | | GetDuration | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Get clip duration - Not verified |
319 | | GetStart | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Get start frame - Not verified |
320 | | GetEnd | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Get end frame - Not verified |
321 | | GetLeftOffset | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Get left handle length - Not verified |
322 | | GetRightOffset | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Get right handle length - Not verified |
323 | | GetProperty | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Get clip property - Not verified |
324 | | SetProperty | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Change clip property - Not verified |
325 | | AddMarker | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Add marker at offset - Not verified |
326 | | GetMarkers | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Get all clip markers - Not verified |
327 | | DeleteMarkerAtFrame | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Remove marker at position - Not verified |
328 | | DeleteMarkersByColor | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Remove markers by color - Not verified |
329 | | DeleteAllMarkers | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Clear all markers - Not verified |
330 | | AddFusionComp | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Add Fusion composition - Not verified |
331 | | ImportFusionComp | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Import Fusion composition - Not verified |
332 | | ExportFusionComp | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Export Fusion composition - Not verified |
333 |
334 | ### Project Object Methods
335 |
336 | | Method | Implementation | Cursor (Mac) | Claude (Mac) | Cursor (Win) | Claude (Win) | Notes |
337 | |--------|---------------|--------------|--------------|--------------|--------------|-------|
338 | | GetName | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Get project name - Not verified |
339 | | GetPresetList | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Get available presets - Not verified |
340 | | SetPreset | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Apply preset to project - Not verified |
341 | | AddRenderJob | 🐞 | 🐞 | 🐞 | ⚠️ | ⚠️ | Add job to render queue - Failed in our testing |
342 | | DeleteAllRenderJobs | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Clear render queue - Verified working |
343 | | StartRendering | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Begin render process - Not verified |
344 | | StopRendering | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Abort render process - Not verified |
345 | | IsRenderingInProgress | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Check render status - Not verified |
346 | | SetRenderFormat | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Set output format - Not verified |
347 | | LoadLayoutPreset | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Apply UI layout - Not verified |
348 | | SaveLayoutPreset | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Store current UI layout - Not verified |
349 | | ExportLayoutPreset | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Save layout to file - Not verified |
350 | | DeleteLayoutPreset | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Remove saved layout - Not verified |
351 | | GetSetting | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Get project setting - Not verified |
352 | | SetSetting | 🐞 | 🐞 | 🐞 | ⚠️ | ⚠️ | Change project setting - Failed with parameter type issues |
353 | | GetRenderJobStatus | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Get job progress info - Not verified |
354 | | GetRenderPresetList | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | List render presets - Not verified |
355 | | ImportRenderPresets | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Import presets file - Not verified |
356 | | ExportRenderPresets | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Export presets to file - Not verified |
357 | | GetCurrentRenderFormatAndCodec | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Get format settings - Not verified |
358 | | SetCurrentRenderFormatAndCodec | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Set format settings - Not verified |
359 |
360 | ### MediaPool Object Methods
361 |
362 | | Method | Implementation | Cursor (Mac) | Claude (Mac) | Cursor (Win) | Claude (Win) | Notes |
363 | |--------|---------------|--------------|--------------|--------------|--------------|-------|
364 | | GetRootFolder | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Get root media folder - Not verified |
365 | | AddSubFolder | 🐞 | 🐞 | 🐞 | ⚠️ | ⚠️ | Create new subfolder - Failed with existing folder name |
366 | | CreateEmptyTimeline | 🐞 | 🐞 | 🐞 | ⚠️ | ⚠️ | Create blank timeline - Failed with existing name |
367 | | AppendToTimeline | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Add clips to timeline - Not verified |
368 | | ImportMedia | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Import media files - Not verified |
369 | | ExportMetadata | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Export clip metadata - Not verified |
370 | | DeleteClips | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Remove clips from pool - Not verified |
371 | | MoveClips | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Move clips between bins - Not verified |
372 | | GetCurrentFolder | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Get active folder - Not verified |
373 | | SetCurrentFolder | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Switch active folder - Not verified |
374 | | GetClipMatteList | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Get clip matte files - Not verified |
375 | | AddClipMatte | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Add matte to clip - Not verified |
376 | | DeleteClipMatte | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Remove clip matte - Not verified |
377 | | RelinkClips | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Reconnect media files - Not verified |
378 | | UnlinkClips | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Disconnect media files - Not verified |
379 | | LinkProxyMedia | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Connect proxy media - Not verified |
380 | | UnlinkProxyMedia | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Remove proxy links - Not verified |
381 | | ReplaceClip | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Replace with new media - Not verified |
382 |
383 | ### Gallery Object Methods
384 |
385 | | Method | Implementation | Cursor (Mac) | Claude (Mac) | Cursor (Win) | Claude (Win) | Notes |
386 | |--------|---------------|--------------|--------------|--------------|--------------|-------|
387 | | GetAlbumName | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Get current album name - Not verified |
388 | | SetAlbumName | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Rename current album - Not verified |
389 | | GetCurrentAlbum | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Get active album - Not verified |
390 | | SetCurrentAlbum | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Switch to album - Not verified |
391 | | GetAlbumList | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | List all albums - Not verified |
392 | | CreateAlbum | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Create new album - Not verified |
393 | | DeleteAlbum | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Remove album - Not verified |
394 | | GetStillList | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | List album stills - Not verified |
395 | | DeleteStill | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Delete still - Not verified |
396 | | ExportStills | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Save stills to files - Not verified |
397 | | ImportStills | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Load stills from files - Not verified |
398 |
399 | ### ColorPage Object Methods
400 |
401 | | Method | Implementation | Cursor (Mac) | Claude (Mac) | Cursor (Win) | Claude (Win) | Notes |
402 | |--------|---------------|--------------|--------------|--------------|--------------|-------|
403 | | GetLUTs | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Get available LUTs - Not verified |
404 | | GetCurrentNode | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Get active color node - Not verified |
405 | | GetNodeList | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | List all color nodes - Not verified |
406 | | SelectNode | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Switch active node - Not verified |
407 | | AddNode | 🐞 | 🐞 | 🐞 | ⚠️ | ⚠️ | Add new node - Failed with "Cannot access grade object" |
408 | | DeleteNode | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Remove node - Not verified |
409 | | SetPrimaryColorGrade | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Apply primary correction - Not verified |
410 | | SetColorWheelPrimaryParam | 🐞 | 🐞 | 🐞 | ⚠️ | ⚠️ | Adjust primary wheel - Failed with "Cannot access grade object" |
411 | | SetColorWheelLogParam | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Adjust log wheel - Not verified |
412 | | GetKeyframeMode | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Get keyframe mode - Not verified |
413 | | SetKeyframeMode | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Set keyframe mode - Not verified |
414 | | ApplyLUT | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Apply LUT to node - Not verified |
415 | | ExportLUT | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Export node as LUT - Not verified |
416 | | GetColorVersion | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Get current version - Not verified |
417 | | GetColorVersions | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | List all versions - Not verified |
418 | | CreateColorVersion | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Create new version - Not verified |
419 | | DeleteColorVersion | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Remove version - Not verified |
420 | | LoadColorVersion | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Switch to version - Not verified |
421 | | GetColorGroupList | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | List color groups - Not verified |
422 | | CreateColorGroup | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Create new group - Not verified |
423 | | DeleteColorGroup | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Remove group - Not verified |
424 |
425 | ### Delivery Object Methods
426 |
427 | | Method | Implementation | Cursor (Mac) | Claude (Mac) | Cursor (Win) | Claude (Win) | Notes |
428 | |--------|---------------|--------------|--------------|--------------|--------------|-------|
429 | | AddRenderJob | 🐞 | 🐞 | 🐞 | ⚠️ | ⚠️ | Add to render queue - Failed in our testing |
430 | | DeleteRenderJob | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Remove render job - Not verified |
431 | | DeleteAllRenderJobs | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Clear render queue - Verified working |
432 | | GetRenderJobList | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | List queued jobs - Not verified |
433 | | GetRenderPresetList | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | List available presets - Not verified |
434 | | GetRenderFormats | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | List output formats - Not verified |
435 | | GetRenderCodecs | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | List available codecs - Not verified |
436 | | RenderJobStatus | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Get job status - Not verified |
437 | | IsRenderingInProgress | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Check render activity - Not verified |
438 | | StartRendering | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Begin render process - Not verified |
439 | | StopRendering | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | Cancel rendering - Not verified |
440 |
441 | ## Implementation Details
442 |
443 | ### Object Inspection
444 |
445 | The object inspection implementation provides comprehensive functionality for:
446 |
447 | 1. **API Exploration** - Inspect Resolve API objects to discover methods and properties
448 | 2. **Method Analysis** - Get detailed information about object methods and their parameters
449 | 3. **Property Inspection** - Access object properties with type information
450 | 4. **Python Integration** - Combines Python's introspection with structured output
451 | 5. **Documentation Generation** - Can be used to create documentation for API objects
452 |
453 | ### Layout Presets
454 |
455 | The layout presets implementation enables:
456 |
457 | 1. **Preset Management** - List, save, load, export, and import UI layout presets
458 | 2. **User Interface Customization** - Store and recall different UI layouts for different tasks
459 | 3. **Workflow Optimization** - Quick switching between different interface configurations
460 | 4. **Cross-Project Sharing** - Export and import layouts between different projects or systems
461 |
462 | ### App Control
463 |
464 | The app control implementation provides:
465 |
466 | 1. **Application Management** - Functions to control the Resolve application itself
467 | 2. **State Monitoring** - Check application state and version information
468 | 3. **Settings Access** - Open project settings and preferences dialogs
469 | 4. **Session Control** - Safely quit or restart the application programmatically
470 |
471 | ### Cloud Project Operations
472 |
473 | The cloud project operations implementation provides:
474 |
475 | 1. **Cloud Project Creation** - Create new cloud projects with specified settings
476 | 2. **Project Restoration** - Restore cloud projects from online storage
477 | 3. **Import Functionality** - Import cloud projects into the local database
478 | 4. **User Management** - Add, remove, and manage users for collaborative workflow
479 | 5. **Export Functions** - Export local projects to cloud storage
480 |
481 | ### Audio Synchronization
482 |
483 | The audio synchronization implementation supports:
484 |
485 | 1. **Multi-camera workflows** - Synchronizing video clips from multiple cameras with separate audio
486 | 2. **External audio sources** - Integrating audio from external recorders
487 | 3. **Sync method options** - Support for both waveform analysis and timecode-based synchronization
488 | 4. **Organization workflow** - Automatic organization of synced clips into dedicated bins
489 |
490 | The implementation supports these parameters:
491 |
492 | 1. **clip_names** - List of clips to synchronize
493 | 2. **sync_method** - "waveform" (audio pattern matching) or "timecode" (TC matching)
494 | 3. **append_mode** - Toggle between appending audio tracks or replacing audio
495 | 4. **target_bin** - Optional bin name for organization
496 |
497 | ### Proxy Media Management
498 |
499 | Comprehensive proxy media functionality for:
500 |
501 | 1. **Proxy workflow support** - Connecting high-resolution clips to lower-resolution proxy media
502 | 2. **Performance optimization** - Improving playback performance with proxy media
503 | 3. **Quality toggling** - Easily switching between proxy and full-resolution media
504 | 4. **Path management** - Maintaining proper file paths for proxies
505 | 5. **Quality settings** - Control proxy generation quality (quarter, half, three-quarter, full)
506 |
507 | ### Cache Management
508 |
509 | The cache management implementation provides detailed control over:
510 |
511 | 1. **Cache Modes** - Control over cache usage with Auto/On/Off settings
512 | 2. **Optimized Media** - Management of optimized media settings and generation
513 | 3. **Proxy Media** - Control of proxy media settings, quality, and usage
514 | 4. **Mode Selection** - Simple mode selection with human-friendly options
515 |
516 | ## Planned Features
517 |
518 | Next development priorities:
519 |
520 | 1. **Fusion Page Integration** - Access to Fusion scripting and composition management
521 | 2. **Fairlight Page Operations** - Audio editing and mixing functionality
522 | 3. **Media Storage Management** - Advanced media storage and organization tools
523 | 4. **Render Job Operations** - Comprehensive render queue management with job ID support
524 | 5. **Timeline Export Properties** - Export formats including AAF, XML, EDL, etc.
525 | 6. **Windows Platform Compatibility** - Ensuring full functionality across platforms
526 |
527 | ### Fairlight Page Operations
528 |
529 | | Feature | Implementation | Cursor (Mac) | Claude (Mac) | Cursor (Win) | Claude (Win) | Notes |
530 | |---------|---------------|--------------|--------------|--------------|--------------|-------|
531 | | Audio Levels | 🟡 | - | - | - | - | Control audio levels |
532 | | Audio Effects | 🟡 | - | - | - | - | Apply audio effects |
533 | | Audio Routing | 🟡 | - | - | - | - | Configure audio routing |
534 | | Audio Metering | 🟡 | - | - | - | - | Monitor audio levels |
535 | | Track Management | 🟡 | - | - | - | - | Add/remove/edit audio tracks |
536 | | Sound Libraries | 🟡 | - | - | - | - | Access sound effects libraries |
537 | | Voice Isolation | 🟡 | - | - | - | - | AI-powered voice separation |
538 | | Noise Removal | 🟡 | - | - | - | - | Audio cleanup tools |
539 |
540 | ### Fusion Page Integration
541 |
542 | | Feature | Implementation | Cursor (Mac) | Claude (Mac) | Cursor (Win) | Claude (Win) | Notes |
543 | |---------|---------------|--------------|--------------|--------------|--------------|-------|
544 | | Fusion Composition | 🟡 | - | - | - | - | Create/edit Fusion compositions |
545 | | Node Graph | 🟡 | - | - | - | - | Work with Fusion node graph |
546 | | Add Effects | 🟡 | - | - | - | - | Add visual effects nodes |
547 | | Animation | 🟡 | - | - | - | - | Animate properties and parameters |
548 | | Templates | 🟡 | - | - | - | - | Use/save effect templates |
549 | | 3D Objects | 🟡 | - | - | - | - | Work with 3D elements |
550 | | Particle Systems | 🟡 | - | - | - | - | Create and edit particle effects |
551 | | Text Generation | 🟡 | - | - | - | - | Create text effects and animations |
552 |
553 | ### Edit Page Operations
554 |
555 | | Feature | Implementation | Cursor (Mac) | Claude (Mac) | Cursor (Win) | Claude (Win) | Notes |
556 | |---------|---------------|--------------|--------------|--------------|--------------|-------|
557 | | Clip Editing | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Edit clip properties |
558 | | Transitions | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Add/edit transitions |
559 | | Effects | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Apply video effects |
560 | | Generators | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Add titles, solids, etc. |
561 | | Speed Effects | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | Control clip playback speed |
562 | | Dynamic Zoom | 🟡 | - | - | - | - | Ken Burns style effects |
563 | | Stabilization | 🟡 | - | - | - | - | Video stabilization tools |
564 | | Smart Reframe | 🟡 | - | - | - | - | AI-based reframing for different aspect ratios |
565 |
566 | ## Testing Summary
567 |
568 | During our testing process, we've identified several key issues and limitations:
569 |
570 | 1. **Color Page Operations**: Several color-related operations failed with "Cannot access grade object" errors, including AddNode and SetColorWheelPrimaryParam. These issues may be related to the current project state or clip selection.
571 |
572 | 2. **Delivery Operations**: Adding render jobs to the queue consistently failed in our tests, though clearing the render queue works correctly.
573 |
574 | 3. **Media Pool Operations**: Some operations such as creating new bins and timelines failed when existing items with the same name were present, indicating a need for better error handling or checking.
575 |
576 | 4. **Proxy and Transcription**: Proxy and transcription operations failed with various issues, including "Clip not found" errors, suggesting the need for better media management integration.
577 |
578 | 5. **Project Settings**: Setting project settings failed with parameter type issues, suggesting a mismatch between the expected and provided parameter formats.
579 |
580 | ### Next Steps
581 |
582 | Based on our testing, we recommend:
583 |
584 | 1. Implementing better error handling and validation in the API endpoints
585 | 2. Adding more robust logging for troubleshooting
586 | 3. Creating comprehensive test cases for each feature category
587 | 4. Focusing on fixing the most critical issues in color grading and rendering first
588 | 5. Adding better documentation for parameter types and expected formats
589 |
590 | The MCP server has good fundamental implementation but requires significant testing and debugging to reach production readiness.
591 |
```
--------------------------------------------------------------------------------
/src/api/color_operations.py:
--------------------------------------------------------------------------------
```python
1 | #!/usr/bin/env python3
2 | """
3 | DaVinci Resolve Color Page Operations
4 | """
5 |
6 | import logging
7 | from typing import Dict, Any, List, Optional, Tuple, Union
8 |
9 | logger = logging.getLogger("davinci-resolve-mcp.color")
10 |
11 | def get_current_node(resolve) -> Dict[str, Any]:
12 | """Get information about the current node in the color page.
13 |
14 | Args:
15 | resolve: The DaVinci Resolve instance
16 |
17 | Returns:
18 | Dictionary with current node information
19 | """
20 | if resolve is None:
21 | return {"error": "Not connected to DaVinci Resolve"}
22 |
23 | project_manager = resolve.GetProjectManager()
24 | if not project_manager:
25 | return {"error": "Failed to get Project Manager"}
26 |
27 | current_project = project_manager.GetCurrentProject()
28 | if not current_project:
29 | return {"error": "No project currently open"}
30 |
31 | # First, ensure we're on the color page
32 | current_page = resolve.GetCurrentPage()
33 | if current_page.lower() != "color":
34 | return {"error": f"Not on Color page. Current page is: {current_page}"}
35 |
36 | # Get the current timeline
37 | current_timeline = current_project.GetCurrentTimeline()
38 | if not current_timeline:
39 | return {"error": "No timeline currently active"}
40 |
41 | try:
42 | # Access color-specific functionality through the timeline
43 | # First get the current clip in the timeline
44 | current_clip = current_timeline.GetCurrentVideoItem()
45 | if not current_clip:
46 | return {"error": "No clip is currently selected in the timeline"}
47 |
48 | # Get the clip's grade
49 | current_grade = current_clip.GetCurrentGrade()
50 | if not current_grade:
51 | return {"error": "Failed to get current grade"}
52 |
53 | # Get the currently selected node
54 | current_node_index = current_grade.GetCurrentNode()
55 | if current_node_index < 1:
56 | return {"error": "No node is currently selected"}
57 |
58 | # Get node count
59 | node_count = current_grade.GetNodeCount()
60 |
61 | # Get information about the current node
62 | node_info = {
63 | "clip_name": current_clip.GetName(),
64 | "node_index": current_node_index,
65 | "node_count": node_count,
66 | "is_serial": current_grade.IsSerial(current_node_index),
67 | "is_parallel": current_grade.IsParallel(current_node_index),
68 | "is_layer": current_grade.IsLayer(current_node_index),
69 | }
70 |
71 | # Try to get node name
72 | try:
73 | node_name = current_grade.GetNodeName(current_node_index)
74 | node_info["name"] = node_name
75 | except:
76 | node_info["name"] = f"Node {current_node_index}"
77 |
78 | # Try to get additional node properties if available
79 | try:
80 | # Check for common node properties that might be available
81 | properties = {}
82 |
83 | # Check if node is enabled
84 | try:
85 | properties["enabled"] = current_grade.IsNodeEnabled(current_node_index)
86 | except:
87 | pass
88 |
89 | # Get node type if available
90 | try:
91 | properties["type"] = current_grade.GetNodeType(current_node_index)
92 | except:
93 | pass
94 |
95 | # Add properties if we found any
96 | if properties:
97 | node_info["properties"] = properties
98 | except:
99 | pass
100 |
101 | return node_info
102 |
103 | except Exception as e:
104 | return {"error": f"Error getting current node: {str(e)}"}
105 |
106 | def apply_lut(resolve, lut_path: str, node_index: int = None) -> str:
107 | """Apply a LUT to a node in the color page.
108 |
109 | Args:
110 | resolve: The DaVinci Resolve instance
111 | lut_path: Path to the LUT file to apply
112 | node_index: Index of the node to apply the LUT to (uses current node if None)
113 |
114 | Returns:
115 | String indicating success or failure with detailed error message
116 | """
117 | if resolve is None:
118 | return "Error: Not connected to DaVinci Resolve"
119 |
120 | # Validate LUT path
121 | if not lut_path:
122 | return "Error: LUT path cannot be empty"
123 |
124 | import os
125 | if not os.path.exists(lut_path):
126 | return f"Error: LUT file '{lut_path}' does not exist"
127 |
128 | # Check file extension for supported LUT types
129 | valid_extensions = ['.cube', '.3dl', '.lut', '.mga']
130 | file_extension = os.path.splitext(lut_path)[1].lower()
131 | if file_extension not in valid_extensions:
132 | return f"Error: Unsupported LUT file format. Supported formats: {', '.join(valid_extensions)}"
133 |
134 | project_manager = resolve.GetProjectManager()
135 | if not project_manager:
136 | return "Error: Failed to get Project Manager"
137 |
138 | current_project = project_manager.GetCurrentProject()
139 | if not current_project:
140 | return "Error: No project currently open"
141 |
142 | # First, ensure we're on the color page
143 | current_page = resolve.GetCurrentPage()
144 | if current_page.lower() != "color":
145 | # Try to switch to color page
146 | result = resolve.OpenPage("color")
147 | if not result:
148 | return f"Error: Failed to switch to Color page. Current page is: {current_page}"
149 |
150 | # Get the current timeline
151 | current_timeline = current_project.GetCurrentTimeline()
152 | if not current_timeline:
153 | return "Error: No timeline currently active"
154 |
155 | try:
156 | # Get the current clip in the timeline
157 | current_clip = current_timeline.GetCurrentVideoItem()
158 | if not current_clip:
159 | return "Error: No clip is currently selected in the timeline"
160 |
161 | # Get the clip's grade
162 | current_grade = current_clip.GetCurrentGrade()
163 | if not current_grade:
164 | return "Error: Failed to get current grade"
165 |
166 | # Determine which node to apply the LUT to
167 | target_node_index = node_index
168 | if target_node_index is None:
169 | # Use the currently selected node
170 | target_node_index = current_grade.GetCurrentNode()
171 | if target_node_index < 1:
172 | return "Error: No node is currently selected"
173 | else:
174 | # Validate the provided node index
175 | node_count = current_grade.GetNodeCount()
176 | if target_node_index < 1 or target_node_index > node_count:
177 | return f"Error: Invalid node index {target_node_index}. Valid range: 1-{node_count}"
178 |
179 | # Apply the LUT to the node
180 | result = current_grade.ApplyLUT(target_node_index, lut_path)
181 |
182 | if result:
183 | # Try to get the node name for a better message
184 | try:
185 | node_name = current_grade.GetNodeName(target_node_index)
186 | return f"Successfully applied LUT '{os.path.basename(lut_path)}' to node '{node_name}' (index {target_node_index})"
187 | except:
188 | return f"Successfully applied LUT '{os.path.basename(lut_path)}' to node {target_node_index}"
189 | else:
190 | return f"Failed to apply LUT to node {target_node_index}"
191 |
192 | except Exception as e:
193 | return f"Error applying LUT: {str(e)}"
194 |
195 | def add_node(resolve, node_type: str = "serial", label: str = None) -> str:
196 | """Add a new node to the current grade in the color page.
197 |
198 | Args:
199 | resolve: The DaVinci Resolve instance
200 | node_type: Type of node to add. Options: 'serial', 'parallel', 'layer'
201 | label: Optional label/name for the new node
202 |
203 | Returns:
204 | String indicating success or failure with detailed error message
205 | """
206 | if resolve is None:
207 | return "Error: Not connected to DaVinci Resolve"
208 |
209 | # Validate node type
210 | valid_node_types = ['serial', 'parallel', 'layer']
211 | if node_type.lower() not in valid_node_types:
212 | return f"Error: Invalid node type. Must be one of: {', '.join(valid_node_types)}"
213 |
214 | logger.info(f"Adding {node_type} node with label: {label if label else 'None'}")
215 |
216 | project_manager = resolve.GetProjectManager()
217 | if not project_manager:
218 | logger.error("Failed to get Project Manager")
219 | return "Error: Failed to get Project Manager"
220 |
221 | current_project = project_manager.GetCurrentProject()
222 | if not current_project:
223 | logger.error("No project currently open")
224 | return "Error: No project currently open"
225 |
226 | # First, ensure we're on the color page
227 | current_page = resolve.GetCurrentPage()
228 | if current_page.lower() != "color":
229 | # Try to switch to color page
230 | logger.info(f"Currently on {current_page} page, switching to color page")
231 | result = resolve.OpenPage("color")
232 | if not result:
233 | logger.error(f"Failed to switch to Color page. Current page is: {current_page}")
234 | return f"Error: Failed to switch to Color page. Current page is: {current_page}"
235 | logger.info("Successfully switched to color page")
236 |
237 | # Get the current timeline
238 | current_timeline = current_project.GetCurrentTimeline()
239 | if not current_timeline:
240 | logger.error("No timeline currently active")
241 | return "Error: No timeline currently active"
242 |
243 | try:
244 | # Use the helper function to ensure a clip is selected
245 | clip_selected, current_clip, message = ensure_clip_selected(resolve, current_timeline)
246 |
247 | if not clip_selected or not current_clip:
248 | logger.error("No clip could be selected automatically")
249 | return f"Error: {message}. Please select a clip manually in DaVinci Resolve."
250 |
251 | logger.info(f"Working with clip: {current_clip.GetName()}")
252 |
253 | # Get the clip's grade
254 | # This is where the NoneType error typically occurs
255 | logger.info("Attempting to get current grade")
256 |
257 | # First method: Direct approach
258 | try:
259 | current_grade = current_clip.GetCurrentGrade()
260 | if current_grade:
261 | logger.info("Successfully got current grade using GetCurrentGrade()")
262 | else:
263 | logger.warning("GetCurrentGrade() returned None")
264 | except Exception as e:
265 | logger.error(f"Error getting current grade via GetCurrentGrade(): {str(e)}")
266 | current_grade = None
267 |
268 | # Alternative approach if the first method failed
269 | if not current_grade:
270 | logger.info("Attempting alternative methods to access grade functionality")
271 |
272 | # Try to select the clip first to ensure it's active
273 | try:
274 | # Ensure clip is selected in the timeline
275 | logger.info("Trying to select the clip in timeline again")
276 | current_timeline.SetCurrentVideoItem(current_clip)
277 | logger.info(f"Selected clip {current_clip.GetName()} in timeline")
278 |
279 | # Try to get grade again after selection
280 | current_grade = current_clip.GetCurrentGrade()
281 | if current_grade:
282 | logger.info("Successfully got current grade after selection")
283 | except Exception as e:
284 | logger.error(f"Error in alternative selection approach: {str(e)}")
285 |
286 | # Direct node creation if we still don't have a grade object
287 | if not current_grade:
288 | logger.warning("Could not get grade object, attempting direct node creation")
289 |
290 | try:
291 | # Try using the node graph directly through ColorPage
292 | color_page = project_manager.GetCurrentPage()
293 | if color_page and hasattr(color_page, "GetNodeGraph"):
294 | node_graph = color_page.GetNodeGraph()
295 | if node_graph:
296 | logger.info("Successfully got node graph through ColorPage")
297 |
298 | # Direct node creation using node graph
299 | if node_type.lower() == "serial":
300 | result = node_graph.AddSerialNode()
301 | elif node_type.lower() == "parallel":
302 | result = node_graph.AddParallelNode()
303 | elif node_type.lower() == "layer":
304 | result = node_graph.AddLayerNode()
305 |
306 | if result:
307 | return f"Successfully added {node_type} node using direct NodeGraph approach"
308 | else:
309 | logger.error(f"Failed to add {node_type} node using NodeGraph")
310 | except Exception as e:
311 | logger.error(f"Error in direct node creation attempt: {str(e)}")
312 |
313 | return f"Error adding {node_type} node: Cannot access grade object. The clip may not be properly graded yet."
314 |
315 | logger.info("Proceeding with node addition with valid grade object")
316 | # Add the appropriate type of node
317 | result = False
318 | method_name = ""
319 |
320 | if node_type.lower() == "serial":
321 | # Add a serial node after the current node
322 | method_name = "AddSerialNode"
323 | logger.info(f"Calling {method_name}()")
324 | result = current_grade.AddSerialNode()
325 | elif node_type.lower() == "parallel":
326 | # Add a parallel node
327 | method_name = "AddParallelNode"
328 | logger.info(f"Calling {method_name}()")
329 | result = current_grade.AddParallelNode()
330 | elif node_type.lower() == "layer":
331 | # Add a layer node
332 | method_name = "AddLayerNode"
333 | logger.info(f"Calling {method_name}()")
334 | result = current_grade.AddLayerNode()
335 |
336 | if not result:
337 | logger.error(f"Failed to add {node_type} node using {method_name}()")
338 | return f"Failed to add {node_type} node using {method_name}()"
339 |
340 | # Get the new node count and find the newly added node
341 | new_node_count = current_grade.GetNodeCount()
342 | logger.info(f"New node count: {new_node_count}")
343 |
344 | # Get the new node index - it should be the currently selected node
345 | new_node_index = current_grade.GetCurrentNode()
346 | logger.info(f"New node index: {new_node_index}")
347 |
348 | # Set label if provided
349 | if label and new_node_index > 0:
350 | try:
351 | logger.info(f"Setting node label to '{label}'")
352 | current_grade.SetNodeLabel(new_node_index, label)
353 | node_label_info = f" with label '{label}'"
354 | except Exception as e:
355 | logger.warning(f"Failed to set node label: {str(e)}")
356 | node_label_info = f" (couldn't set label to '{label}')"
357 | else:
358 | node_label_info = ""
359 |
360 | logger.info(f"Successfully added {node_type} node (index {new_node_index}){node_label_info}")
361 | return f"Successfully added {node_type} node (index {new_node_index}){node_label_info}"
362 |
363 | except Exception as e:
364 | logger.error(f"Error adding {node_type} node: {str(e)}")
365 | return f"Error adding {node_type} node: {str(e)}"
366 |
367 | def copy_grade(resolve, source_clip_name: str = None, target_clip_name: str = None, mode: str = "full") -> str:
368 | """Copy a grade from one clip to another in the color page.
369 |
370 | Args:
371 | resolve: The DaVinci Resolve instance
372 | source_clip_name: Name of the source clip to copy grade from (uses current clip if None)
373 | target_clip_name: Name of the target clip to apply grade to (uses current clip if None)
374 | mode: What to copy - 'full' (entire grade), 'current_node', or 'all_nodes'
375 |
376 | Returns:
377 | String indicating success or failure with detailed error message
378 | """
379 | if resolve is None:
380 | return "Error: Not connected to DaVinci Resolve"
381 |
382 | # Validate copy mode
383 | valid_modes = ['full', 'current_node', 'all_nodes']
384 | if mode.lower() not in valid_modes:
385 | return f"Error: Invalid copy mode. Must be one of: {', '.join(valid_modes)}"
386 |
387 | project_manager = resolve.GetProjectManager()
388 | if not project_manager:
389 | return "Error: Failed to get Project Manager"
390 |
391 | current_project = project_manager.GetCurrentProject()
392 | if not current_project:
393 | return "Error: No project currently open"
394 |
395 | # First, ensure we're on the color page
396 | current_page = resolve.GetCurrentPage()
397 | if current_page.lower() != "color":
398 | # Try to switch to color page
399 | result = resolve.OpenPage("color")
400 | if not result:
401 | return f"Error: Failed to switch to Color page. Current page is: {current_page}"
402 |
403 | # Get the current timeline
404 | current_timeline = current_project.GetCurrentTimeline()
405 | if not current_timeline:
406 | return "Error: No timeline currently active"
407 |
408 | try:
409 | # Get all clips in the timeline
410 | all_video_clips = []
411 |
412 | # Get video track count
413 | video_track_count = current_timeline.GetTrackCount("video")
414 |
415 | # Gather all clips from video tracks
416 | for track_index in range(1, video_track_count + 1):
417 | track_items = current_timeline.GetItemListInTrack("video", track_index)
418 | if track_items:
419 | all_video_clips.extend(track_items)
420 |
421 | # Get the source clip
422 | source_clip = None
423 | if source_clip_name:
424 | # Find the source clip by name
425 | for clip in all_video_clips:
426 | if clip and clip.GetName() == source_clip_name:
427 | source_clip = clip
428 | break
429 |
430 | if not source_clip:
431 | return f"Error: Source clip '{source_clip_name}' not found in timeline"
432 | else:
433 | # Use the current clip as source
434 | source_clip = current_timeline.GetCurrentVideoItem()
435 | if not source_clip:
436 | return "Error: No clip is currently selected to use as source"
437 | source_clip_name = source_clip.GetName()
438 |
439 | # Get the source grade
440 | source_grade = source_clip.GetCurrentGrade()
441 | if not source_grade:
442 | return f"Error: Failed to get grade from source clip '{source_clip_name}'"
443 |
444 | # Get the target clip
445 | target_clip = None
446 | if target_clip_name:
447 | # Check if target is same as source
448 | if target_clip_name == source_clip_name:
449 | return f"Error: Source and target clips cannot be the same (both are '{source_clip_name}')"
450 |
451 | # Find the target clip by name
452 | for clip in all_video_clips:
453 | if clip and clip.GetName() == target_clip_name:
454 | target_clip = clip
455 | break
456 |
457 | if not target_clip:
458 | return f"Error: Target clip '{target_clip_name}' not found in timeline"
459 | else:
460 | # Use the current clip as target (need to select a different clip first)
461 | current_clip = current_timeline.GetCurrentVideoItem()
462 |
463 | if not current_clip:
464 | return "Error: No clip is currently selected to use as target"
465 |
466 | if current_clip.GetName() == source_clip_name:
467 | return "Error: Cannot copy grade to the same clip. Please specify a different target clip."
468 |
469 | target_clip = current_clip
470 | target_clip_name = target_clip.GetName()
471 |
472 | # Get the target grade
473 | target_grade = target_clip.GetCurrentGrade()
474 | if not target_grade:
475 | return f"Error: Failed to get grade from target clip '{target_clip_name}'"
476 |
477 | # Select the target clip to make it active for grade operations
478 | current_timeline.SetCurrentVideoItem(target_clip)
479 |
480 | # Execute the copy based on the specified mode
481 | result = False
482 | if mode.lower() == "full":
483 | # Copy the entire grade including all nodes
484 | result = target_clip.CopyGrade(source_clip)
485 | elif mode.lower() == "current_node":
486 | # Copy only the current node from source to target
487 | source_node_index = source_grade.GetCurrentNode()
488 | target_node_index = target_grade.GetCurrentNode()
489 |
490 | if source_node_index < 1:
491 | return "Error: No node selected in source clip"
492 |
493 | if target_node_index < 1:
494 | return "Error: No node selected in target clip"
495 |
496 | # Copy the current node
497 | result = target_grade.CopyFromNodeToNode(source_grade, source_node_index, target_node_index)
498 | elif mode.lower() == "all_nodes":
499 | # Copy all nodes but keep other grade settings
500 | source_node_count = source_grade.GetNodeCount()
501 |
502 | if source_node_count < 1:
503 | return "Error: Source clip has no nodes to copy"
504 |
505 | # First, clear all nodes in target
506 | target_node_count = target_grade.GetNodeCount()
507 | for i in range(target_node_count, 0, -1):
508 | target_grade.DeleteNode(i)
509 |
510 | # Then, add nodes matching the source structure
511 | for i in range(1, source_node_count + 1):
512 | # Determine node type
513 | if source_grade.IsSerial(i):
514 | target_grade.AddSerialNode()
515 | elif source_grade.IsParallel(i):
516 | target_grade.AddParallelNode()
517 | elif source_grade.IsLayer(i):
518 | target_grade.AddLayerNode()
519 |
520 | # Copy node settings
521 | new_node_index = target_grade.GetCurrentNode()
522 | if new_node_index > 0:
523 | target_grade.CopyFromNodeToNode(source_grade, i, new_node_index)
524 |
525 | result = True
526 |
527 | if result:
528 | return f"Successfully copied grade from '{source_clip_name}' to '{target_clip_name}' using mode '{mode}'"
529 | else:
530 | return f"Failed to copy grade from '{source_clip_name}' to '{target_clip_name}' using mode '{mode}'"
531 |
532 | except Exception as e:
533 | return f"Error copying grade: {str(e)}"
534 |
535 | def get_color_wheels(resolve, node_index: int = None) -> Dict[str, Any]:
536 | """Get color wheel parameters for a specific node.
537 |
538 | Args:
539 | resolve: The DaVinci Resolve instance
540 | node_index: Index of the node to get color wheels from (uses current node if None)
541 |
542 | Returns:
543 | Dictionary with color wheel parameters
544 | """
545 | if resolve is None:
546 | return {"error": "Not connected to DaVinci Resolve"}
547 |
548 | project_manager = resolve.GetProjectManager()
549 | if not project_manager:
550 | return {"error": "Failed to get Project Manager"}
551 |
552 | current_project = project_manager.GetCurrentProject()
553 | if not current_project:
554 | return {"error": "No project currently open"}
555 |
556 | # First, ensure we're on the color page
557 | current_page = resolve.GetCurrentPage()
558 | if current_page.lower() != "color":
559 | return {"error": f"Not on Color page. Current page is: {current_page}"}
560 |
561 | # Get the current timeline
562 | current_timeline = current_project.GetCurrentTimeline()
563 | if not current_timeline:
564 | return {"error": "No timeline currently active"}
565 |
566 | try:
567 | # Get the current clip in the timeline
568 | current_clip = current_timeline.GetCurrentVideoItem()
569 | if not current_clip:
570 | return {"error": "No clip is currently selected in the timeline"}
571 |
572 | # Get the clip's grade
573 | current_grade = current_clip.GetCurrentGrade()
574 | if not current_grade:
575 | return {"error": "Failed to get current grade"}
576 |
577 | # Determine which node to get color wheels from
578 | target_node_index = node_index
579 | if target_node_index is None:
580 | # Use the currently selected node
581 | target_node_index = current_grade.GetCurrentNode()
582 | if target_node_index < 1:
583 | return {"error": "No node is currently selected"}
584 | else:
585 | # Validate the provided node index
586 | node_count = current_grade.GetNodeCount()
587 | if target_node_index < 1 or target_node_index > node_count:
588 | return {"error": f"Invalid node index {target_node_index}. Valid range: 1-{node_count}"}
589 |
590 | # Get node name if available
591 | node_name = ""
592 | try:
593 | node_name = current_grade.GetNodeName(target_node_index)
594 | except:
595 | node_name = f"Node {target_node_index}"
596 |
597 | # Get color wheel parameters
598 | color_wheels = {
599 | "node_index": target_node_index,
600 | "node_name": node_name,
601 | "clip_name": current_clip.GetName(),
602 | "wheels": {}
603 | }
604 |
605 | # Try to get each of the color wheels
606 | wheels_to_get = [
607 | {"name": "lift", "function_prefix": "GetLift"},
608 | {"name": "gamma", "function_prefix": "GetGamma"},
609 | {"name": "gain", "function_prefix": "GetGain"},
610 | {"name": "offset", "function_prefix": "GetOffset"},
611 | ]
612 |
613 | for wheel in wheels_to_get:
614 | wheel_name = wheel["name"]
615 | prefix = wheel["function_prefix"]
616 |
617 | wheel_data = {}
618 | try:
619 | # Try to get R, G, B, and Y (master) values
620 | for channel, channel_name in [("R", "red"), ("G", "green"), ("B", "blue"), ("Y", "master")]:
621 | # Build the function name dynamically
622 | function_name = f"{prefix}{channel}"
623 |
624 | if hasattr(current_grade, function_name):
625 | # Call the function with the node index
626 | getter_func = getattr(current_grade, function_name)
627 | value = getter_func(target_node_index)
628 | wheel_data[channel_name] = value
629 |
630 | if wheel_data:
631 | color_wheels["wheels"][wheel_name] = wheel_data
632 | except Exception as e:
633 | color_wheels["wheels"][wheel_name] = {"error": f"Could not get {wheel_name} wheel: {str(e)}"}
634 |
635 | # Try to get additional common color controls
636 | try:
637 | additional_controls = {}
638 |
639 | # Try to get contrast
640 | try:
641 | if hasattr(current_grade, "GetContrast"):
642 | additional_controls["contrast"] = current_grade.GetContrast(target_node_index)
643 | except:
644 | pass
645 |
646 | # Try to get saturation
647 | try:
648 | if hasattr(current_grade, "GetSaturation"):
649 | additional_controls["saturation"] = current_grade.GetSaturation(target_node_index)
650 | except:
651 | pass
652 |
653 | # Try to get color temperature
654 | try:
655 | if hasattr(current_grade, "GetColorTemp"):
656 | additional_controls["color_temp"] = current_grade.GetColorTemp(target_node_index)
657 | except:
658 | pass
659 |
660 | # Try to get tint
661 | try:
662 | if hasattr(current_grade, "GetTint"):
663 | additional_controls["tint"] = current_grade.GetTint(target_node_index)
664 | except:
665 | pass
666 |
667 | # Add additional controls if any were found
668 | if additional_controls:
669 | color_wheels["additional_controls"] = additional_controls
670 | except:
671 | pass
672 |
673 | return color_wheels
674 |
675 | except Exception as e:
676 | return {"error": f"Error getting color wheel parameters: {str(e)}"}
677 |
678 | def set_color_wheel_param(resolve, wheel: str, param: str, value: float, node_index: int = None) -> str:
679 | """Set a color wheel parameter for a node.
680 |
681 | Args:
682 | resolve: The DaVinci Resolve instance
683 | wheel: Which color wheel to adjust ('lift', 'gamma', 'gain', 'offset')
684 | param: Which parameter to adjust ('red', 'green', 'blue', 'master')
685 | value: The value to set (typically between -1.0 and 1.0)
686 | node_index: Index of the node to set parameter for (uses current node if None)
687 |
688 | Returns:
689 | String indicating success or failure with detailed error message
690 | """
691 | if resolve is None:
692 | return "Error: Not connected to DaVinci Resolve"
693 |
694 | # Validate wheel
695 | valid_wheels = ['lift', 'gamma', 'gain', 'offset']
696 | if wheel.lower() not in valid_wheels:
697 | return f"Error: Invalid wheel name. Must be one of: {', '.join(valid_wheels)}"
698 |
699 | # Validate parameter
700 | valid_params = ['red', 'green', 'blue', 'master']
701 | if param.lower() not in valid_params:
702 | return f"Error: Invalid parameter name. Must be one of: {', '.join(valid_params)}"
703 |
704 | logger.info(f"Setting {wheel} {param} to {value}")
705 |
706 | # Map parameter names to channel identifiers used in the API
707 | param_to_channel = {
708 | 'red': 'R',
709 | 'green': 'G',
710 | 'blue': 'B',
711 | 'master': 'Y'
712 | }
713 |
714 | # Map wheel names to function name prefixes used in the API
715 | wheel_to_function_prefix = {
716 | 'lift': 'SetLift',
717 | 'gamma': 'SetGamma',
718 | 'gain': 'SetGain',
719 | 'offset': 'SetOffset'
720 | }
721 |
722 | project_manager = resolve.GetProjectManager()
723 | if not project_manager:
724 | logger.error("Failed to get Project Manager")
725 | return "Error: Failed to get Project Manager"
726 |
727 | current_project = project_manager.GetCurrentProject()
728 | if not current_project:
729 | logger.error("No project currently open")
730 | return "Error: No project currently open"
731 |
732 | # First, ensure we're on the color page
733 | current_page = resolve.GetCurrentPage()
734 | if current_page.lower() != "color":
735 | # Try to switch to color page
736 | logger.info(f"Currently on {current_page} page, switching to color page")
737 | result = resolve.OpenPage("color")
738 | if not result:
739 | logger.error(f"Failed to switch to Color page. Current page is: {current_page}")
740 | return f"Error: Failed to switch to Color page. Current page is: {current_page}"
741 | logger.info("Successfully switched to color page")
742 |
743 | # Get the current timeline
744 | current_timeline = current_project.GetCurrentTimeline()
745 | if not current_timeline:
746 | logger.error("No timeline currently active")
747 | return "Error: No timeline currently active"
748 |
749 | try:
750 | # Use the helper function to ensure a clip is selected
751 | clip_selected, current_clip, message = ensure_clip_selected(resolve, current_timeline)
752 |
753 | if not clip_selected or not current_clip:
754 | logger.error("No clip could be selected automatically")
755 | return f"Error: {message}. Please select a clip manually in DaVinci Resolve."
756 |
757 | logger.info(f"Working with clip: {current_clip.GetName()}")
758 |
759 | # Get the clip's grade
760 | # This is where the NoneType error typically occurs
761 | logger.info("Attempting to get current grade")
762 |
763 | # First method: Direct approach
764 | try:
765 | current_grade = current_clip.GetCurrentGrade()
766 | if current_grade:
767 | logger.info("Successfully got current grade using GetCurrentGrade()")
768 | else:
769 | logger.warning("GetCurrentGrade() returned None")
770 | except Exception as e:
771 | logger.error(f"Error getting current grade via GetCurrentGrade(): {str(e)}")
772 | current_grade = None
773 |
774 | # Alternative approach if the first method failed
775 | if not current_grade:
776 | logger.info("Attempting alternative methods to access grade functionality")
777 |
778 | # Try to select the clip first to ensure it's active
779 | try:
780 | # Ensure clip is selected in the timeline
781 | logger.info("Trying to select the clip in timeline again")
782 | current_timeline.SetCurrentVideoItem(current_clip)
783 | logger.info(f"Selected clip {current_clip.GetName()} in timeline")
784 |
785 | # Try to get grade again after selection
786 | current_grade = current_clip.GetCurrentGrade()
787 | if current_grade:
788 | logger.info("Successfully got current grade after selection")
789 | except Exception as e:
790 | logger.error(f"Error in alternative selection approach: {str(e)}")
791 |
792 | # Check if we have a valid grade object
793 | if not current_grade:
794 | logger.error("Could not get grade object after multiple attempts")
795 | return "Error setting color wheel parameter: Cannot access grade object. The clip may not be properly graded yet."
796 |
797 | logger.info("Proceeding with parameter setting with valid grade object")
798 |
799 | # Determine which node to set parameter for
800 | target_node_index = node_index
801 | if target_node_index is None:
802 | # Use the currently selected node
803 | logger.info("Getting current node index")
804 | target_node_index = current_grade.GetCurrentNode()
805 | if target_node_index < 1:
806 | logger.error("No node is currently selected")
807 | return "Error: No node is currently selected"
808 | logger.info(f"Using current node: {target_node_index}")
809 | else:
810 | # Validate the provided node index
811 | logger.info(f"Validating provided node index: {target_node_index}")
812 | node_count = current_grade.GetNodeCount()
813 | if target_node_index < 1 or target_node_index > node_count:
814 | logger.error(f"Invalid node index {target_node_index}. Valid range: 1-{node_count}")
815 | return f"Error: Invalid node index {target_node_index}. Valid range: 1-{node_count}"
816 |
817 | # Get node name for better reporting
818 | node_name = ""
819 | try:
820 | logger.info(f"Getting name for node {target_node_index}")
821 | node_name = current_grade.GetNodeName(target_node_index) or f"Node {target_node_index}"
822 | logger.info(f"Node name: {node_name}")
823 | except Exception as e:
824 | logger.warning(f"Could not get node name: {str(e)}")
825 | node_name = f"Node {target_node_index}"
826 |
827 | # Build the function name to call
828 | channel = param_to_channel[param.lower()]
829 | function_prefix = wheel_to_function_prefix[wheel.lower()]
830 | function_name = f"{function_prefix}{channel}"
831 | logger.info(f"Function to call: {function_name}")
832 |
833 | # Check if the function exists
834 | if not hasattr(current_grade, function_name):
835 | logger.error(f"Function '{function_name}' not found in DaVinci Resolve API")
836 | return f"Error: Function '{function_name}' not found in DaVinci Resolve API for setting {wheel} {param}"
837 |
838 | # Get the setter function
839 | setter_func = getattr(current_grade, function_name)
840 |
841 | # Set the parameter value
842 | logger.info(f"Calling {function_name}({target_node_index}, {value})")
843 | result = setter_func(target_node_index, value)
844 |
845 | if result:
846 | logger.info(f"Successfully set {wheel} {param} to {value} for {node_name}")
847 | return f"Successfully set {wheel} {param} to {value} for {node_name}"
848 | else:
849 | logger.error(f"Failed to set {wheel} {param} to {value} for {node_name}")
850 | return f"Failed to set {wheel} {param} to {value} for {node_name}"
851 |
852 | except Exception as e:
853 | logger.error(f"Error setting color wheel parameter: {str(e)}")
854 | return f"Error setting color wheel parameter: {str(e)}"
855 |
856 | def ensure_clip_selected(resolve, timeline) -> Tuple[bool, Optional[Any], str]:
857 | """Ensures a clip is selected in the timeline, selecting the first clip if needed.
858 |
859 | Args:
860 | resolve: The DaVinci Resolve instance
861 | timeline: The current timeline
862 |
863 | Returns:
864 | Tuple containing (success, clip_object, message)
865 | """
866 | # First check if there's already a clip selected
867 | current_clip = timeline.GetCurrentVideoItem()
868 | if current_clip:
869 | logger.info(f"Clip already selected: {current_clip.GetName()}")
870 | return True, current_clip, f"Using currently selected clip: {current_clip.GetName()}"
871 |
872 | # No clip selected, try to select the first clip
873 | logger.info("No clip currently selected, attempting to select first clip")
874 | try:
875 | # Get video tracks
876 | video_track_count = timeline.GetTrackCount("video")
877 | logger.info(f"Timeline has {video_track_count} video tracks")
878 |
879 | # Check each track for clips
880 | for track_index in range(1, video_track_count + 1):
881 | logger.info(f"Checking video track {track_index}")
882 |
883 | # Get clips in this track
884 | track_items = timeline.GetItemListInTrack("video", track_index)
885 | if not track_items or len(track_items) == 0:
886 | logger.info(f"No clips in track {track_index}")
887 | continue
888 |
889 | logger.info(f"Found {len(track_items)} clips in track {track_index}")
890 |
891 | # Try to select the first clip
892 | first_clip = track_items[0]
893 | if first_clip:
894 | clip_name = first_clip.GetName()
895 | logger.info(f"Attempting to select clip: {clip_name}")
896 |
897 | # Set it as the current clip
898 | timeline.SetCurrentVideoItem(first_clip)
899 |
900 | # Verify selection
901 | selected_clip = timeline.GetCurrentVideoItem()
902 | if selected_clip and selected_clip.GetName() == clip_name:
903 | logger.info(f"Successfully selected first clip: {clip_name}")
904 | return True, selected_clip, f"Automatically selected clip: {clip_name}"
905 | else:
906 | logger.warning("Failed to verify clip selection")
907 |
908 | # If we got here, we couldn't select a clip in this track
909 | logger.warning(f"Could not select a clip in track {track_index}")
910 |
911 | # If we reach here, we couldn't find or select any clips
912 | logger.warning("No clips found in any video track, or could not select any")
913 | return False, None, "Could not find any clips in the timeline to select"
914 |
915 | except Exception as e:
916 | logger.error(f"Error attempting to select a clip: {str(e)}")
917 | return False, None, f"Error selecting clip: {str(e)}"
```