DaVinci Resolve MCP

by samuelgursky
Verified
#!/usr/bin/env python3 """ DaVinci Resolve Timeline Operations """ import logging from typing import List, Dict, Any, Optional logger = logging.getLogger("davinci-resolve-mcp.timeline") def list_timelines(resolve) -> List[str]: """List all timelines in the current project.""" if resolve is None: return ["Error: Not connected to DaVinci Resolve"] project_manager = resolve.GetProjectManager() if not project_manager: return ["Error: Failed to get Project Manager"] current_project = project_manager.GetCurrentProject() if not current_project: return ["Error: No project currently open"] timeline_count = current_project.GetTimelineCount() timelines = [] for i in range(1, timeline_count + 1): timeline = current_project.GetTimelineByIndex(i) if timeline: timelines.append(timeline.GetName()) return timelines if timelines else ["No timelines found in the current project"] def get_current_timeline_info(resolve) -> Dict[str, Any]: """Get information about the current timeline.""" if resolve is None: return {"error": "Not connected to DaVinci Resolve"} project_manager = resolve.GetProjectManager() if not project_manager: return {"error": "Failed to get Project Manager"} current_project = project_manager.GetCurrentProject() if not current_project: return {"error": "No project currently open"} current_timeline = current_project.GetCurrentTimeline() if not current_timeline: return {"error": "No timeline currently active"} # Get basic timeline info info = { "name": current_timeline.GetName(), "framerate": current_timeline.GetSetting("timelineFrameRate"), "resolution": { "width": current_timeline.GetSetting("timelineResolutionWidth"), "height": current_timeline.GetSetting("timelineResolutionHeight") }, "start_timecode": current_timeline.GetStartTimecode() } return info def create_timeline(resolve, name: str) -> str: """Create a new timeline with the given name.""" if resolve is None: return "Error: Not connected to DaVinci Resolve" if not name: return "Error: Timeline name cannot be empty" project_manager = resolve.GetProjectManager() if not project_manager: return "Error: Failed to get Project Manager" current_project = project_manager.GetCurrentProject() if not current_project: return "Error: No project currently open" media_pool = current_project.GetMediaPool() if not media_pool: return "Error: Failed to get Media Pool" # Check if timeline already exists to avoid duplicates existing_timelines = list_timelines(resolve) if name in existing_timelines: return f"Error: Timeline '{name}' already exists" # Create the timeline timeline = media_pool.CreateEmptyTimeline(name) if timeline: return f"Successfully created timeline '{name}'" else: return f"Failed to create timeline '{name}'" def set_current_timeline(resolve, name: str) -> str: """Switch to a timeline by name.""" if resolve is None: return "Error: Not connected to DaVinci Resolve" if not name: return "Error: Timeline name cannot be empty" project_manager = resolve.GetProjectManager() if not project_manager: return "Error: Failed to get Project Manager" current_project = project_manager.GetCurrentProject() if not current_project: return "Error: No project currently open" # First get a list of all timelines timeline_count = current_project.GetTimelineCount() for i in range(1, timeline_count + 1): timeline = current_project.GetTimelineByIndex(i) if timeline and timeline.GetName() == name: # Found the timeline, set it as current current_project.SetCurrentTimeline(timeline) # Verify it was set current_timeline = current_project.GetCurrentTimeline() if current_timeline and current_timeline.GetName() == name: return f"Successfully switched to timeline '{name}'" else: return f"Error: Failed to switch to timeline '{name}'" return f"Error: Timeline '{name}' not found" def add_marker(resolve, frame: Optional[int] = None, color: str = "Blue", note: str = "") -> str: """Add a marker at the specified frame in the current timeline. Args: resolve: The DaVinci Resolve instance frame: The frame number to add the marker at (defaults to auto-selection if None) color: The marker color (Blue, Cyan, Green, Yellow, Red, Pink, Purple, Fuchsia, Rose, Lavender, Sky, Mint, Lemon, Sand, Cocoa, Cream) note: Text note to add to the marker Returns: String indicating success or failure with detailed error message """ if resolve is None: return "Error: Not connected to DaVinci Resolve" project_manager = resolve.GetProjectManager() if not project_manager: return "Error: Failed to get Project Manager" current_project = project_manager.GetCurrentProject() if not current_project: return "Error: No project currently open" current_timeline = current_project.GetCurrentTimeline() if not current_timeline: return "Error: No timeline currently active" # Get timeline information try: timeline_start = current_timeline.GetStartFrame() timeline_end = current_timeline.GetEndFrame() timeline_name = current_timeline.GetName() print(f"Timeline '{timeline_name}' frame range: {timeline_start}-{timeline_end}") except Exception as e: return f"Error: Failed to get timeline information: {str(e)}" # Validate marker color valid_colors = [ "Blue", "Cyan", "Green", "Yellow", "Red", "Pink", "Purple", "Fuchsia", "Rose", "Lavender", "Sky", "Mint", "Lemon", "Sand", "Cocoa", "Cream" ] if color not in valid_colors: return f"Error: Invalid marker color. Valid colors are: {', '.join(valid_colors)}" try: # Get information about clips in the timeline clips = [] for track_idx in range(1, 5): # Check first 4 video tracks try: track_clips = current_timeline.GetItemListInTrack("video", track_idx) if track_clips and len(track_clips) > 0: clips.extend(track_clips) except: continue if not clips: return "Error: No clips found in timeline. Add media to the timeline first." # Get existing markers to avoid conflicts existing_markers = current_timeline.GetMarkers() or {} # If no frame specified, find a good position if frame is None: # Try to find a frame in the middle of a clip that doesn't have a marker for clip in clips: clip_start = clip.GetStart() clip_end = clip.GetEnd() # Try middle of clip mid_frame = clip_start + ((clip_end - clip_start) // 2) if mid_frame not in existing_markers: frame = mid_frame break # Try middle + 1 if (mid_frame + 1) not in existing_markers: frame = mid_frame + 1 break # Try other positions in the clip for offset in [10, 20, 30, 40, 50]: test_frame = clip_start + offset if clip_start <= test_frame <= clip_end and test_frame not in existing_markers: frame = test_frame break # If we still don't have a frame, use the first valid position we can find if frame is None: for f in range(timeline_start, timeline_end, 10): if f not in existing_markers: # Check if this frame is within a clip for clip in clips: if clip.GetStart() <= f <= clip.GetEnd(): frame = f break if frame is not None: break # If we still don't have a frame, report error if frame is None: return "Error: Could not find a valid frame position for marker. Try specifying a frame number." # Frame specified - validate it else: # Check if frame is within timeline bounds if frame < timeline_start or frame > timeline_end: return f"Error: Frame {frame} is out of timeline bounds ({timeline_start}-{timeline_end})" # Check if frame already has a marker if frame in existing_markers: # Suggest an alternate frame alternate_found = False alternates = [frame + 1, frame - 1, frame + 2, frame + 5, frame + 10] for alt_frame in alternates: if timeline_start <= alt_frame <= timeline_end and alt_frame not in existing_markers: # Check if frame is within a clip for clip in clips: if clip.GetStart() <= alt_frame <= clip.GetEnd(): return f"Error: A marker already exists at frame {frame}. Try frame {alt_frame} instead." return f"Error: A marker already exists at frame {frame}. Try a different frame position." # Verify frame is within a clip frame_in_clip = False for clip in clips: if clip.GetStart() <= frame <= clip.GetEnd(): frame_in_clip = True break if not frame_in_clip: return f"Error: Frame {frame} is not within any media in the timeline. Markers must be on actual clips." # Add the marker print(f"Adding marker at frame {frame} with color {color}") result = current_timeline.AddMarker( frame, color, note or "Marker", note, 1, "" ) if result: return f"Successfully added {color} marker at frame {frame}" else: # One last check if a marker was created despite returning False updated_markers = current_timeline.GetMarkers() or {} if frame in updated_markers: return f"Successfully added marker at frame {frame} (unexpected response)" else: return f"Failed to add marker at frame {frame}. Try a different frame position." except Exception as e: return f"Error adding marker: {str(e)}"