davinci-resolve-mcp 2.23.0

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.
Files changed (92) hide show
  1. package/AGENTS.md +85 -0
  2. package/CHANGELOG.md +802 -0
  3. package/CLAUDE.md +15 -0
  4. package/LICENSE +21 -0
  5. package/README.md +159 -0
  6. package/SECURITY.md +53 -0
  7. package/bin/davinci-resolve-mcp.mjs +376 -0
  8. package/docs/README.md +56 -0
  9. package/docs/SKILL.md +1145 -0
  10. package/docs/authoring/fuse-dctl-authoring.md +242 -0
  11. package/docs/authoring/script-plugin-authoring.md +195 -0
  12. package/docs/contributing.md +82 -0
  13. package/docs/guides/color-decision-guide.md +387 -0
  14. package/docs/guides/editorial-decision-guide.md +136 -0
  15. package/docs/guides/media-analysis-guide.md +615 -0
  16. package/docs/guides/multicam-setup-guide.md +138 -0
  17. package/docs/install.md +198 -0
  18. package/docs/integrations/workflow-integrations.md +120 -0
  19. package/docs/kernels/README.md +28 -0
  20. package/docs/kernels/audio-fairlight-kernel.md +86 -0
  21. package/docs/kernels/color-grade-kernel.md +103 -0
  22. package/docs/kernels/extension-authoring-kernel.md +101 -0
  23. package/docs/kernels/fusion-composition-kernel.md +91 -0
  24. package/docs/kernels/media-pool-ingest-kernel.md +147 -0
  25. package/docs/kernels/project-lifecycle-kernel.md +120 -0
  26. package/docs/kernels/render-deliver-kernel.md +92 -0
  27. package/docs/kernels/review-annotation-kernel.md +110 -0
  28. package/docs/kernels/timeline-conform-interchange-kernel.md +99 -0
  29. package/docs/kernels/timeline-edit-kernel.md +189 -0
  30. package/docs/notes/codec-plugin-notes.md +136 -0
  31. package/docs/notes/dctl-notes.md +234 -0
  32. package/docs/notes/fusion-template-notes.md +136 -0
  33. package/docs/notes/lut-notes.md +136 -0
  34. package/docs/notes/openfx-notes.md +120 -0
  35. package/docs/process/release-process.md +152 -0
  36. package/docs/reference/api-coverage.md +488 -0
  37. package/docs/reference/resolve_scripting_api.txt +1012 -0
  38. package/examples/README.md +53 -0
  39. package/examples/markers/README.md +81 -0
  40. package/examples/media/README.md +94 -0
  41. package/examples/timeline/README.md +98 -0
  42. package/install.py +1196 -0
  43. package/package.json +52 -0
  44. package/scripts/audit_api_parity.py +275 -0
  45. package/scripts/live_media_analysis_polish_probe.py +65 -0
  46. package/src/__init__.py +3 -0
  47. package/src/analysis_dashboard.py +4936 -0
  48. package/src/control_panel.py +13 -0
  49. package/src/granular/__init__.py +17 -0
  50. package/src/granular/common.py +727 -0
  51. package/src/granular/folder.py +287 -0
  52. package/src/granular/gallery.py +306 -0
  53. package/src/granular/graph.py +309 -0
  54. package/src/granular/media_pool.py +679 -0
  55. package/src/granular/media_pool_item.py +852 -0
  56. package/src/granular/media_storage.py +179 -0
  57. package/src/granular/project.py +1594 -0
  58. package/src/granular/resolve_control.py +521 -0
  59. package/src/granular/timeline.py +1074 -0
  60. package/src/granular/timeline_item.py +2251 -0
  61. package/src/resolve_mcp_server.py +43 -0
  62. package/src/server.py +15691 -0
  63. package/src/utils/__init__.py +3 -0
  64. package/src/utils/app_control.py +319 -0
  65. package/src/utils/audio_fairlight_live_probe.py +263 -0
  66. package/src/utils/cdl.py +20 -0
  67. package/src/utils/cloud_operations.py +192 -0
  68. package/src/utils/color_grade_live_probe.py +444 -0
  69. package/src/utils/dctl_templates.py +368 -0
  70. package/src/utils/extension_authoring_live_probe.py +292 -0
  71. package/src/utils/fuse_templates.py +1968 -0
  72. package/src/utils/fusion_composition_live_probe.py +284 -0
  73. package/src/utils/layout_presets.py +333 -0
  74. package/src/utils/mcp_stdio.py +32 -0
  75. package/src/utils/media_analysis.py +3618 -0
  76. package/src/utils/media_analysis_jobs.py +796 -0
  77. package/src/utils/media_pool_ingest_live_probe.py +592 -0
  78. package/src/utils/multicam.py +393 -0
  79. package/src/utils/object_inspection.py +287 -0
  80. package/src/utils/platform.py +157 -0
  81. package/src/utils/project_lifecycle_live_probe.py +376 -0
  82. package/src/utils/project_properties.py +601 -0
  83. package/src/utils/render_deliver_live_probe.py +384 -0
  84. package/src/utils/resolve_connection.py +77 -0
  85. package/src/utils/review_annotation_live_probe.py +352 -0
  86. package/src/utils/script_templates.py +1193 -0
  87. package/src/utils/sync_detection.py +887 -0
  88. package/src/utils/timeline_conform_live_probe.py +280 -0
  89. package/src/utils/timeline_kernel_live_probe.py +1091 -0
  90. package/src/utils/timeline_kernel_probe.py +185 -0
  91. package/src/utils/timeline_title_text.py +87 -0
  92. package/src/utils/update_check.py +610 -0
@@ -0,0 +1,601 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ DaVinci Resolve MCP Server - Project Properties Utilities
4
+
5
+ This module provides functions for working with DaVinci Resolve project properties:
6
+ - Getting and setting project settings
7
+ - Managing project metadata
8
+ - Handling project-specific configurations
9
+ """
10
+
11
+ import os
12
+ import logging
13
+ import json
14
+ from typing import Dict, List, Any, Optional, Union
15
+
16
+ # Configure logging
17
+ logger = logging.getLogger("davinci-resolve-mcp.project_properties")
18
+
19
+ # Common project properties with their types
20
+ PROJECT_PROPERTY_TYPES = {
21
+ # Timeline settings
22
+ "timelineFrameRate": "float",
23
+ "timelineResolutionWidth": "int",
24
+ "timelineResolutionHeight": "int",
25
+ "timelineOutputResolutionWidth": "int",
26
+ "timelineOutputResolutionHeight": "int",
27
+ "timelineInterlaceProcessing": "int",
28
+
29
+ # Color settings
30
+ "colorScienceMode": "int",
31
+ "timelineColorSpace": "string",
32
+ "timelineGamma": "string",
33
+ "inputDRT": "string",
34
+ "outputDRT": "string",
35
+
36
+ # Image processing
37
+ "superScaleEnabled": "bool",
38
+ "superScaleQuality": "int",
39
+ "noiseReductionEnabled": "bool",
40
+ "noiseReductionMode": "int",
41
+ "noiseReductionValue": "float",
42
+
43
+ # Format settings
44
+ "timelineAudioSampleRate": "int",
45
+ "timelineAudioBitDepth": "int",
46
+ "mediaPoolRelativePath": "bool",
47
+
48
+ # Cache settings
49
+ "CacheMode": "int",
50
+ "CacheClipMode": "int",
51
+ "OptimizedMediaMode": "int",
52
+ "ProxyMode": "int",
53
+ "ProxyQuality": "int",
54
+ "TimelineCacheMode": "int",
55
+ }
56
+
57
+ def get_all_project_properties(project_obj) -> Dict[str, Any]:
58
+ """
59
+ Get all project properties and their values.
60
+
61
+ Args:
62
+ project_obj: Project object from DaVinci Resolve API
63
+
64
+ Returns:
65
+ Dictionary of all available project properties and their values
66
+ """
67
+ if project_obj is None:
68
+ return {"error": "Invalid project object"}
69
+
70
+ try:
71
+ # Get all settings using empty string as key
72
+ all_settings = project_obj.GetSetting('')
73
+
74
+ # Check if we got a valid response
75
+ if all_settings is None or not isinstance(all_settings, dict):
76
+ logger.warning("GetSetting('') did not return expected dictionary")
77
+
78
+ # Fall back to getting known properties individually
79
+ properties = {}
80
+ for prop_name in PROJECT_PROPERTY_TYPES.keys():
81
+ try:
82
+ value = project_obj.GetSetting(prop_name)
83
+ properties[prop_name] = value
84
+ except Exception as e:
85
+ logger.debug(f"Error getting property {prop_name}: {str(e)}")
86
+
87
+ return properties
88
+ else:
89
+ # Return all settings
90
+ return all_settings
91
+
92
+ except Exception as e:
93
+ logger.error(f"Error getting project properties: {str(e)}")
94
+ return {"error": f"Error getting project properties: {str(e)}"}
95
+
96
+ def get_project_property(project_obj, property_name: str) -> Any:
97
+ """
98
+ Get a specific project property value.
99
+
100
+ Args:
101
+ project_obj: Project object from DaVinci Resolve API
102
+ property_name: Name of the property to get
103
+
104
+ Returns:
105
+ Value of the specified property or error information
106
+ """
107
+ if project_obj is None:
108
+ return {"error": "Invalid project object"}
109
+
110
+ try:
111
+ # Get the specified property
112
+ value = project_obj.GetSetting(property_name)
113
+
114
+ # Properly convert the value based on expected type
115
+ if property_name in PROJECT_PROPERTY_TYPES:
116
+ property_type = PROJECT_PROPERTY_TYPES[property_name]
117
+
118
+ if property_type == "int" and not isinstance(value, int):
119
+ try:
120
+ value = int(value)
121
+ except (ValueError, TypeError):
122
+ # Keep Resolve's raw value when it cannot be safely coerced.
123
+ pass
124
+ elif property_type == "float" and not isinstance(value, float):
125
+ try:
126
+ value = float(value)
127
+ except (ValueError, TypeError):
128
+ # Keep Resolve's raw value when it cannot be safely coerced.
129
+ pass
130
+ elif property_type == "bool" and not isinstance(value, bool):
131
+ # Convert string representations of boolean
132
+ if isinstance(value, str):
133
+ value = value.lower() in ("true", "yes", "1", "on")
134
+
135
+ return value
136
+ except Exception as e:
137
+ logger.error(f"Error getting project property {property_name}: {str(e)}")
138
+ return {"error": f"Error getting project property: {str(e)}"}
139
+
140
+ def set_project_property(project_obj, property_name: str, property_value: Any) -> bool:
141
+ """
142
+ Set a project property value.
143
+
144
+ Args:
145
+ project_obj: Project object from DaVinci Resolve API
146
+ property_name: Name of the property to set
147
+ property_value: Value to set for the property
148
+
149
+ Returns:
150
+ True if successful, False otherwise
151
+ """
152
+ if project_obj is None:
153
+ return False
154
+
155
+ try:
156
+ # Handle type conversion based on expected property type
157
+ if property_name in PROJECT_PROPERTY_TYPES:
158
+ property_type = PROJECT_PROPERTY_TYPES[property_name]
159
+
160
+ if property_type == "int":
161
+ try:
162
+ property_value = int(property_value)
163
+ except (ValueError, TypeError):
164
+ logger.warning(f"Invalid integer value for property {property_name}: {property_value}")
165
+
166
+ elif property_type == "float":
167
+ try:
168
+ property_value = float(property_value)
169
+ except (ValueError, TypeError):
170
+ logger.warning(f"Invalid float value for property {property_name}: {property_value}")
171
+
172
+ elif property_type == "bool":
173
+ if isinstance(property_value, str):
174
+ property_value = property_value.lower() in ("true", "yes", "1", "on")
175
+ property_value = bool(property_value)
176
+
177
+ # Set the property
178
+ return project_obj.SetSetting(property_name, property_value)
179
+
180
+ except Exception as e:
181
+ logger.error(f"Error setting project property {property_name}: {str(e)}")
182
+ return False
183
+
184
+ def get_timeline_format_settings(project_obj) -> Dict[str, Any]:
185
+ """
186
+ Get timeline format settings for the project.
187
+
188
+ Args:
189
+ project_obj: Project object from DaVinci Resolve API
190
+
191
+ Returns:
192
+ Dictionary of timeline format settings
193
+ """
194
+ if project_obj is None:
195
+ return {"error": "Invalid project object"}
196
+
197
+ try:
198
+ # Get relevant timeline format settings
199
+ settings = {}
200
+ format_properties = [
201
+ "timelineFrameRate",
202
+ "timelineResolutionWidth",
203
+ "timelineResolutionHeight",
204
+ "timelineOutputResolutionWidth",
205
+ "timelineOutputResolutionHeight",
206
+ "timelineInterlaceProcessing"
207
+ ]
208
+
209
+ for prop in format_properties:
210
+ settings[prop] = get_project_property(project_obj, prop)
211
+
212
+ # Add frame rate details
213
+ if "timelineFrameRate" in settings:
214
+ fps = settings["timelineFrameRate"]
215
+
216
+ # Check if it's a drop frame rate
217
+ is_drop_frame = False
218
+ if isinstance(fps, (int, float)):
219
+ # Common drop frame rates: 29.97, 59.94
220
+ is_drop_frame = abs(fps - 29.97) < 0.01 or abs(fps - 59.94) < 0.01
221
+
222
+ settings["isDropFrame"] = is_drop_frame
223
+
224
+ # Add resolution name if standard
225
+ if "timelineResolutionWidth" in settings and "timelineResolutionHeight" in settings:
226
+ width = settings["timelineResolutionWidth"]
227
+ height = settings["timelineResolutionHeight"]
228
+
229
+ # Determine common resolution names
230
+ resolution_name = None
231
+ if width == 3840 and height == 2160:
232
+ resolution_name = "UHD 4K"
233
+ elif width == 1920 and height == 1080:
234
+ resolution_name = "FHD 1080p"
235
+ elif width == 1280 and height == 720:
236
+ resolution_name = "HD 720p"
237
+ elif width == 4096 and height in [2160, 2304]:
238
+ resolution_name = "DCI 4K"
239
+ elif width == 2048 and height in [1080, 1152]:
240
+ resolution_name = "DCI 2K"
241
+
242
+ if resolution_name:
243
+ settings["resolutionName"] = resolution_name
244
+
245
+ return settings
246
+
247
+ except Exception as e:
248
+ logger.error(f"Error getting timeline format settings: {str(e)}")
249
+ return {"error": f"Error getting timeline format settings: {str(e)}"}
250
+
251
+ def set_timeline_format(project_obj, width: int, height: int, frame_rate: float,
252
+ interlaced: bool = False) -> bool:
253
+ """
254
+ Set timeline format (resolution and frame rate).
255
+
256
+ Args:
257
+ project_obj: Project object from DaVinci Resolve API
258
+ width: Timeline width in pixels
259
+ height: Timeline height in pixels
260
+ frame_rate: Timeline frame rate
261
+ interlaced: Whether the timeline should use interlaced processing
262
+
263
+ Returns:
264
+ True if successful, False otherwise
265
+ """
266
+ if project_obj is None:
267
+ return False
268
+
269
+ try:
270
+ # Set timeline format properties
271
+ success = True
272
+
273
+ # Set resolution
274
+ if not set_project_property(project_obj, "timelineResolutionWidth", width):
275
+ success = False
276
+
277
+ if not set_project_property(project_obj, "timelineResolutionHeight", height):
278
+ success = False
279
+
280
+ # Set frame rate
281
+ if not set_project_property(project_obj, "timelineFrameRate", frame_rate):
282
+ success = False
283
+
284
+ # Set interlaced processing
285
+ interlace_value = 1 if interlaced else 0
286
+ if not set_project_property(project_obj, "timelineInterlaceProcessing", interlace_value):
287
+ success = False
288
+
289
+ return success
290
+
291
+ except Exception as e:
292
+ logger.error(f"Error setting timeline format: {str(e)}")
293
+ return False
294
+
295
+ def get_superscale_settings(project_obj) -> Dict[str, Any]:
296
+ """
297
+ Get SuperScale settings for the project.
298
+
299
+ Args:
300
+ project_obj: Project object from DaVinci Resolve API
301
+
302
+ Returns:
303
+ Dictionary of SuperScale settings
304
+ """
305
+ if project_obj is None:
306
+ return {"error": "Invalid project object"}
307
+
308
+ try:
309
+ # Get SuperScale settings
310
+ settings = {}
311
+
312
+ # Check if SuperScale is enabled
313
+ superscale_enabled = get_project_property(project_obj, "superScaleEnabled")
314
+ settings["enabled"] = bool(superscale_enabled)
315
+
316
+ # Get quality setting
317
+ quality = get_project_property(project_obj, "superScaleQuality")
318
+ settings["quality"] = quality
319
+
320
+ # Translate quality number to descriptive name
321
+ quality_names = {
322
+ 0: "Auto",
323
+ 1: "Better Quality", # Sharper but might have artifacts
324
+ 2: "Smoother", # Less sharp but fewer artifacts
325
+ }
326
+
327
+ if quality in quality_names:
328
+ settings["qualityName"] = quality_names[quality]
329
+
330
+ # Add additional SuperScale properties if available
331
+ for prop in ["superScaleOverrideWidth", "superScaleOverrideHeight"]:
332
+ value = get_project_property(project_obj, prop)
333
+ if value is not None:
334
+ settings[prop] = value
335
+
336
+ return settings
337
+
338
+ except Exception as e:
339
+ logger.error(f"Error getting SuperScale settings: {str(e)}")
340
+ return {"error": f"Error getting SuperScale settings: {str(e)}"}
341
+
342
+ def set_superscale_settings(project_obj, enabled: bool, quality: int = 0) -> bool:
343
+ """
344
+ Set SuperScale settings for the project.
345
+
346
+ Args:
347
+ project_obj: Project object from DaVinci Resolve API
348
+ enabled: Whether SuperScale is enabled
349
+ quality: SuperScale quality (0=Auto, 1=Better Quality, 2=Smoother)
350
+
351
+ Returns:
352
+ True if successful, False otherwise
353
+ """
354
+ if project_obj is None:
355
+ return False
356
+
357
+ try:
358
+ # Validate quality value
359
+ if quality not in [0, 1, 2]:
360
+ logger.warning(f"Invalid SuperScale quality value: {quality}. Using 0 (Auto)")
361
+ quality = 0
362
+
363
+ # Set SuperScale properties
364
+ success = True
365
+
366
+ # Set enabled state
367
+ if not set_project_property(project_obj, "superScaleEnabled", enabled):
368
+ success = False
369
+
370
+ # Set quality
371
+ if not set_project_property(project_obj, "superScaleQuality", quality):
372
+ success = False
373
+
374
+ return success
375
+
376
+ except Exception as e:
377
+ logger.error(f"Error setting SuperScale settings: {str(e)}")
378
+ return False
379
+
380
+ def get_color_settings(project_obj) -> Dict[str, Any]:
381
+ """
382
+ Get color science and color space settings for the project.
383
+
384
+ Args:
385
+ project_obj: Project object from DaVinci Resolve API
386
+
387
+ Returns:
388
+ Dictionary of color settings
389
+ """
390
+ if project_obj is None:
391
+ return {"error": "Invalid project object"}
392
+
393
+ try:
394
+ # Get color-related settings
395
+ settings = {}
396
+ color_properties = [
397
+ "colorScienceMode",
398
+ "timelineColorSpace",
399
+ "timelineGamma",
400
+ "inputDRT",
401
+ "outputDRT"
402
+ ]
403
+
404
+ for prop in color_properties:
405
+ value = get_project_property(project_obj, prop)
406
+ if value is not None:
407
+ settings[prop] = value
408
+
409
+ # Translate colorScienceMode to descriptive name
410
+ if "colorScienceMode" in settings:
411
+ mode = settings["colorScienceMode"]
412
+ mode_names = {
413
+ 0: "DaVinci YRGB",
414
+ 1: "DaVinci YRGB Color Managed",
415
+ 2: "ACEScct"
416
+ }
417
+
418
+ if mode in mode_names:
419
+ settings["colorScienceName"] = mode_names[mode]
420
+
421
+ return settings
422
+
423
+ except Exception as e:
424
+ logger.error(f"Error getting color settings: {str(e)}")
425
+ return {"error": f"Error getting color settings: {str(e)}"}
426
+
427
+ def set_color_science_mode(project_obj, mode: str) -> bool:
428
+ """
429
+ Set color science mode for the project.
430
+
431
+ Args:
432
+ project_obj: Project object from DaVinci Resolve API
433
+ mode: Color science mode ('YRGB', 'YRGB Color Managed', 'ACEScct', or numeric value)
434
+
435
+ Returns:
436
+ True if successful, False otherwise
437
+ """
438
+ if project_obj is None:
439
+ return False
440
+
441
+ try:
442
+ # Map string modes to numeric values
443
+ mode_values = {
444
+ "YRGB": 0,
445
+ "DaVinci YRGB": 0,
446
+ "YRGB Color Managed": 1,
447
+ "DaVinci YRGB Color Managed": 1,
448
+ "ACEScct": 2,
449
+ "ACES": 2
450
+ }
451
+
452
+ # Get numeric value
453
+ mode_value = None
454
+
455
+ if isinstance(mode, int) and 0 <= mode <= 2:
456
+ mode_value = mode
457
+ elif isinstance(mode, str):
458
+ mode_value = mode_values.get(mode)
459
+
460
+ if mode_value is None:
461
+ logger.error(f"Invalid color science mode: {mode}")
462
+ return False
463
+
464
+ # Set the color science mode
465
+ return set_project_property(project_obj, "colorScienceMode", mode_value)
466
+
467
+ except Exception as e:
468
+ logger.error(f"Error setting color science mode: {str(e)}")
469
+ return False
470
+
471
+ def set_color_space(project_obj, color_space: str, gamma: str = None) -> bool:
472
+ """
473
+ Set timeline color space and gamma.
474
+
475
+ Args:
476
+ project_obj: Project object from DaVinci Resolve API
477
+ color_space: Timeline color space (e.g., 'Rec.709', 'DCI-P3 D65', 'Rec.2020')
478
+ gamma: Timeline gamma (e.g., 'Rec.709 Gamma', 'Gamma 2.4')
479
+
480
+ Returns:
481
+ True if successful, False otherwise
482
+ """
483
+ if project_obj is None:
484
+ return False
485
+
486
+ try:
487
+ success = True
488
+
489
+ # Set timeline color space
490
+ if not set_project_property(project_obj, "timelineColorSpace", color_space):
491
+ success = False
492
+
493
+ # Set gamma if provided
494
+ if gamma is not None:
495
+ if not set_project_property(project_obj, "timelineGamma", gamma):
496
+ success = False
497
+
498
+ return success
499
+
500
+ except Exception as e:
501
+ logger.error(f"Error setting color space: {str(e)}")
502
+ return False
503
+
504
+ def get_project_metadata(project_obj) -> Dict[str, Any]:
505
+ """
506
+ Get project metadata.
507
+
508
+ Args:
509
+ project_obj: Project object from DaVinci Resolve API
510
+
511
+ Returns:
512
+ Dictionary of project metadata
513
+ """
514
+ if project_obj is None:
515
+ return {"error": "Invalid project object"}
516
+
517
+ try:
518
+ metadata = {}
519
+
520
+ # Add basic project info
521
+ metadata["name"] = project_obj.GetName()
522
+
523
+ # Add project path if available
524
+ if hasattr(project_obj, "GetPath"):
525
+ metadata["path"] = project_obj.GetPath()
526
+
527
+ # Get current timeline
528
+ current_timeline = project_obj.GetCurrentTimeline()
529
+ if current_timeline:
530
+ metadata["currentTimeline"] = current_timeline.GetName()
531
+
532
+ # Add timeline count
533
+ timeline_count = project_obj.GetTimelineCount()
534
+ metadata["timelineCount"] = timeline_count
535
+
536
+ # Add frame rate and resolution
537
+ format_settings = get_timeline_format_settings(project_obj)
538
+ if "error" not in format_settings:
539
+ metadata.update(format_settings)
540
+
541
+ # Add color settings
542
+ color_settings = get_color_settings(project_obj)
543
+ if "error" not in color_settings:
544
+ metadata["colorSettings"] = color_settings
545
+
546
+ # Add SuperScale settings
547
+ superscale_settings = get_superscale_settings(project_obj)
548
+ if "error" not in superscale_settings:
549
+ metadata["superScale"] = superscale_settings
550
+
551
+ return metadata
552
+
553
+ except Exception as e:
554
+ logger.error(f"Error getting project metadata: {str(e)}")
555
+ return {"error": f"Error getting project metadata: {str(e)}"}
556
+
557
+ def get_project_info(project_obj) -> Dict[str, Any]:
558
+ """
559
+ Get comprehensive project information including settings and metadata.
560
+
561
+ Args:
562
+ project_obj: Project object from DaVinci Resolve API
563
+
564
+ Returns:
565
+ Dictionary with project information
566
+ """
567
+ if project_obj is None:
568
+ return {"error": "Invalid project object"}
569
+
570
+ try:
571
+ # Get project name
572
+ project_name = project_obj.GetName()
573
+
574
+ # Combine all project information
575
+ project_info = {
576
+ "name": project_name,
577
+ "metadata": get_project_metadata(project_obj),
578
+ "settings": get_all_project_properties(project_obj),
579
+ "timelines": []
580
+ }
581
+
582
+ # Add timeline information
583
+ timeline_count = project_obj.GetTimelineCount()
584
+ current_timeline = project_obj.GetCurrentTimeline()
585
+ current_timeline_name = current_timeline.GetName() if current_timeline else None
586
+
587
+ for i in range(1, timeline_count + 1):
588
+ timeline = project_obj.GetTimelineByIndex(i)
589
+ if timeline:
590
+ timeline_info = {
591
+ "name": timeline.GetName(),
592
+ "isCurrent": timeline.GetName() == current_timeline_name,
593
+ "duration": timeline.GetEndFrame() - timeline.GetStartFrame() + 1
594
+ }
595
+ project_info["timelines"].append(timeline_info)
596
+
597
+ return project_info
598
+
599
+ except Exception as e:
600
+ logger.error(f"Error getting project info: {str(e)}")
601
+ return {"error": f"Error getting project info: {str(e)}"}