davinci-resolve-mcp 4.8.19 → 4.8.21
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.
- package/CHANGELOG.md +77 -0
- package/README.md +1 -1
- package/README.zh-CN.md +2 -2
- package/install.py +1 -1
- package/package.json +1 -1
- package/src/granular/common.py +1 -1
- package/src/granular/timeline.py +1 -1
- package/src/server.py +157 -157
- package/src/utils/api_truth.py +24 -0
- package/src/utils/dctl_templates.py +4 -2
- package/src/utils/fuse_templates.py +4 -2
- package/src/utils/media_analysis_jobs.py +4 -2
- package/src/utils/multicam.py +5 -3
- package/src/utils/project_properties.py +1 -1
package/src/server.py
CHANGED
|
@@ -11,7 +11,7 @@ Usage:
|
|
|
11
11
|
python src/server.py --full # Start the 377-tool granular server instead
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
|
-
VERSION = "4.8.
|
|
14
|
+
VERSION = "4.8.21"
|
|
15
15
|
|
|
16
16
|
import base64
|
|
17
17
|
import os
|
|
@@ -1276,7 +1276,7 @@ def _ai_governance_gate(op: str, p: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
|
1276
1276
|
"""
|
|
1277
1277
|
if _ai_governance_mode() != "enforce":
|
|
1278
1278
|
return None
|
|
1279
|
-
if p.get("override_governance") or p.get("overrideGovernance"):
|
|
1279
|
+
if _coerce_bool(p.get("override_governance")) or _coerce_bool(p.get("overrideGovernance")):
|
|
1280
1280
|
return None
|
|
1281
1281
|
check = _ai_governance_check(op)
|
|
1282
1282
|
if not check.get("applies") or not check.get("exceeded"):
|
|
@@ -2474,7 +2474,7 @@ def _annotation_target(scope: str, p: Dict[str, Any], tl=None):
|
|
|
2474
2474
|
return None, err
|
|
2475
2475
|
return tl, None
|
|
2476
2476
|
if scope == "timeline_item":
|
|
2477
|
-
if p.get("current"):
|
|
2477
|
+
if _coerce_bool(p.get("current")):
|
|
2478
2478
|
if tl is None:
|
|
2479
2479
|
_, tl, err = _get_tl()
|
|
2480
2480
|
if err:
|
|
@@ -2555,7 +2555,7 @@ def _probe_annotations(tl, p: Dict[str, Any]):
|
|
|
2555
2555
|
|
|
2556
2556
|
|
|
2557
2557
|
def _normalize_marker_payload_action(tl, p: Dict[str, Any]):
|
|
2558
|
-
marker, err = _marker_add_payload(p, tl=tl, default_to_current=
|
|
2558
|
+
marker, err = _marker_add_payload(p, tl=tl, default_to_current=_coerce_bool(p.get("default_to_current"), False))
|
|
2559
2559
|
if err:
|
|
2560
2560
|
return err
|
|
2561
2561
|
return {"marker": marker}
|
|
@@ -2630,13 +2630,13 @@ def _clear_annotations_by_scope(tl, p: Dict[str, Any]):
|
|
|
2630
2630
|
if not _has_method(target, "DeleteMarkerByCustomData"):
|
|
2631
2631
|
return _err(f"{scope} does not expose DeleteMarkerByCustomData")
|
|
2632
2632
|
return {"success": bool(target.DeleteMarkerByCustomData(_first_param(p, "custom_data", "customData", default="")))}
|
|
2633
|
-
color = p.get("color", "All" if p.get("all", True) else "Blue")
|
|
2633
|
+
color = p.get("color", "All" if _coerce_bool(p.get("all"), True) else "Blue")
|
|
2634
2634
|
if not _has_method(target, "DeleteMarkersByColor"):
|
|
2635
2635
|
return _err(f"{scope} does not expose DeleteMarkersByColor")
|
|
2636
2636
|
result = {"success": bool(target.DeleteMarkersByColor(color)), "color": color}
|
|
2637
|
-
if p.get("clear_flags") and _has_method(target, "ClearFlags"):
|
|
2637
|
+
if _coerce_bool(p.get("clear_flags")) and _has_method(target, "ClearFlags"):
|
|
2638
2638
|
result["flags_cleared"] = bool(target.ClearFlags(p.get("flag_color", "All")))
|
|
2639
|
-
if p.get("clear_clip_color") and _has_method(target, "ClearClipColor"):
|
|
2639
|
+
if _coerce_bool(p.get("clear_clip_color")) and _has_method(target, "ClearClipColor"):
|
|
2640
2640
|
result["clip_color_cleared"] = bool(target.ClearClipColor())
|
|
2641
2641
|
return result
|
|
2642
2642
|
|
|
@@ -2647,7 +2647,7 @@ def _export_review_report(tl, p: Dict[str, Any]):
|
|
|
2647
2647
|
"generated_at": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
|
|
2648
2648
|
"annotations": _probe_annotations(tl, p),
|
|
2649
2649
|
}
|
|
2650
|
-
if p.get("include_capabilities", True):
|
|
2650
|
+
if _coerce_bool(p.get("include_capabilities"), True):
|
|
2651
2651
|
report["capabilities"] = _annotation_capabilities()
|
|
2652
2652
|
return report
|
|
2653
2653
|
|
|
@@ -4859,7 +4859,7 @@ def _append_and_recover_timeline_item(
|
|
|
4859
4859
|
|
|
4860
4860
|
def _timeline_duplicate_clips_impl(proj, tl, p: Dict[str, Any], *, delete_sources: bool = False, resolve=None):
|
|
4861
4861
|
ids = p.get("clip_ids") or p.get("ids")
|
|
4862
|
-
selected =
|
|
4862
|
+
selected = _coerce_bool(p.get("selected"), False)
|
|
4863
4863
|
if ids is not None and not isinstance(ids, list):
|
|
4864
4864
|
return _err("duplicate_clips requires clip_ids (list of timeline item unique IDs)")
|
|
4865
4865
|
if not ids and not selected:
|
|
@@ -4872,7 +4872,7 @@ def _timeline_duplicate_clips_impl(proj, tl, p: Dict[str, Any], *, delete_source
|
|
|
4872
4872
|
copy_properties, copy_err = _normalize_copy_properties(p.get("copy_properties", p.get("copyProperties")))
|
|
4873
4873
|
if copy_err:
|
|
4874
4874
|
return copy_err
|
|
4875
|
-
if p.get("copy_keyframes", p.get("copyKeyframes", False)
|
|
4875
|
+
if _coerce_bool(p.get("copy_keyframes", p.get("copyKeyframes")), False) and "keyframes" not in copy_properties:
|
|
4876
4876
|
copy_properties.append("keyframes")
|
|
4877
4877
|
try:
|
|
4878
4878
|
offset = int(p.get("record_frame_offset", p.get("recordFrameOffset", 0)))
|
|
@@ -4906,7 +4906,7 @@ def _timeline_duplicate_clips_impl(proj, tl, p: Dict[str, Any], *, delete_source
|
|
|
4906
4906
|
seen_ids.add(sid)
|
|
4907
4907
|
|
|
4908
4908
|
include_types = _normalize_include_linked(p.get("include_linked", p.get("includeLinked")))
|
|
4909
|
-
relink =
|
|
4909
|
+
relink = _coerce_bool(p.get("relink", p.get("restore_linked", p.get("restoreLinked"))), bool(include_types))
|
|
4910
4910
|
results: List[Dict[str, Any]] = []
|
|
4911
4911
|
source_delete_items = []
|
|
4912
4912
|
|
|
@@ -5518,7 +5518,7 @@ def _timeline_ripple_insert_impl(proj, tl, p: Dict[str, Any], *, resolve=None) -
|
|
|
5518
5518
|
|
|
5519
5519
|
|
|
5520
5520
|
def _range_frames_from_params(tl, p: Dict[str, Any]):
|
|
5521
|
-
if p.get("use_mark_in_out", p.get("useMarkInOut", False)
|
|
5521
|
+
if _coerce_bool(p.get("use_mark_in_out", p.get("useMarkInOut")), False):
|
|
5522
5522
|
mark = tl.GetMarkInOut() or {}
|
|
5523
5523
|
mark_type = p.get("mark_type", p.get("markType", "video"))
|
|
5524
5524
|
if mark_type not in mark:
|
|
@@ -5600,7 +5600,7 @@ def _timeline_copy_range_impl(proj, tl, p: Dict[str, Any], *, overwrite: bool =
|
|
|
5600
5600
|
copy_properties, copy_err = _normalize_copy_properties(p.get("copy_properties", p.get("copyProperties")))
|
|
5601
5601
|
if copy_err:
|
|
5602
5602
|
return copy_err
|
|
5603
|
-
if p.get("copy_keyframes", p.get("copyKeyframes", False)
|
|
5603
|
+
if _coerce_bool(p.get("copy_keyframes", p.get("copyKeyframes")), False) and "keyframes" not in copy_properties:
|
|
5604
5604
|
copy_properties.append("keyframes")
|
|
5605
5605
|
mp = proj.GetMediaPool()
|
|
5606
5606
|
if not mp:
|
|
@@ -5863,7 +5863,7 @@ def _timeline_item_probe(item):
|
|
|
5863
5863
|
|
|
5864
5864
|
def _timeline_probe_edit_kernel_item(tl, p: Dict[str, Any]):
|
|
5865
5865
|
ids = p.get("clip_ids") or p.get("ids")
|
|
5866
|
-
selected =
|
|
5866
|
+
selected = _coerce_bool(p.get("selected"), False)
|
|
5867
5867
|
items = []
|
|
5868
5868
|
if ids:
|
|
5869
5869
|
if not isinstance(ids, list):
|
|
@@ -6010,10 +6010,10 @@ def _timeline_set_title_text(tl, p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
6010
6010
|
return _err("set_title_text requires params.text (string)")
|
|
6011
6011
|
|
|
6012
6012
|
property_key = p.get("property_key") or p.get("key")
|
|
6013
|
-
as_styled_xml =
|
|
6014
|
-
try_plain_first =
|
|
6015
|
-
readback =
|
|
6016
|
-
try_heuristic_keys =
|
|
6013
|
+
as_styled_xml = _coerce_bool(p.get("as_styled_xml", p.get("styled")), False)
|
|
6014
|
+
try_plain_first = _coerce_bool(p.get("try_plain_first"), True)
|
|
6015
|
+
readback = _coerce_bool(p.get("readback"), False)
|
|
6016
|
+
try_heuristic_keys = _coerce_bool(p.get("try_heuristic_keys"), not bool(property_key))
|
|
6017
6017
|
|
|
6018
6018
|
if as_styled_xml:
|
|
6019
6019
|
payload_modes = [(text, "as_given")]
|
|
@@ -6069,7 +6069,7 @@ def _timeline_set_title_text(tl, p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
6069
6069
|
# route get_title_text already reads. Deliberately a bare, UNLOCKED
|
|
6070
6070
|
# SetInput: the comp-lock render bug (api_truth / v2.98.5) eats writes
|
|
6071
6071
|
# wrapped in Comp.Lock(), and unlocked writes are the safe ones.
|
|
6072
|
-
if not
|
|
6072
|
+
if not _coerce_bool(p.get("as_styled_xml", p.get("styled")), False):
|
|
6073
6073
|
try:
|
|
6074
6074
|
if int(item.GetFusionCompCount() or 0) > 0:
|
|
6075
6075
|
comp = item.GetFusionCompByIndex(1)
|
|
@@ -6349,7 +6349,7 @@ def _detect_gaps_overlaps_from_snapshot(snapshot: Dict[str, Any], p: Optional[Di
|
|
|
6349
6349
|
def _source_ranges_from_snapshot(snapshot: Dict[str, Any], p: Optional[Dict[str, Any]] = None):
|
|
6350
6350
|
p = p or {}
|
|
6351
6351
|
handles = int(p.get("handles", 0))
|
|
6352
|
-
merge =
|
|
6352
|
+
merge = _coerce_bool(p.get("merge"), True)
|
|
6353
6353
|
ranges: Dict[str, List[List[int]]] = {}
|
|
6354
6354
|
occurrences = []
|
|
6355
6355
|
for track_type, type_payload in (snapshot.get("tracks") or {}).items():
|
|
@@ -6508,7 +6508,7 @@ def _timeline_bulk_set_item_properties(tl, p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
6508
6508
|
if not isinstance(ops, list) or not ops:
|
|
6509
6509
|
return _err("bulk_set_item_properties requires params.ops: non-empty list of objects")
|
|
6510
6510
|
dry_run = _coerce_bool(p.get("dry_run"), False)
|
|
6511
|
-
readback =
|
|
6511
|
+
readback = _coerce_bool(p.get("readback"), False)
|
|
6512
6512
|
results = []
|
|
6513
6513
|
for index, op in enumerate(ops):
|
|
6514
6514
|
if not isinstance(op, dict):
|
|
@@ -6668,7 +6668,7 @@ def _timeline_apply_look_to_items(tl, p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
6668
6668
|
out["copy_grades"] = False
|
|
6669
6669
|
out["copy_grades_error"] = str(exc)
|
|
6670
6670
|
out["success"] = (
|
|
6671
|
-
all(row.get("set_cdl", True) for row in results)
|
|
6671
|
+
all(_coerce_bool(row.get("set_cdl"), True) for row in results)
|
|
6672
6672
|
and (source_item is None or bool(out.get("copy_grades")))
|
|
6673
6673
|
)
|
|
6674
6674
|
return out
|
|
@@ -6812,7 +6812,7 @@ def _timeline_create_variant_from_ranges(proj, source_tl, p: Dict[str, Any]) ->
|
|
|
6812
6812
|
# pack=True butts clips together at the end of each track (record_frame is
|
|
6813
6813
|
# ignored); Resolve packs by actual placed duration, so the result is gap-free
|
|
6814
6814
|
# even when source and timeline frame rates differ.
|
|
6815
|
-
pack =
|
|
6815
|
+
pack = _coerce_bool(p.get("pack"), False)
|
|
6816
6816
|
built = []
|
|
6817
6817
|
cursor_by_track: Dict[Tuple[int, int], int] = {}
|
|
6818
6818
|
max_tracks = {"video": 1, "audio": 1}
|
|
@@ -7321,7 +7321,7 @@ def _export_timeline_checked(tl, p: Dict[str, Any]):
|
|
|
7321
7321
|
path = p.get("path")
|
|
7322
7322
|
if not path:
|
|
7323
7323
|
return _err("path is required")
|
|
7324
|
-
if p.get("require_temp_path", True) and not _render_temp_path_ok(path):
|
|
7324
|
+
if _coerce_bool(p.get("require_temp_path"), True) and not _render_temp_path_ok(path):
|
|
7325
7325
|
return _err("path must be under the system temp directory unless require_temp_path=False")
|
|
7326
7326
|
folder = os.path.dirname(os.path.abspath(path))
|
|
7327
7327
|
if folder:
|
|
@@ -7702,7 +7702,7 @@ def _import_timeline_checked(proj, mp, p: Dict[str, Any]):
|
|
|
7702
7702
|
"the media pool afterward (see `relink`)."
|
|
7703
7703
|
)
|
|
7704
7704
|
sanitize = sanitize_requested and not is_binary and not is_json
|
|
7705
|
-
if not sanitize and p.get("require_temp_path", True) and not _render_temp_path_ok(path):
|
|
7705
|
+
if not sanitize and _coerce_bool(p.get("require_temp_path"), True) and not _render_temp_path_ok(path):
|
|
7706
7706
|
return _err(
|
|
7707
7707
|
"path must be under the system temp directory unless require_temp_path=False",
|
|
7708
7708
|
remediation="Pass require_temp_path=False to import from this location, or "
|
|
@@ -7731,7 +7731,7 @@ def _import_timeline_checked(proj, mp, p: Dict[str, Any]):
|
|
|
7731
7731
|
san_kwargs["search_roots"] = list(search_roots)
|
|
7732
7732
|
if p.get("relink_min_confidence") is not None:
|
|
7733
7733
|
san_kwargs["min_confidence"] = float(p["relink_min_confidence"])
|
|
7734
|
-
if p.get("verify_visually") or p.get("reference_movie"):
|
|
7734
|
+
if _coerce_bool(p.get("verify_visually")) or p.get("reference_movie"):
|
|
7735
7735
|
san_kwargs["verify_visually"] = True
|
|
7736
7736
|
if p.get("reference_movie"):
|
|
7737
7737
|
san_kwargs["reference_movie"] = p["reference_movie"]
|
|
@@ -8376,14 +8376,14 @@ def _compare_timelines(proj, tl, p: Dict[str, Any]):
|
|
|
8376
8376
|
|
|
8377
8377
|
def _probe_interchange_roundtrip(proj, mp, tl, p: Dict[str, Any]):
|
|
8378
8378
|
output_dir = p.get("output_dir") or tempfile.mkdtemp(prefix="mcp_conform_roundtrip_")
|
|
8379
|
-
if p.get("require_temp_path", True) and not _render_temp_path_ok(output_dir):
|
|
8379
|
+
if _coerce_bool(p.get("require_temp_path"), True) and not _render_temp_path_ok(output_dir):
|
|
8380
8380
|
return _err("output_dir must be under the system temp directory unless require_temp_path=False")
|
|
8381
8381
|
os.makedirs(output_dir, exist_ok=True)
|
|
8382
8382
|
spec = _timeline_export_spec(p, resolve)
|
|
8383
8383
|
base_name = p.get("name") or f"roundtrip_{str(spec['requested']).lower()}"
|
|
8384
8384
|
path = p.get("path") or os.path.join(output_dir, base_name + spec["extension"])
|
|
8385
8385
|
# background is a top-level option; a sub-step export must run synchronously.
|
|
8386
|
-
export_result = _export_timeline_checked(tl, {**p, "path": path, "require_temp_path": p.get("require_temp_path", True), "background": False, "async_job": False})
|
|
8386
|
+
export_result = _export_timeline_checked(tl, {**p, "path": path, "require_temp_path": _coerce_bool(p.get("require_temp_path"), True), "background": False, "async_job": False})
|
|
8387
8387
|
if export_result.get("error") or not export_result.get("success"):
|
|
8388
8388
|
return {"success": False, "stage": "export", "export": export_result}
|
|
8389
8389
|
import_path = export_result.get("primary_file") or export_result.get("path") or path
|
|
@@ -8391,14 +8391,14 @@ def _probe_interchange_roundtrip(proj, mp, tl, p: Dict[str, Any]):
|
|
|
8391
8391
|
requested_key = str(spec["requested"]).lower()
|
|
8392
8392
|
if "drt" not in requested_key:
|
|
8393
8393
|
import_options.setdefault("timelineName", p.get("imported_timeline_name", f"{tl.GetName()} {spec['requested']} Roundtrip"))
|
|
8394
|
-
import_options.setdefault("importSourceClips",
|
|
8394
|
+
import_options.setdefault("importSourceClips", _coerce_bool(p.get("import_source_clips"), False))
|
|
8395
8395
|
import_result = _import_timeline_checked(
|
|
8396
8396
|
proj,
|
|
8397
8397
|
mp,
|
|
8398
8398
|
{
|
|
8399
8399
|
"path": import_path,
|
|
8400
8400
|
"options": import_options,
|
|
8401
|
-
"require_temp_path": p.get("require_temp_path", True),
|
|
8401
|
+
"require_temp_path": _coerce_bool(p.get("require_temp_path"), True),
|
|
8402
8402
|
},
|
|
8403
8403
|
)
|
|
8404
8404
|
if import_result.get("error") or not import_result.get("success"):
|
|
@@ -8410,7 +8410,7 @@ def _probe_interchange_roundtrip(proj, mp, tl, p: Dict[str, Any]):
|
|
|
8410
8410
|
if imported_tl:
|
|
8411
8411
|
comparison = _compare_timeline_snapshots(_timeline_conform_snapshot(tl, p), _timeline_conform_snapshot(imported_tl, p))
|
|
8412
8412
|
cleanup_result = None
|
|
8413
|
-
if p.get("cleanup_imported", True) and imported_tl:
|
|
8413
|
+
if _coerce_bool(p.get("cleanup_imported"), True) and imported_tl:
|
|
8414
8414
|
cleanup_result = {"success": bool(mp.DeleteTimelines([imported_tl]))}
|
|
8415
8415
|
return {
|
|
8416
8416
|
"success": True,
|
|
@@ -8605,13 +8605,13 @@ def _detect_missing_media_from_snapshot(snapshot: Dict[str, Any]):
|
|
|
8605
8605
|
def _detect_missing_media(tl, p: Dict[str, Any]):
|
|
8606
8606
|
snapshot = _timeline_conform_snapshot(tl, {**p, "include_clip_properties": True})
|
|
8607
8607
|
report = _detect_missing_media_from_snapshot(snapshot)
|
|
8608
|
-
if p.get("sanitize_paths") or p.get("sanitized"):
|
|
8608
|
+
if _coerce_bool(p.get("sanitize_paths")) or _coerce_bool(p.get("sanitized")):
|
|
8609
8609
|
report = dict(report)
|
|
8610
8610
|
report["missing"] = [
|
|
8611
8611
|
{
|
|
8612
8612
|
**row,
|
|
8613
8613
|
"file_path_sanitized": _sanitize_media_path(row.get("file_path")),
|
|
8614
|
-
"file_path": None if p.get("omit_raw_paths", True) else row.get("file_path"),
|
|
8614
|
+
"file_path": None if _coerce_bool(p.get("omit_raw_paths"), True) else row.get("file_path"),
|
|
8615
8615
|
}
|
|
8616
8616
|
for row in report.get("missing", [])
|
|
8617
8617
|
]
|
|
@@ -8630,7 +8630,7 @@ def _bounded_basename_matches(
|
|
|
8630
8630
|
max_files = max(1, int(p.get("max_files_scanned", p.get("maxFilesScanned", 50000)) or 50000))
|
|
8631
8631
|
max_depth_raw = p.get("max_depth", p.get("maxDepth"))
|
|
8632
8632
|
max_depth = int(max_depth_raw) if max_depth_raw is not None else None
|
|
8633
|
-
all_matches =
|
|
8633
|
+
all_matches = _coerce_bool(p.get("all_matches"), False)
|
|
8634
8634
|
matches: List[str] = []
|
|
8635
8635
|
files_scanned = 0
|
|
8636
8636
|
dirs_scanned = 0
|
|
@@ -8680,7 +8680,7 @@ def _build_relink_plan(tl, p: Dict[str, Any]):
|
|
|
8680
8680
|
return _err(f"search_roots must be existing directories: {invalid}")
|
|
8681
8681
|
missing_report = _detect_missing_media(tl, p)
|
|
8682
8682
|
diagnosis = missing_report.get("diagnosis") or _missing_media_diagnosis(missing_report.get("missing", []))
|
|
8683
|
-
if p.get("skip_search_when_volume_missing", True) and any(
|
|
8683
|
+
if _coerce_bool(p.get("skip_search_when_volume_missing"), True) and any(
|
|
8684
8684
|
not row.get("mounted") for row in diagnosis.get("missing_volumes", [])
|
|
8685
8685
|
):
|
|
8686
8686
|
return {
|
|
@@ -8713,7 +8713,7 @@ def _build_relink_plan(tl, p: Dict[str, Any]):
|
|
|
8713
8713
|
**row,
|
|
8714
8714
|
"wanted_basename": wanted,
|
|
8715
8715
|
"timeline_occurrence_count": len(rows),
|
|
8716
|
-
"candidate_paths": [] if p.get("sanitize_paths") or p.get("sanitized") else matches,
|
|
8716
|
+
"candidate_paths": [] if _coerce_bool(p.get("sanitize_paths")) or _coerce_bool(p.get("sanitized")) else matches,
|
|
8717
8717
|
"candidate_paths_sanitized": sanitized_matches,
|
|
8718
8718
|
"candidate_count": len(matches),
|
|
8719
8719
|
"scan": scan,
|
|
@@ -8722,7 +8722,7 @@ def _build_relink_plan(tl, p: Dict[str, Any]):
|
|
|
8722
8722
|
return {
|
|
8723
8723
|
"success": True,
|
|
8724
8724
|
"dry_run": True,
|
|
8725
|
-
"search_roots": [] if p.get("sanitize_paths") or p.get("sanitized") else search_roots,
|
|
8725
|
+
"search_roots": [] if _coerce_bool(p.get("sanitize_paths")) or _coerce_bool(p.get("sanitized")) else search_roots,
|
|
8726
8726
|
"search_roots_sanitized": [_sanitize_media_path(root, keep_filename=False) for root in search_roots],
|
|
8727
8727
|
"candidate_count": sum(1 for row in candidates if row["candidate_count"]),
|
|
8728
8728
|
"missing_count": missing_report.get("missing_count", 0),
|
|
@@ -8916,7 +8916,7 @@ def _safe_set_audio_properties(tl, p: Dict[str, Any]):
|
|
|
8916
8916
|
row["readback"] = item.GetProperty(key)
|
|
8917
8917
|
except Exception as exc:
|
|
8918
8918
|
row["readback_error"] = str(exc)
|
|
8919
|
-
if p.get("restore", True) and not isinstance(original.get(key), dict):
|
|
8919
|
+
if _coerce_bool(p.get("restore"), True) and not isinstance(original.get(key), dict):
|
|
8920
8920
|
try:
|
|
8921
8921
|
row["restore"] = bool(item.SetProperty(key, original[key]))
|
|
8922
8922
|
except Exception as exc:
|
|
@@ -9361,7 +9361,7 @@ def _transcription_capabilities(mp, p: Dict[str, Any]):
|
|
|
9361
9361
|
return _err("clip_ids must be a list")
|
|
9362
9362
|
clips = [_find_clip(root, str(clip_id)) for clip_id in ids]
|
|
9363
9363
|
clips = [clip for clip in clips if clip]
|
|
9364
|
-
elif p.get("selected"):
|
|
9364
|
+
elif _coerce_bool(p.get("selected")):
|
|
9365
9365
|
clips = mp.GetSelectedClips() or []
|
|
9366
9366
|
current_folder = mp.GetCurrentFolder()
|
|
9367
9367
|
return {
|
|
@@ -10300,7 +10300,7 @@ def _media_analysis_records_from_target(mp, p: Dict[str, Any], project=None) ->
|
|
|
10300
10300
|
|
|
10301
10301
|
elif target_type == "clip":
|
|
10302
10302
|
clip_id = target.get("clip_id") or p.get("clip_id")
|
|
10303
|
-
selected = bool(target.get("selected") or p.get("selected"))
|
|
10303
|
+
selected = bool(_coerce_bool(target.get("selected")) or _coerce_bool(p.get("selected")))
|
|
10304
10304
|
clips = []
|
|
10305
10305
|
if selected:
|
|
10306
10306
|
try:
|
|
@@ -10362,7 +10362,7 @@ def _media_analysis_records_from_target(mp, p: Dict[str, Any], project=None) ->
|
|
|
10362
10362
|
target.update({"type": "clips", "clip_ids": clip_ids, "skipped": skipped})
|
|
10363
10363
|
elif target_type == "bin":
|
|
10364
10364
|
path = target.get("path") or p.get("bin_path") or p.get("path") or "Master"
|
|
10365
|
-
recursive =
|
|
10365
|
+
recursive = _coerce_bool(target.get("recursive", p.get("recursive")), True)
|
|
10366
10366
|
folder = _navigate_folder(mp, path)
|
|
10367
10367
|
if not folder:
|
|
10368
10368
|
return None, target, warnings, _err(f"Folder not found: {path}")
|
|
@@ -10370,14 +10370,14 @@ def _media_analysis_records_from_target(mp, p: Dict[str, Any], project=None) ->
|
|
|
10370
10370
|
warnings.extend(folder_warnings)
|
|
10371
10371
|
target.update({"type": "bin", "path": path, "recursive": recursive})
|
|
10372
10372
|
elif target_type == "project":
|
|
10373
|
-
recursive =
|
|
10373
|
+
recursive = _coerce_bool(target.get("recursive"), True)
|
|
10374
10374
|
records, folder_warnings = _media_analysis_folder_records(mp.GetRootFolder(), "Master", recursive=recursive)
|
|
10375
10375
|
warnings.extend(folder_warnings)
|
|
10376
10376
|
target.update({"type": "project", "recursive": recursive})
|
|
10377
10377
|
else:
|
|
10378
10378
|
return None, target, warnings, _err("target.type must be one of file, clip, clips, bin, project, sequence, timeline")
|
|
10379
10379
|
|
|
10380
|
-
records, duplicate_count = _media_analysis_dedupe_records(records,
|
|
10380
|
+
records, duplicate_count = _media_analysis_dedupe_records(records, _coerce_bool(p.get("include_duplicates", target.get("include_duplicates")), False))
|
|
10381
10381
|
if duplicate_count:
|
|
10382
10382
|
warnings.append(f"Deduped {duplicate_count} repeated source media reference(s)")
|
|
10383
10383
|
if not records:
|
|
@@ -12489,7 +12489,7 @@ def _media_analysis_metadata_writeback_enabled(p: Dict[str, Any]) -> bool:
|
|
|
12489
12489
|
"writeToResolve",
|
|
12490
12490
|
)
|
|
12491
12491
|
if raw is None:
|
|
12492
|
-
return
|
|
12492
|
+
return _coerce_bool(_media_analysis_effective_preferences().get("metadata_writeback_default"), True)
|
|
12493
12493
|
return _media_analysis_bool(raw, True)
|
|
12494
12494
|
|
|
12495
12495
|
|
|
@@ -12752,7 +12752,7 @@ def _media_analysis_apply_setup_defaults(action: str, p: Dict[str, Any]) -> Dict
|
|
|
12752
12752
|
"write_to_resolve",
|
|
12753
12753
|
"writeToResolve",
|
|
12754
12754
|
):
|
|
12755
|
-
out["publish_metadata"] =
|
|
12755
|
+
out["publish_metadata"] = _coerce_bool(prefs.get("metadata_writeback_default"), True)
|
|
12756
12756
|
applied["metadata_writeback_default"] = out["publish_metadata"]
|
|
12757
12757
|
|
|
12758
12758
|
persistence = prefs.get("analysis_persistence")
|
|
@@ -13325,7 +13325,7 @@ def _media_pool_probe(mp, p: Dict[str, Any]):
|
|
|
13325
13325
|
def _media_pool_probe_ingest_items(mp, p: Dict[str, Any]):
|
|
13326
13326
|
root = mp.GetRootFolder()
|
|
13327
13327
|
ids = p.get("clip_ids") or p.get("ids")
|
|
13328
|
-
selected =
|
|
13328
|
+
selected = _coerce_bool(p.get("selected"), False)
|
|
13329
13329
|
clips = []
|
|
13330
13330
|
warnings = []
|
|
13331
13331
|
if ids:
|
|
@@ -13376,7 +13376,7 @@ def _string_list_param(p: Dict[str, Any], key: str):
|
|
|
13376
13376
|
|
|
13377
13377
|
def _clips_from_params(root, mp, p: Dict[str, Any], *, key: str = "clip_ids"):
|
|
13378
13378
|
ids = p.get(key) or p.get("ids")
|
|
13379
|
-
selected =
|
|
13379
|
+
selected = _coerce_bool(p.get("selected"), False)
|
|
13380
13380
|
clips = []
|
|
13381
13381
|
missing = []
|
|
13382
13382
|
if ids:
|
|
@@ -13587,7 +13587,7 @@ def _copy_metadata(root, p: Dict[str, Any]):
|
|
|
13587
13587
|
keys = set(p["keys"])
|
|
13588
13588
|
metadata = {key: value for key, value in metadata.items() if key in keys}
|
|
13589
13589
|
third_party = {}
|
|
13590
|
-
if p.get("include_third_party", True):
|
|
13590
|
+
if _coerce_bool(p.get("include_third_party"), True):
|
|
13591
13591
|
third_party = source.GetThirdPartyMetadata("") or {}
|
|
13592
13592
|
results = []
|
|
13593
13593
|
for target_id in target_ids:
|
|
@@ -14310,7 +14310,7 @@ def _link_proxy_checked(root, p: Dict[str, Any]):
|
|
|
14310
14310
|
return missing
|
|
14311
14311
|
check_compatibility = bool(
|
|
14312
14312
|
p.get("check_compatibility")
|
|
14313
|
-
or p.get("require_compatible")
|
|
14313
|
+
or _coerce_bool(p.get("require_compatible"))
|
|
14314
14314
|
or p.get("expected_codec")
|
|
14315
14315
|
or p.get("expected_profile")
|
|
14316
14316
|
or p.get("codec")
|
|
@@ -14329,7 +14329,7 @@ def _link_proxy_checked(root, p: Dict[str, Any]):
|
|
|
14329
14329
|
"proxy_path": proxy_path,
|
|
14330
14330
|
"dry_run": _coerce_bool(p.get("dry_run")),
|
|
14331
14331
|
"check_compatibility": check_compatibility,
|
|
14332
|
-
"require_compatible":
|
|
14332
|
+
"require_compatible": _coerce_bool(p.get("require_compatible")),
|
|
14333
14333
|
"expected_codec": p.get("expected_codec") or p.get("codec"),
|
|
14334
14334
|
"expected_profile": p.get("expected_profile") or p.get("profile"),
|
|
14335
14335
|
}
|
|
@@ -14363,7 +14363,7 @@ def _link_proxy_checked(root, p: Dict[str, Any]):
|
|
|
14363
14363
|
),
|
|
14364
14364
|
)
|
|
14365
14365
|
return out
|
|
14366
|
-
if p.get("require_compatible") and compatibility is not None and not compatibility.get("compatible"):
|
|
14366
|
+
if _coerce_bool(p.get("require_compatible")) and compatibility is not None and not compatibility.get("compatible"):
|
|
14367
14367
|
out = _err(
|
|
14368
14368
|
"Proxy media is not compatible with the source clip; refusing LinkProxyMedia",
|
|
14369
14369
|
code="PROXY_INCOMPATIBLE",
|
|
@@ -14697,7 +14697,7 @@ def _media_pool_boundary_report(mp, p: Dict[str, Any]):
|
|
|
14697
14697
|
"capabilities": _media_pool_ingest_capabilities(),
|
|
14698
14698
|
"media_pool": _media_pool_probe(mp, {"depth": p.get("depth", 1)}),
|
|
14699
14699
|
}
|
|
14700
|
-
if p.get("clip_ids") or p.get("selected"):
|
|
14700
|
+
if p.get("clip_ids") or _coerce_bool(p.get("selected")):
|
|
14701
14701
|
report["items"] = _media_pool_probe_ingest_items(mp, p)
|
|
14702
14702
|
return report
|
|
14703
14703
|
|
|
@@ -18563,7 +18563,7 @@ def _probe_project_settings(project, p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
18563
18563
|
except Exception as exc:
|
|
18564
18564
|
row["error"] = str(exc)
|
|
18565
18565
|
out["candidate_settings"][key] = row
|
|
18566
|
-
if p.get("try_write"):
|
|
18566
|
+
if _coerce_bool(p.get("try_write")):
|
|
18567
18567
|
settings = {
|
|
18568
18568
|
key: row["value"]
|
|
18569
18569
|
for key, row in out["candidate_settings"].items()
|
|
@@ -18591,7 +18591,7 @@ def _safe_set_project_settings(project, p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
18591
18591
|
except Exception as exc:
|
|
18592
18592
|
original[key] = {"error": str(exc)}
|
|
18593
18593
|
if _coerce_bool(p.get("dry_run")):
|
|
18594
|
-
return _ok(would_set=settings, original=original, restore=p.get("restore", True))
|
|
18594
|
+
return _ok(would_set=settings, original=original, restore=_coerce_bool(p.get("restore"), True))
|
|
18595
18595
|
results: Dict[str, Any] = {}
|
|
18596
18596
|
for key, value in settings.items():
|
|
18597
18597
|
row: Dict[str, Any] = {"requested": value, "original": original.get(key)}
|
|
@@ -18604,7 +18604,7 @@ def _safe_set_project_settings(project, p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
18604
18604
|
row["readback"] = _ser(project.GetSetting(key))
|
|
18605
18605
|
except Exception as exc:
|
|
18606
18606
|
row["readback_error"] = str(exc)
|
|
18607
|
-
if p.get("restore", True) and not isinstance(original.get(key), dict):
|
|
18607
|
+
if _coerce_bool(p.get("restore"), True) and not isinstance(original.get(key), dict):
|
|
18608
18608
|
try:
|
|
18609
18609
|
row["restore"] = bool(project.SetSetting(key, original[key]))
|
|
18610
18610
|
row["restored_value"] = _ser(project.GetSetting(key))
|
|
@@ -18617,7 +18617,7 @@ def _safe_set_project_settings(project, p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
18617
18617
|
|
|
18618
18618
|
def _safe_project_create(pm, resolve_obj, p: Dict[str, Any]) -> Dict[str, Any]:
|
|
18619
18619
|
name = p.get("name")
|
|
18620
|
-
invalid = _require_disposable_project_name(name, allow_non_mcp_name=p.get("allow_non_mcp_name", False))
|
|
18620
|
+
invalid = _require_disposable_project_name(name, allow_non_mcp_name=_coerce_bool(p.get("allow_non_mcp_name"), False))
|
|
18621
18621
|
if invalid:
|
|
18622
18622
|
return invalid
|
|
18623
18623
|
media_location_path = p.get("media_location_path") or p.get("mediaLocationPath")
|
|
@@ -18625,7 +18625,7 @@ def _safe_project_create(pm, resolve_obj, p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
18625
18625
|
path_err = _project_path_guard(
|
|
18626
18626
|
media_location_path,
|
|
18627
18627
|
field="media_location_path",
|
|
18628
|
-
require_temp_path=p.get("require_temp_media_location", True),
|
|
18628
|
+
require_temp_path=_coerce_bool(p.get("require_temp_media_location"), True),
|
|
18629
18629
|
)
|
|
18630
18630
|
if path_err:
|
|
18631
18631
|
return path_err
|
|
@@ -18640,15 +18640,15 @@ def _safe_project_create(pm, resolve_obj, p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
18640
18640
|
|
|
18641
18641
|
def _safe_project_export(pm, p: Dict[str, Any]) -> Dict[str, Any]:
|
|
18642
18642
|
name = p.get("name")
|
|
18643
|
-
invalid = _require_disposable_project_name(name, allow_non_mcp_name=p.get("allow_non_mcp_name", False))
|
|
18643
|
+
invalid = _require_disposable_project_name(name, allow_non_mcp_name=_coerce_bool(p.get("allow_non_mcp_name"), False))
|
|
18644
18644
|
if invalid:
|
|
18645
18645
|
return invalid
|
|
18646
18646
|
path = p.get("path")
|
|
18647
|
-
path_err = _project_path_guard(path, require_temp_path=p.get("require_temp_path", True))
|
|
18647
|
+
path_err = _project_path_guard(path, require_temp_path=_coerce_bool(p.get("require_temp_path"), True))
|
|
18648
18648
|
if path_err:
|
|
18649
18649
|
return path_err
|
|
18650
18650
|
os.makedirs(_project_path_parent(path), exist_ok=True)
|
|
18651
|
-
with_stills =
|
|
18651
|
+
with_stills = _coerce_bool(p.get("with_stills_and_luts"), False)
|
|
18652
18652
|
if _coerce_bool(p.get("dry_run")):
|
|
18653
18653
|
return _ok(would_export=True, name=name, path=path, with_stills_and_luts=with_stills)
|
|
18654
18654
|
return {"success": bool(pm.ExportProject(name, path, with_stills))}
|
|
@@ -18656,13 +18656,13 @@ def _safe_project_export(pm, p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
18656
18656
|
|
|
18657
18657
|
def _safe_project_import(pm, p: Dict[str, Any]) -> Dict[str, Any]:
|
|
18658
18658
|
path = p.get("path")
|
|
18659
|
-
path_err = _project_path_guard(path, require_temp_path=p.get("require_temp_path", True))
|
|
18659
|
+
path_err = _project_path_guard(path, require_temp_path=_coerce_bool(p.get("require_temp_path"), True))
|
|
18660
18660
|
if path_err:
|
|
18661
18661
|
return path_err
|
|
18662
18662
|
if not os.path.exists(path):
|
|
18663
18663
|
return _err(f"path does not exist: {path}")
|
|
18664
18664
|
name = p.get("name")
|
|
18665
|
-
invalid = _require_disposable_project_name(name, allow_non_mcp_name=p.get("allow_non_mcp_name", False))
|
|
18665
|
+
invalid = _require_disposable_project_name(name, allow_non_mcp_name=_coerce_bool(p.get("allow_non_mcp_name"), False))
|
|
18666
18666
|
if invalid:
|
|
18667
18667
|
return invalid
|
|
18668
18668
|
if _coerce_bool(p.get("dry_run")):
|
|
@@ -18672,11 +18672,11 @@ def _safe_project_import(pm, p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
18672
18672
|
|
|
18673
18673
|
def _safe_project_archive(pm, p: Dict[str, Any]) -> Dict[str, Any]:
|
|
18674
18674
|
name = p.get("name")
|
|
18675
|
-
invalid = _require_disposable_project_name(name, allow_non_mcp_name=p.get("allow_non_mcp_name", False))
|
|
18675
|
+
invalid = _require_disposable_project_name(name, allow_non_mcp_name=_coerce_bool(p.get("allow_non_mcp_name"), False))
|
|
18676
18676
|
if invalid:
|
|
18677
18677
|
return invalid
|
|
18678
18678
|
path = p.get("path")
|
|
18679
|
-
path_err = _project_path_guard(path, require_temp_path=p.get("require_temp_path", True))
|
|
18679
|
+
path_err = _project_path_guard(path, require_temp_path=_coerce_bool(p.get("require_temp_path"), True))
|
|
18680
18680
|
if path_err:
|
|
18681
18681
|
return path_err
|
|
18682
18682
|
flags, flag_err = archive_guard.read_flags(p)
|
|
@@ -18707,13 +18707,13 @@ def _safe_project_archive(pm, p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
18707
18707
|
|
|
18708
18708
|
def _safe_project_restore(pm, p: Dict[str, Any]) -> Dict[str, Any]:
|
|
18709
18709
|
path = p.get("path")
|
|
18710
|
-
path_err = _project_path_guard(path, require_temp_path=p.get("require_temp_path", True))
|
|
18710
|
+
path_err = _project_path_guard(path, require_temp_path=_coerce_bool(p.get("require_temp_path"), True))
|
|
18711
18711
|
if path_err:
|
|
18712
18712
|
return path_err
|
|
18713
18713
|
if not os.path.exists(path):
|
|
18714
18714
|
return _err(f"path does not exist: {path}")
|
|
18715
18715
|
name = p.get("name")
|
|
18716
|
-
invalid = _require_disposable_project_name(name, allow_non_mcp_name=p.get("allow_non_mcp_name", False))
|
|
18716
|
+
invalid = _require_disposable_project_name(name, allow_non_mcp_name=_coerce_bool(p.get("allow_non_mcp_name"), False))
|
|
18717
18717
|
if invalid:
|
|
18718
18718
|
return invalid
|
|
18719
18719
|
if _coerce_bool(p.get("dry_run")):
|
|
@@ -18723,7 +18723,7 @@ def _safe_project_restore(pm, p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
18723
18723
|
|
|
18724
18724
|
def _safe_project_delete(pm, p: Dict[str, Any]) -> Dict[str, Any]:
|
|
18725
18725
|
name = p.get("name")
|
|
18726
|
-
invalid = _require_disposable_project_name(name, allow_non_mcp_name=p.get("allow_non_mcp_name", False))
|
|
18726
|
+
invalid = _require_disposable_project_name(name, allow_non_mcp_name=_coerce_bool(p.get("allow_non_mcp_name"), False))
|
|
18727
18727
|
if invalid:
|
|
18728
18728
|
return invalid
|
|
18729
18729
|
if _coerce_bool(p.get("dry_run")):
|
|
@@ -18736,7 +18736,7 @@ def _safe_project_delete(pm, p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
18736
18736
|
if current_name == name:
|
|
18737
18737
|
if not _coerce_bool(p.get("close_current")):
|
|
18738
18738
|
return _err("Refusing to delete the currently open project; pass close_current=True")
|
|
18739
|
-
saved = bool(pm.SaveProject()) if p.get("save_current", True) else None
|
|
18739
|
+
saved = bool(pm.SaveProject()) if _coerce_bool(p.get("save_current"), True) else None
|
|
18740
18740
|
closed = bool(pm.CloseProject(current))
|
|
18741
18741
|
if not closed:
|
|
18742
18742
|
return _err(f"Failed to close current project '{name}' before delete")
|
|
@@ -19112,13 +19112,13 @@ def _spec_action(r, pm, action: str, p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
19112
19112
|
return _ok(project=spec.project,
|
|
19113
19113
|
**_project_spec.apply_spec(spec, executor, dry_run=True))
|
|
19114
19114
|
# apply_spec
|
|
19115
|
-
run_hooks =
|
|
19115
|
+
run_hooks = _coerce_bool(p.get("run_hooks"), False)
|
|
19116
19116
|
try:
|
|
19117
19117
|
result = _project_spec.apply_spec(
|
|
19118
19118
|
spec, executor,
|
|
19119
19119
|
dry_run=_coerce_bool(p.get("dry_run"), False),
|
|
19120
19120
|
run_hooks=run_hooks,
|
|
19121
|
-
continue_on_error=
|
|
19121
|
+
continue_on_error=_coerce_bool(p.get("continue_on_error"), False),
|
|
19122
19122
|
run_hook=_make_spec_hook_runner() if run_hooks else None,
|
|
19123
19123
|
)
|
|
19124
19124
|
except _project_spec.SpecError as exc:
|
|
@@ -19473,7 +19473,7 @@ def project_manager(action: str, params: Optional[Dict[str, Any]] = None) -> Dic
|
|
|
19473
19473
|
except Exception:
|
|
19474
19474
|
rendering = False
|
|
19475
19475
|
if rendering:
|
|
19476
|
-
if not p.get("stop_render"):
|
|
19476
|
+
if not _coerce_bool(p.get("stop_render")):
|
|
19477
19477
|
return _err(
|
|
19478
19478
|
"A render is in progress on this project. Closing now would "
|
|
19479
19479
|
"orphan the render and wedge Resolve's render pipeline — the "
|
|
@@ -19519,7 +19519,7 @@ def project_manager(action: str, params: Optional[Dict[str, Any]] = None) -> Dic
|
|
|
19519
19519
|
})
|
|
19520
19520
|
if err:
|
|
19521
19521
|
return _err(err)
|
|
19522
|
-
return {"success": bool(pm.ExportProject(p["name"], p["path"], p.get("with_stills_and_luts", True)))}
|
|
19522
|
+
return {"success": bool(pm.ExportProject(p["name"], p["path"], _coerce_bool(p.get("with_stills_and_luts"), True)))}
|
|
19523
19523
|
elif action == "archive":
|
|
19524
19524
|
err, _clean = _validate_params(p, {
|
|
19525
19525
|
"name": {"type": str, "required": True, "non_empty": True},
|
|
@@ -19787,7 +19787,7 @@ def project_settings(action: str, params: Optional[Dict[str, Any]] = None) -> Di
|
|
|
19787
19787
|
elif action == "project_summary":
|
|
19788
19788
|
return _project_summary(
|
|
19789
19789
|
proj,
|
|
19790
|
-
include_clips=
|
|
19790
|
+
include_clips=_coerce_bool(p.get("include_clips")),
|
|
19791
19791
|
clip_limit=int(p.get("clip_limit", 50)),
|
|
19792
19792
|
)
|
|
19793
19793
|
elif action == "load_burnin_preset":
|
|
@@ -20214,7 +20214,7 @@ def _validate_render_settings_payload(settings: Dict[str, Any], *, require_temp_
|
|
|
20214
20214
|
def _validate_render_settings_action(p: Dict[str, Any]):
|
|
20215
20215
|
validation, err = _validate_render_settings_payload(
|
|
20216
20216
|
p.get("settings"),
|
|
20217
|
-
require_temp_target=
|
|
20217
|
+
require_temp_target=_coerce_bool(p.get("require_temp_target"), False),
|
|
20218
20218
|
)
|
|
20219
20219
|
if err:
|
|
20220
20220
|
return err
|
|
@@ -20238,7 +20238,7 @@ def _safe_set_render_settings(proj, p: Dict[str, Any]):
|
|
|
20238
20238
|
settings = p.get("settings")
|
|
20239
20239
|
validation, err = _validate_render_settings_payload(
|
|
20240
20240
|
settings,
|
|
20241
|
-
require_temp_target=
|
|
20241
|
+
require_temp_target=_coerce_bool(p.get("require_temp_target"), False),
|
|
20242
20242
|
)
|
|
20243
20243
|
if err:
|
|
20244
20244
|
return err
|
|
@@ -20256,7 +20256,7 @@ def _safe_set_render_settings(proj, p: Dict[str, Any]):
|
|
|
20256
20256
|
"after": after_settings,
|
|
20257
20257
|
"diff": _settings_diff(settings, after_settings),
|
|
20258
20258
|
}
|
|
20259
|
-
if p.get("restore") and isinstance(before.get("settings"), dict):
|
|
20259
|
+
if _coerce_bool(p.get("restore")) and isinstance(before.get("settings"), dict):
|
|
20260
20260
|
result["restore_success"] = bool(proj.SetRenderSettings(before["settings"]))
|
|
20261
20261
|
return result
|
|
20262
20262
|
|
|
@@ -20267,13 +20267,13 @@ def _prepare_render_job(proj, p: Dict[str, Any]):
|
|
|
20267
20267
|
return _err("target_dir or settings.TargetDir is required")
|
|
20268
20268
|
if not os.path.isdir(target_dir):
|
|
20269
20269
|
return _err(f"target_dir does not exist: {target_dir}")
|
|
20270
|
-
if p.get("require_temp_target", True) and not _render_temp_path_ok(target_dir):
|
|
20270
|
+
if _coerce_bool(p.get("require_temp_target"), True) and not _render_temp_path_ok(target_dir):
|
|
20271
20271
|
return _err("target_dir must be under the system temp directory unless require_temp_target=False")
|
|
20272
20272
|
settings = dict(p.get("settings") or {})
|
|
20273
20273
|
settings.setdefault("TargetDir", target_dir)
|
|
20274
20274
|
if p.get("custom_name"):
|
|
20275
20275
|
settings["CustomName"] = p["custom_name"]
|
|
20276
|
-
validation, err = _validate_render_settings_payload(settings, require_temp_target=p.get("require_temp_target", True))
|
|
20276
|
+
validation, err = _validate_render_settings_payload(settings, require_temp_target=_coerce_bool(p.get("require_temp_target"), True))
|
|
20277
20277
|
if err:
|
|
20278
20278
|
return err
|
|
20279
20279
|
if not validation["valid"]:
|
|
@@ -20516,7 +20516,7 @@ def _list_delivery_targets(proj, p: Dict[str, Any]):
|
|
|
20516
20516
|
extras = _delivery_target_extras()
|
|
20517
20517
|
listing = _delivery_targets.list_targets(extras, tier=tier)
|
|
20518
20518
|
|
|
20519
|
-
if p.get("check_availability"):
|
|
20519
|
+
if _coerce_bool(p.get("check_availability")):
|
|
20520
20520
|
# Resolve every target against the live matrix so the caller sees what
|
|
20521
20521
|
# this machine/license can actually render, not just what ships.
|
|
20522
20522
|
formats = _render_formats(proj)
|
|
@@ -20543,7 +20543,7 @@ def _list_delivery_targets(proj, p: Dict[str, Any]):
|
|
|
20543
20543
|
targets=listing,
|
|
20544
20544
|
schema_version=_delivery_targets.SCHEMA_VERSION,
|
|
20545
20545
|
tiers=list(_delivery_targets.TIERS),
|
|
20546
|
-
availability_checked=
|
|
20546
|
+
availability_checked=_coerce_bool(p.get("check_availability")),
|
|
20547
20547
|
)
|
|
20548
20548
|
|
|
20549
20549
|
|
|
@@ -20618,7 +20618,7 @@ def _safe_quick_export(proj, p: Dict[str, Any]):
|
|
|
20618
20618
|
params["EnableUpload"] = False
|
|
20619
20619
|
validation, err = _validate_render_settings_payload(
|
|
20620
20620
|
{key: value for key, value in params.items() if key in _RENDER_SETTING_KEYS},
|
|
20621
|
-
require_temp_target=
|
|
20621
|
+
require_temp_target=_coerce_bool(p.get("require_temp_target"), True),
|
|
20622
20622
|
)
|
|
20623
20623
|
if err:
|
|
20624
20624
|
return err
|
|
@@ -20667,9 +20667,9 @@ def _export_render_boundary_report(proj, p: Dict[str, Any]):
|
|
|
20667
20667
|
"capabilities": _render_capabilities(proj),
|
|
20668
20668
|
"settings": _render_settings_snapshot(proj),
|
|
20669
20669
|
}
|
|
20670
|
-
if p.get("include_matrix", True):
|
|
20670
|
+
if _coerce_bool(p.get("include_matrix"), True):
|
|
20671
20671
|
report["matrix"] = _probe_render_matrix(proj, {"max_pairs": p.get("max_pairs")})
|
|
20672
|
-
if p.get("include_quick_export", True):
|
|
20672
|
+
if _coerce_bool(p.get("include_quick_export"), True):
|
|
20673
20673
|
report["quick_export"] = _quick_export_capabilities(proj)
|
|
20674
20674
|
return report
|
|
20675
20675
|
|
|
@@ -20945,7 +20945,7 @@ def render(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, An
|
|
|
20945
20945
|
return result
|
|
20946
20946
|
elif action == "start":
|
|
20947
20947
|
job_ids = p.get("job_ids")
|
|
20948
|
-
interactive = p.get("interactive", False)
|
|
20948
|
+
interactive = _coerce_bool(p.get("interactive"), False)
|
|
20949
20949
|
if job_ids:
|
|
20950
20950
|
return {"success": bool(proj.StartRendering(job_ids, interactive))}
|
|
20951
20951
|
return {"success": bool(proj.StartRendering(interactive))}
|
|
@@ -21946,7 +21946,7 @@ def media_pool_item(action: str, params: Optional[Dict[str, Any]] = None) -> Dic
|
|
|
21946
21946
|
# when the clip loads in source viewer. Caller passes seconds; we convert
|
|
21947
21947
|
# to frames using the clip's fps from properties.
|
|
21948
21948
|
mark_set: Optional[Dict[str, Any]] = None
|
|
21949
|
-
if p.get("clear_marks") or p.get("clearMarks"):
|
|
21949
|
+
if _coerce_bool(p.get("clear_marks")) or _coerce_bool(p.get("clearMarks")):
|
|
21950
21950
|
try:
|
|
21951
21951
|
clip.ClearMarkInOut(p.get("mark_type") or "all")
|
|
21952
21952
|
mark_set = {"cleared": True}
|
|
@@ -22007,7 +22007,7 @@ def media_pool_item(action: str, params: Optional[Dict[str, Any]] = None) -> Dic
|
|
|
22007
22007
|
|
|
22008
22008
|
# Bring Resolve to the foreground so the editor doesn't have to
|
|
22009
22009
|
# alt-tab. Default on; pass focus_app=false to suppress.
|
|
22010
|
-
focus_app = p.get("focus_app", p.get("focusApp", True)
|
|
22010
|
+
focus_app = _coerce_bool(p.get("focus_app", p.get("focusApp")), True)
|
|
22011
22011
|
focus_result: Optional[Dict[str, Any]] = None
|
|
22012
22012
|
if focus_app:
|
|
22013
22013
|
focus_result = _activate_resolve_window()
|
|
@@ -22017,7 +22017,7 @@ def media_pool_item(action: str, params: Optional[Dict[str, Any]] = None) -> Dic
|
|
|
22017
22017
|
# call, so we send the keyboard equivalent of "Go to In" once the
|
|
22018
22018
|
# app is in the foreground. Default on when a mark was set.
|
|
22019
22019
|
jump_result: Optional[Dict[str, Any]] = None
|
|
22020
|
-
wants_jump = p.get("jump_to_mark_in", p.get("jumpToMarkIn", True)
|
|
22020
|
+
wants_jump = _coerce_bool(p.get("jump_to_mark_in", p.get("jumpToMarkIn")), True)
|
|
22021
22021
|
applied_mark = bool(mark_set and mark_set.get("applied"))
|
|
22022
22022
|
if wants_jump and applied_mark and focus_result and focus_result.get("activated"):
|
|
22023
22023
|
jump_result = _send_resolve_keystroke_go_to_mark_in()
|
|
@@ -22167,12 +22167,12 @@ def media_pool_item(action: str, params: Optional[Dict[str, Any]] = None) -> Dic
|
|
|
22167
22167
|
# Segment timecodes are SOURCE timecodes, not timeline positions.
|
|
22168
22168
|
if _has_method(clip, "GetTranscription"):
|
|
22169
22169
|
try:
|
|
22170
|
-
full = clip.GetTranscription(
|
|
22170
|
+
full = clip.GetTranscription(_coerce_bool(p.get("use_nested_clip_transcription"), False))
|
|
22171
22171
|
except Exception:
|
|
22172
22172
|
full = None
|
|
22173
22173
|
segs = full.get("segments") if isinstance(full, dict) else None
|
|
22174
22174
|
if segs:
|
|
22175
|
-
if not p.get("include_words"):
|
|
22175
|
+
if not _coerce_bool(p.get("include_words")):
|
|
22176
22176
|
# `words` is several times the bulk of the segment text and
|
|
22177
22177
|
# most callers want segment-level timing. Opt in for it.
|
|
22178
22178
|
segs = [{k: v for k, v in seg.items() if k != "words"}
|
|
@@ -22799,7 +22799,7 @@ async def media_analysis(action: str, params: Optional[Dict[str, Any]] = None, c
|
|
|
22799
22799
|
if vctx is None:
|
|
22800
22800
|
return _err("No project context — open a Resolve project first")
|
|
22801
22801
|
_r, _proj, project_root, _name = vctx
|
|
22802
|
-
session_only =
|
|
22802
|
+
session_only = _coerce_bool(p.get("session_only"), False)
|
|
22803
22803
|
session_id = _AI_LEDGER_SESSION_ID if session_only else None
|
|
22804
22804
|
limit = _safe_int(p.get("limit"), 50, minimum=1, maximum=1000)
|
|
22805
22805
|
return {
|
|
@@ -22913,7 +22913,7 @@ async def media_analysis(action: str, params: Optional[Dict[str, Any]] = None, c
|
|
|
22913
22913
|
project_id=project_id,
|
|
22914
22914
|
analysis_root=p.get("analysis_root"),
|
|
22915
22915
|
source_paths=p.get("source_paths") or p.get("sourcePaths") or [],
|
|
22916
|
-
create=
|
|
22916
|
+
create=_coerce_bool(p.get("create"), False),
|
|
22917
22917
|
)
|
|
22918
22918
|
|
|
22919
22919
|
if action in {
|
|
@@ -22978,7 +22978,7 @@ async def media_analysis(action: str, params: Optional[Dict[str, Any]] = None, c
|
|
|
22978
22978
|
project_id=project_id,
|
|
22979
22979
|
analysis_root=p.get("analysis_root"),
|
|
22980
22980
|
source_paths=[],
|
|
22981
|
-
create=
|
|
22981
|
+
create=_coerce_bool(p.get("create"), False),
|
|
22982
22982
|
)
|
|
22983
22983
|
if not root.get("success"):
|
|
22984
22984
|
return root
|
|
@@ -23091,7 +23091,7 @@ async def media_analysis(action: str, params: Optional[Dict[str, Any]] = None, c
|
|
|
23091
23091
|
end_seconds=end,
|
|
23092
23092
|
match_word=p.get("match_word") or p.get("matchWord") or p.get("word"),
|
|
23093
23093
|
context_seconds=_opt_number(p.get("context_seconds", p.get("contextSeconds"))) or 2.0,
|
|
23094
|
-
include_curve_values=
|
|
23094
|
+
include_curve_values=_coerce_bool(p.get("include_curve_values", p.get("includeCurveValues")), False),
|
|
23095
23095
|
limit=int(_opt_number(p.get("limit")) or 20),
|
|
23096
23096
|
)
|
|
23097
23097
|
if action == "timeline_strata":
|
|
@@ -23116,7 +23116,7 @@ async def media_analysis(action: str, params: Optional[Dict[str, Any]] = None, c
|
|
|
23116
23116
|
record_start_frame=int(start_frame) if start_frame is not None else None,
|
|
23117
23117
|
record_end_frame=int(end_frame) if end_frame is not None else None,
|
|
23118
23118
|
fps=fps,
|
|
23119
|
-
include_curve_values=
|
|
23119
|
+
include_curve_values=_coerce_bool(p.get("include_curve_values", p.get("includeCurveValues")), False),
|
|
23120
23120
|
)
|
|
23121
23121
|
if action == "plan_story_beats":
|
|
23122
23122
|
from src.utils import strata_story
|
|
@@ -23319,7 +23319,7 @@ async def media_analysis(action: str, params: Optional[Dict[str, Any]] = None, c
|
|
|
23319
23319
|
if not job_id:
|
|
23320
23320
|
return _err("resume_batch_job requires job_id")
|
|
23321
23321
|
return resume_media_analysis_batch_job(project_root, str(job_id))
|
|
23322
|
-
return cleanup_media_analysis_artifacts(project_root, frames_only=
|
|
23322
|
+
return cleanup_media_analysis_artifacts(project_root, frames_only=_coerce_bool(p.get("frames_only"), True))
|
|
23323
23323
|
|
|
23324
23324
|
if action == "review_timeline_markers":
|
|
23325
23325
|
tl = proj.GetCurrentTimeline()
|
|
@@ -23514,11 +23514,11 @@ async def media_analysis(action: str, params: Optional[Dict[str, Any]] = None, c
|
|
|
23514
23514
|
if action == "analyze_file":
|
|
23515
23515
|
target.update({"type": "file", "path": p.get("path") or p.get("file_path") or p.get("filePath") or target.get("path")})
|
|
23516
23516
|
elif action == "analyze_clip":
|
|
23517
|
-
target.update({"type": "clip", "clip_id": p.get("clip_id") or target.get("clip_id"), "selected": p.get("selected", target.get("selected", False)
|
|
23517
|
+
target.update({"type": "clip", "clip_id": p.get("clip_id") or target.get("clip_id"), "selected": _coerce_bool(p.get("selected", target.get("selected")), False)})
|
|
23518
23518
|
elif action == "analyze_bin":
|
|
23519
|
-
target.update({"type": "bin", "path": p.get("bin_path") or p.get("path") or target.get("path") or "Master", "recursive": p.get("recursive", target.get("recursive", True)
|
|
23519
|
+
target.update({"type": "bin", "path": p.get("bin_path") or p.get("path") or target.get("path") or "Master", "recursive": _coerce_bool(p.get("recursive", target.get("recursive")), True)})
|
|
23520
23520
|
elif action == "analyze_project":
|
|
23521
|
-
target.update({"type": "project", "recursive": p.get("recursive", target.get("recursive", True)
|
|
23521
|
+
target.update({"type": "project", "recursive": _coerce_bool(p.get("recursive", target.get("recursive")), True)})
|
|
23522
23522
|
elif action in {"analyze_timeline", "analyze_sequence"}:
|
|
23523
23523
|
target.update({
|
|
23524
23524
|
"type": "sequence",
|
|
@@ -24047,7 +24047,7 @@ def _attach_audit_report(result: Dict[str, Any], params: Dict[str, Any]) -> Dict
|
|
|
24047
24047
|
if not isinstance(result, dict) or not result.get("success"):
|
|
24048
24048
|
return result
|
|
24049
24049
|
result["report_available"] = "pass include_report=true for a Markdown report"
|
|
24050
|
-
flag = params.get("include_report", params.get("includeReport", False)
|
|
24050
|
+
flag = _coerce_bool(params.get("include_report", params.get("includeReport")), False)
|
|
24051
24051
|
if str(flag).strip().lower() in {"true", "1", "yes", "on"}:
|
|
24052
24052
|
try:
|
|
24053
24053
|
result["report_markdown"] = _edit_report_mod.render_any(result)
|
|
@@ -24401,7 +24401,7 @@ def edit_engine(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[st
|
|
|
24401
24401
|
target_ratio=p.get("target_ratio") or p.get("targetRatio"),
|
|
24402
24402
|
min_pause_seconds=float(p.get("min_pause_seconds") or p.get("minPauseSeconds") or _edit_engine_mod.DEFAULT_MIN_PAUSE_SECONDS),
|
|
24403
24403
|
handle_seconds=float(p.get("handle_seconds") or p.get("handleSeconds") or _edit_engine_mod.DEFAULT_HANDLE_SECONDS),
|
|
24404
|
-
include_audio=str(p.get("include_audio", p.get("includeAudio", True))
|
|
24404
|
+
include_audio=str(_coerce_bool(p.get("include_audio", p.get("includeAudio")), True)).strip().lower() not in {"false", "0", "no", "none", "off"},
|
|
24405
24405
|
)
|
|
24406
24406
|
|
|
24407
24407
|
if action == "generate_captions":
|
|
@@ -24430,7 +24430,7 @@ def edit_engine(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[st
|
|
|
24430
24430
|
_result = _captions_mod.generate(
|
|
24431
24431
|
_words,
|
|
24432
24432
|
fmt=str(p.get("format") or p.get("fmt") or "srt").lower(),
|
|
24433
|
-
with_chapters=str(p.get("with_chapters", p.get("withChapters", False))
|
|
24433
|
+
with_chapters=str(_coerce_bool(p.get("with_chapters", p.get("withChapters")), False)).strip().lower() in {"true", "1", "yes", "on"},
|
|
24434
24434
|
**_opts,
|
|
24435
24435
|
)
|
|
24436
24436
|
except _captions_mod.CaptionError as exc:
|
|
@@ -24445,9 +24445,9 @@ def edit_engine(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[st
|
|
|
24445
24445
|
return _edit_engine_mod.plan_transcript_tighten(
|
|
24446
24446
|
project_root,
|
|
24447
24447
|
clip_ref=p.get("clip_ref") or p.get("clipRef") or p.get("clip_id"),
|
|
24448
|
-
remove_fillers=str(p.get("remove_fillers", p.get("removeFillers", True))
|
|
24449
|
-
remove_false_starts=str(p.get("remove_false_starts", p.get("removeFalseStarts", True))
|
|
24450
|
-
collapse_pauses=str(p.get("collapse_pauses", p.get("collapsePauses", True))
|
|
24448
|
+
remove_fillers=str(_coerce_bool(p.get("remove_fillers", p.get("removeFillers")), True)).strip().lower() not in {"false", "0", "no", "off"},
|
|
24449
|
+
remove_false_starts=str(_coerce_bool(p.get("remove_false_starts", p.get("removeFalseStarts")), True)).strip().lower() not in {"false", "0", "no", "off"},
|
|
24450
|
+
collapse_pauses=str(_coerce_bool(p.get("collapse_pauses", p.get("collapsePauses")), True)).strip().lower() not in {"false", "0", "no", "off"},
|
|
24451
24451
|
max_pause=float(p.get("max_pause", p.get("maxPause", _transcript_edit_defaults.DEFAULT_MAX_PAUSE_S))),
|
|
24452
24452
|
handle=float(p.get("handle", _transcript_edit_defaults.DEFAULT_HANDLE_S)),
|
|
24453
24453
|
min_cut=float(p.get("min_cut", p.get("minCut", _transcript_edit_defaults.DEFAULT_MIN_CUT_S))),
|
|
@@ -24509,7 +24509,7 @@ def edit_engine(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[st
|
|
|
24509
24509
|
min_strip_frames=float(p.get("min_strip_frames") if p.get("min_strip_frames") is not None else p.get("minStripFrames") if p.get("minStripFrames") is not None else _edit_engine_mod.DEFAULT_SILENCE_MIN_STRIP_FRAMES),
|
|
24510
24510
|
pre_head_frames=float(p.get("pre_head_frames") if p.get("pre_head_frames") is not None else p.get("preHeadFrames") if p.get("preHeadFrames") is not None else _edit_engine_mod.DEFAULT_SILENCE_PRE_HEAD_FRAMES),
|
|
24511
24511
|
post_tail_frames=float(p.get("post_tail_frames") if p.get("post_tail_frames") is not None else p.get("postTailFrames") if p.get("postTailFrames") is not None else _edit_engine_mod.DEFAULT_SILENCE_POST_TAIL_FRAMES),
|
|
24512
|
-
include_audio=str(p.get("include_audio", p.get("includeAudio", True))
|
|
24512
|
+
include_audio=str(_coerce_bool(p.get("include_audio", p.get("includeAudio")), True)).strip().lower() not in {"false", "0", "no", "none", "off"},
|
|
24513
24513
|
)
|
|
24514
24514
|
|
|
24515
24515
|
if action == "rule_of_six_audit":
|
|
@@ -24744,7 +24744,7 @@ def edit_engine(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[st
|
|
|
24744
24744
|
code="MISSING_CANDIDATES", category="invalid_input")
|
|
24745
24745
|
return _edit_engine_mod.plan_broll(
|
|
24746
24746
|
beats=beats, candidates=candidates,
|
|
24747
|
-
allow_reuse=str(p.get("allow_reuse", p.get("allowReuse", False))
|
|
24747
|
+
allow_reuse=str(_coerce_bool(p.get("allow_reuse", p.get("allowReuse")), False)).strip().lower() in {"true", "1", "yes", "on"},
|
|
24748
24748
|
)
|
|
24749
24749
|
|
|
24750
24750
|
if action == "plan_turnover":
|
|
@@ -26085,7 +26085,7 @@ def timeline(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[str,
|
|
|
26085
26085
|
mpi = tl.GetMediaPoolItem()
|
|
26086
26086
|
return {"name": mpi.GetName(), "id": mpi.GetUniqueId()} if mpi else {"name": None, "id": None}
|
|
26087
26087
|
elif action == "get_transcript":
|
|
26088
|
-
return _timeline_transcript(tl, with_timecodes=
|
|
26088
|
+
return _timeline_transcript(tl, with_timecodes=_coerce_bool(p.get("with_timecodes")))
|
|
26089
26089
|
elif action == "propose_cuts":
|
|
26090
26090
|
# Dry-run only: detect mechanical cuts (fillers, long pauses, repeats)
|
|
26091
26091
|
# from the timeline's subtitle transcript. Proposes; never edits.
|
|
@@ -27379,7 +27379,7 @@ def _grade_item_snapshot(item, proj=None, p: Optional[Dict[str, Any]] = None):
|
|
|
27379
27379
|
out["errors"].append({"method": method_name, "error": str(exc)})
|
|
27380
27380
|
try:
|
|
27381
27381
|
graph = item.GetNodeGraph(p["layer_index"]) if "layer_index" in p else item.GetNodeGraph()
|
|
27382
|
-
out["node_graph"] = _graph_snapshot(graph, include_nodes=p.get("include_nodes", True), max_nodes=p.get("max_nodes", 3))
|
|
27382
|
+
out["node_graph"] = _graph_snapshot(graph, include_nodes=_coerce_bool(p.get("include_nodes"), True), max_nodes=p.get("max_nodes", 3))
|
|
27383
27383
|
except Exception as exc:
|
|
27384
27384
|
out["node_graph"] = {"available": False, "error": str(exc)}
|
|
27385
27385
|
try:
|
|
@@ -27432,7 +27432,7 @@ def _probe_color_node_graph(proj, item, p: Dict[str, Any]):
|
|
|
27432
27432
|
return err
|
|
27433
27433
|
snapshot = _graph_snapshot(
|
|
27434
27434
|
graph,
|
|
27435
|
-
include_nodes=p.get("include_nodes", True),
|
|
27435
|
+
include_nodes=_coerce_bool(p.get("include_nodes"), True),
|
|
27436
27436
|
max_nodes=p.get("max_nodes", 3),
|
|
27437
27437
|
)
|
|
27438
27438
|
snapshot["source"] = source
|
|
@@ -27573,7 +27573,7 @@ def _safe_export_lut(item, p: Dict[str, Any]):
|
|
|
27573
27573
|
path = p.get("path")
|
|
27574
27574
|
if not path:
|
|
27575
27575
|
return _err("path is required")
|
|
27576
|
-
if p.get("require_temp_path", True) and not _grade_temp_path_ok(path):
|
|
27576
|
+
if _coerce_bool(p.get("require_temp_path"), True) and not _grade_temp_path_ok(path):
|
|
27577
27577
|
return _err("path must be under the system temp directory unless require_temp_path=False")
|
|
27578
27578
|
folder = os.path.dirname(os.path.abspath(path))
|
|
27579
27579
|
if folder:
|
|
@@ -27602,7 +27602,7 @@ def _safe_apply_drx(proj, item, p: Dict[str, Any]):
|
|
|
27602
27602
|
return _err("path is required", code="MISSING_PATH", category="invalid_input")
|
|
27603
27603
|
if not os.path.isfile(path):
|
|
27604
27604
|
return _err(f"DRX file not found: {path}", code="DRX_NOT_FOUND", category="invalid_input")
|
|
27605
|
-
if p.get("require_temp_path", True) and not _grade_temp_path_ok(path):
|
|
27605
|
+
if _coerce_bool(p.get("require_temp_path"), True) and not _grade_temp_path_ok(path):
|
|
27606
27606
|
return _err("DRX path must be under the system temp directory unless require_temp_path=False",
|
|
27607
27607
|
code="DRX_PATH_NOT_TEMP", category="invalid_input")
|
|
27608
27608
|
graph, source, err = _color_graph_from_params(proj, item, p)
|
|
@@ -27701,7 +27701,7 @@ def _resolve_trace_plan(tl, plan: Dict[str, Any], p: Dict[str, Any]) -> List[Dic
|
|
|
27701
27701
|
"""Map each plan match onto a live clip. status: apply | skip (+reason)."""
|
|
27702
27702
|
min_conf = float(p.get("min_confidence", 0.8))
|
|
27703
27703
|
tol = int(p.get("start_tolerance", 0))
|
|
27704
|
-
require_temp = p.get("require_temp_path", True)
|
|
27704
|
+
require_temp = _coerce_bool(p.get("require_temp_path"), True)
|
|
27705
27705
|
live = _live_video_items(tl)
|
|
27706
27706
|
rows: List[Dict[str, Any]] = []
|
|
27707
27707
|
for m in plan["matches"]:
|
|
@@ -27796,7 +27796,7 @@ def _apply_trace_plan(p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
27796
27796
|
return err
|
|
27797
27797
|
live_name = tl.GetName()
|
|
27798
27798
|
plan_tl = (plan.get("target") or {}).get("timeline")
|
|
27799
|
-
if plan_tl and plan_tl != live_name and not p.get("allow_timeline_mismatch"):
|
|
27799
|
+
if plan_tl and plan_tl != live_name and not _coerce_bool(p.get("allow_timeline_mismatch")):
|
|
27800
27800
|
return _err(
|
|
27801
27801
|
f"plan targets timeline {plan_tl!r} but the current timeline is {live_name!r}",
|
|
27802
27802
|
code="TIMELINE_MISMATCH", category="invalid_input",
|
|
@@ -27821,7 +27821,7 @@ def _apply_trace_plan(p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
27821
27821
|
grade_mode = p.get("grade_mode", 0)
|
|
27822
27822
|
version_name = p.get("version_name")
|
|
27823
27823
|
max_rows = p.get("max_rows", _TRACE_MAX_ROWS_DEFAULT)
|
|
27824
|
-
max_rows = len(public) if p.get("verbose") else max(0, int(max_rows))
|
|
27824
|
+
max_rows = len(public) if _coerce_bool(p.get("verbose")) else max(0, int(max_rows))
|
|
27825
27825
|
# Entries that deserve a human look: skipped for a reason other than the
|
|
27826
27826
|
# two bulk ones, or applied on a tie / a partial source-range overlap.
|
|
27827
27827
|
bulk = {"unmatched", "no-source-grade"}
|
|
@@ -28000,7 +28000,7 @@ def _grade_boundary_report(proj, item, p: Dict[str, Any]):
|
|
|
28000
28000
|
"color_groups": _color_group_capabilities(proj),
|
|
28001
28001
|
"gallery": _gallery_capabilities(proj),
|
|
28002
28002
|
}
|
|
28003
|
-
if p.get("include_timeline_graph", True):
|
|
28003
|
+
if _coerce_bool(p.get("include_timeline_graph"), True):
|
|
28004
28004
|
report["timeline_graph"] = _probe_color_node_graph(proj, item, {"source": "timeline", "include_nodes": False})
|
|
28005
28005
|
return report
|
|
28006
28006
|
|
|
@@ -28487,7 +28487,7 @@ def _propose_grade(proj, p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
28487
28487
|
"frame_paths": list(p.get("frame_paths") or []),
|
|
28488
28488
|
"validation": {"valid": True, "notes": []},
|
|
28489
28489
|
}
|
|
28490
|
-
if not p.get("execute"):
|
|
28490
|
+
if not _coerce_bool(p.get("execute")):
|
|
28491
28491
|
payload["executed"] = False
|
|
28492
28492
|
return payload
|
|
28493
28493
|
|
|
@@ -28730,7 +28730,7 @@ def _grade_evidence_base(proj, item, p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
28730
28730
|
# Optional coverage (best-effort; coverage_report needs a media pool item)
|
|
28731
28731
|
coverage_summary = None
|
|
28732
28732
|
coverage_warnings_count = 0
|
|
28733
|
-
if p.get("include_coverage", True):
|
|
28733
|
+
if _coerce_bool(p.get("include_coverage"), True):
|
|
28734
28734
|
mp_item = _timeline_item_media_pool_item(item)
|
|
28735
28735
|
if mp_item is not None:
|
|
28736
28736
|
try:
|
|
@@ -29298,8 +29298,8 @@ def gallery_stills(action: str, params: Optional[Dict[str, Any]] = None) -> Dict
|
|
|
29298
29298
|
return _err("folder_path is required")
|
|
29299
29299
|
prefix = p.get("prefix", "still")
|
|
29300
29300
|
fmt = p.get("format", "dpx")
|
|
29301
|
-
delete_after = p.get("delete_after", True)
|
|
29302
|
-
cleanup = p.get("cleanup", True)
|
|
29301
|
+
delete_after = _coerce_bool(p.get("delete_after"), True)
|
|
29302
|
+
cleanup = _coerce_bool(p.get("cleanup"), True)
|
|
29303
29303
|
# Redirect sandbox/temp paths that Resolve can't access
|
|
29304
29304
|
folder_path = _resolve_safe_dir(folder_path)
|
|
29305
29305
|
folder_pre_existed = os.path.isdir(folder_path)
|
|
@@ -29697,7 +29697,7 @@ def _fusion_group_settings_export(comp, p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
29697
29697
|
"path": path,
|
|
29698
29698
|
"group_name": group_name,
|
|
29699
29699
|
"parse_warning": f"saved, but summary parse failed: {exc}",
|
|
29700
|
-
**_fusion_group_advisory(p.get("include_advisory", False)),
|
|
29700
|
+
**_fusion_group_advisory(_coerce_bool(p.get("include_advisory"), False)),
|
|
29701
29701
|
}
|
|
29702
29702
|
return {
|
|
29703
29703
|
"success": True,
|
|
@@ -29705,7 +29705,7 @@ def _fusion_group_settings_export(comp, p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
29705
29705
|
"group_name": group_name,
|
|
29706
29706
|
"published_inputs": parsed["published_inputs"],
|
|
29707
29707
|
"input_count": parsed["input_count"],
|
|
29708
|
-
**_fusion_group_advisory(p.get("include_advisory", False)),
|
|
29708
|
+
**_fusion_group_advisory(_coerce_bool(p.get("include_advisory"), False)),
|
|
29709
29709
|
}
|
|
29710
29710
|
|
|
29711
29711
|
|
|
@@ -29740,7 +29740,7 @@ def _fusion_group_settings_splice_inputs(p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
29740
29740
|
"success": True,
|
|
29741
29741
|
"dest_path": summary["dest_path"],
|
|
29742
29742
|
"summary": summary,
|
|
29743
|
-
**_fusion_group_advisory(p.get("include_advisory", False)),
|
|
29743
|
+
**_fusion_group_advisory(_coerce_bool(p.get("include_advisory"), False)),
|
|
29744
29744
|
}
|
|
29745
29745
|
|
|
29746
29746
|
|
|
@@ -29848,7 +29848,7 @@ def _fusion_group_settings_load(comp, p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
29848
29848
|
"Group preserved. If Edit Controls don't refresh, select the group in "
|
|
29849
29849
|
"Fusion and use UI Load Settings to remap InstanceInput order."
|
|
29850
29850
|
),
|
|
29851
|
-
**_fusion_group_advisory(p.get("include_advisory", False)),
|
|
29851
|
+
**_fusion_group_advisory(_coerce_bool(p.get("include_advisory"), False)),
|
|
29852
29852
|
}
|
|
29853
29853
|
|
|
29854
29854
|
|
|
@@ -29965,7 +29965,7 @@ def _fusion_probe_group_published_inputs(comp, p: Dict[str, Any]) -> Dict[str, A
|
|
|
29965
29965
|
"live_inputs": live_inputs,
|
|
29966
29966
|
"live_input_count": len(live_inputs),
|
|
29967
29967
|
"file_summary": file_summary,
|
|
29968
|
-
**_fusion_group_advisory(p.get("include_advisory", False)),
|
|
29968
|
+
**_fusion_group_advisory(_coerce_bool(p.get("include_advisory"), False)),
|
|
29969
29969
|
}
|
|
29970
29970
|
|
|
29971
29971
|
|
|
@@ -30116,7 +30116,7 @@ def _fusion_comp_snapshot(comp, p: Dict[str, Any]):
|
|
|
30116
30116
|
max_tools = int(max_tools)
|
|
30117
30117
|
except (TypeError, ValueError):
|
|
30118
30118
|
max_tools = 20
|
|
30119
|
-
include_io =
|
|
30119
|
+
include_io = _coerce_bool(p.get("include_io"), False)
|
|
30120
30120
|
for idx in list(tool_list)[:max_tools]:
|
|
30121
30121
|
tools.append(_fusion_tool_summary(tool_list[idx], include_io=include_io))
|
|
30122
30122
|
return {
|
|
@@ -30166,7 +30166,7 @@ def _safe_add_fusion_tool(comp, p: Dict[str, Any]):
|
|
|
30166
30166
|
return _err(f"Failed to add tool '{tool_type}'. Check the tool ID is valid.")
|
|
30167
30167
|
if p.get("name"):
|
|
30168
30168
|
tool.SetAttrs({"TOOLS_Name": p["name"]})
|
|
30169
|
-
return _ok(tool=_fusion_tool_summary(tool, include_io=p.get("include_io", True)))
|
|
30169
|
+
return _ok(tool=_fusion_tool_summary(tool, include_io=_coerce_bool(p.get("include_io"), True)))
|
|
30170
30170
|
finally:
|
|
30171
30171
|
comp.Unlock()
|
|
30172
30172
|
|
|
@@ -30178,7 +30178,7 @@ def _probe_fusion_tool(comp, p: Dict[str, Any]):
|
|
|
30178
30178
|
tool = comp.FindTool(name)
|
|
30179
30179
|
if not tool:
|
|
30180
30180
|
return {"found": False, "tool_name": name}
|
|
30181
|
-
return {"found": True, "tool": _fusion_tool_summary(tool, include_io=p.get("include_io", True))}
|
|
30181
|
+
return {"found": True, "tool": _fusion_tool_summary(tool, include_io=_coerce_bool(p.get("include_io"), True))}
|
|
30182
30182
|
|
|
30183
30183
|
|
|
30184
30184
|
def _safe_set_fusion_inputs(comp, p: Dict[str, Any]):
|
|
@@ -30202,7 +30202,7 @@ def _safe_set_fusion_inputs(comp, p: Dict[str, Any]):
|
|
|
30202
30202
|
else:
|
|
30203
30203
|
tool.SetInput(input_name, value)
|
|
30204
30204
|
row = {"success": True}
|
|
30205
|
-
if p.get("readback", True):
|
|
30205
|
+
if _coerce_bool(p.get("readback"), True):
|
|
30206
30206
|
try:
|
|
30207
30207
|
row["value"] = _ser(tool.GetInput(input_name, p["time"])) if "time" in p else _ser(tool.GetInput(input_name))
|
|
30208
30208
|
except Exception as exc:
|
|
@@ -30238,7 +30238,7 @@ def _safe_connect_fusion_tools(comp, p: Dict[str, Any]):
|
|
|
30238
30238
|
def _fusion_boundary_report(comp, p: Dict[str, Any]):
|
|
30239
30239
|
return {
|
|
30240
30240
|
"capabilities": _fusion_graph_capabilities(comp),
|
|
30241
|
-
"composition": _fusion_comp_snapshot(comp, {**p, "include_io": p.get("include_io", True)}),
|
|
30241
|
+
"composition": _fusion_comp_snapshot(comp, {**p, "include_io": _coerce_bool(p.get("include_io"), True)}),
|
|
30242
30242
|
}
|
|
30243
30243
|
|
|
30244
30244
|
|
|
@@ -30372,7 +30372,7 @@ def _fusion_add_mask(comp, p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
30372
30372
|
|
|
30373
30373
|
x = p.get("x", -1)
|
|
30374
30374
|
y = p.get("y", -1)
|
|
30375
|
-
readback =
|
|
30375
|
+
readback = _coerce_bool(p.get("readback"), True)
|
|
30376
30376
|
|
|
30377
30377
|
# The lock covers only the STRUCTURAL half (AddTool + rename). The input
|
|
30378
30378
|
# writes below must run outside it — see _FUSION_VALUE_WRITE_NOTE: a value
|
|
@@ -30524,7 +30524,7 @@ def _fusion_set_text_plus(comp, p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
30524
30524
|
if err:
|
|
30525
30525
|
return err
|
|
30526
30526
|
input_id = p.get("input_name", "StyledText")
|
|
30527
|
-
readback =
|
|
30527
|
+
readback = _coerce_bool(p.get("readback"), True)
|
|
30528
30528
|
# No comp.Lock() around a value write — see _FUSION_VALUE_WRITE_NOTE.
|
|
30529
30529
|
try:
|
|
30530
30530
|
tool.SetInput(input_id, text)
|
|
@@ -31243,7 +31243,7 @@ def fusion_comp(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[st
|
|
|
31243
31243
|
return _ok()
|
|
31244
31244
|
|
|
31245
31245
|
elif action == "end_undo":
|
|
31246
|
-
comp.EndUndo(p.get("keep", True))
|
|
31246
|
+
comp.EndUndo(_coerce_bool(p.get("keep"), True))
|
|
31247
31247
|
return _ok()
|
|
31248
31248
|
|
|
31249
31249
|
# --- Node Layout (FlowView) ---
|
|
@@ -31495,7 +31495,7 @@ def fuse_plugin(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[st
|
|
|
31495
31495
|
d = _fuses_dir()
|
|
31496
31496
|
if not os.path.isdir(d):
|
|
31497
31497
|
return {"fuses": []}
|
|
31498
|
-
show_all =
|
|
31498
|
+
show_all = _coerce_bool(p.get("all"), False)
|
|
31499
31499
|
out = []
|
|
31500
31500
|
for fn in sorted(os.listdir(d)):
|
|
31501
31501
|
if not fn.endswith(".fuse"):
|
|
@@ -31874,7 +31874,7 @@ def dctl(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]
|
|
|
31874
31874
|
root = root if sd is None else os.path.join(root, sd)
|
|
31875
31875
|
if not os.path.isdir(root):
|
|
31876
31876
|
return {"files": []}
|
|
31877
|
-
show_all =
|
|
31877
|
+
show_all = _coerce_bool(p.get("all"), False)
|
|
31878
31878
|
out = []
|
|
31879
31879
|
for fn in sorted(os.listdir(root)):
|
|
31880
31880
|
if not fn.lower().endswith(_DCTL_VALID_EXT):
|
|
@@ -32295,7 +32295,7 @@ def _safe_install_extension(p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
32295
32295
|
if err:
|
|
32296
32296
|
return err
|
|
32297
32297
|
name = p.get("name") or _extension_template_name(extension_type, p.get("kind", "lifecycle"))
|
|
32298
|
-
invalid = _extension_safe_name(name, allow_non_mcp_name=p.get("allow_non_mcp_name", False))
|
|
32298
|
+
invalid = _extension_safe_name(name, allow_non_mcp_name=_coerce_bool(p.get("allow_non_mcp_name"), False))
|
|
32299
32299
|
if invalid:
|
|
32300
32300
|
return invalid
|
|
32301
32301
|
source = p.get("source")
|
|
@@ -32322,7 +32322,7 @@ def _safe_install_extension(p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
32322
32322
|
if not isinstance(source, str) or not source.strip():
|
|
32323
32323
|
return _err("source must be a non-empty string")
|
|
32324
32324
|
marker = {"fuse": _FUSE_MARKER, "dctl": _DCTL_MARKER, "script": _SCRIPT_MARKER}[extension_type]
|
|
32325
|
-
if p.get("require_marker", True) and not _source_has_marker(source, marker):
|
|
32325
|
+
if _coerce_bool(p.get("require_marker"), True) and not _source_has_marker(source, marker):
|
|
32326
32326
|
return _err(f"source must include {marker} unless require_marker=False")
|
|
32327
32327
|
if _coerce_bool(p.get("dry_run")):
|
|
32328
32328
|
return _ok(
|
|
@@ -32335,7 +32335,7 @@ def _safe_install_extension(p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
32335
32335
|
}),
|
|
32336
32336
|
)
|
|
32337
32337
|
if extension_type == "fuse":
|
|
32338
|
-
return fuse_plugin("install", {"name": name, "source": source, "overwrite": p.get("overwrite", False)})
|
|
32338
|
+
return fuse_plugin("install", {"name": name, "source": source, "overwrite": _coerce_bool(p.get("overwrite"), False)})
|
|
32339
32339
|
if extension_type == "dctl":
|
|
32340
32340
|
category = p.get("category") or dctl_templates.KIND_CATEGORY.get(kind, "lut")
|
|
32341
32341
|
return dctl("install", {
|
|
@@ -32344,7 +32344,7 @@ def _safe_install_extension(p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
32344
32344
|
"category": category,
|
|
32345
32345
|
"subdir": p.get("subdir"),
|
|
32346
32346
|
"ext": p.get("ext", ".dctl"),
|
|
32347
|
-
"overwrite": p.get("overwrite", False),
|
|
32347
|
+
"overwrite": _coerce_bool(p.get("overwrite"), False),
|
|
32348
32348
|
})
|
|
32349
32349
|
language = _normalize_script_language(p.get("language", options.get("language", "lua")))
|
|
32350
32350
|
category = p.get("script_category", p.get("category", "Utility"))
|
|
@@ -32362,7 +32362,7 @@ def _safe_remove_extension(p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
32362
32362
|
if err:
|
|
32363
32363
|
return err
|
|
32364
32364
|
name = p.get("name")
|
|
32365
|
-
invalid = _extension_safe_name(name, allow_non_mcp_name=p.get("allow_non_mcp_name", False))
|
|
32365
|
+
invalid = _extension_safe_name(name, allow_non_mcp_name=_coerce_bool(p.get("allow_non_mcp_name"), False))
|
|
32366
32366
|
if invalid:
|
|
32367
32367
|
return invalid
|
|
32368
32368
|
if extension_type == "fuse":
|
|
@@ -32402,7 +32402,7 @@ def _safe_remove_extension(p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
32402
32402
|
marker = _SCRIPT_MARKER
|
|
32403
32403
|
if not os.path.isfile(path):
|
|
32404
32404
|
return _err(f"No {extension_type} extension named '{name}' at {path}")
|
|
32405
|
-
if p.get("require_marker", True) and not _file_has_marker(path, marker):
|
|
32405
|
+
if _coerce_bool(p.get("require_marker"), True) and not _file_has_marker(path, marker):
|
|
32406
32406
|
return _err(f"Refusing to remove unmarked file at {path}; pass require_marker=False only if you intend this")
|
|
32407
32407
|
if _coerce_bool(p.get("dry_run")):
|
|
32408
32408
|
return _ok(would_remove=True, extension_type=extension_type, name=name, path=path)
|
|
@@ -32429,19 +32429,19 @@ def _probe_fuse_lifecycle(p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
32429
32429
|
if isinstance(source, str):
|
|
32430
32430
|
out["has_marker"] = _source_has_marker(source, _FUSE_MARKER)
|
|
32431
32431
|
out["validation"] = fuse_plugin("validate", {"source": source})
|
|
32432
|
-
if p.get("include_template_matrix"):
|
|
32432
|
+
if _coerce_bool(p.get("include_template_matrix")):
|
|
32433
32433
|
out["template_matrix"] = _extension_template_matrix()["fuse"]
|
|
32434
|
-
if p.get("install"):
|
|
32434
|
+
if _coerce_bool(p.get("install")):
|
|
32435
32435
|
install = script_plugin("safe_install_extension", {
|
|
32436
32436
|
"extension_type": "fuse",
|
|
32437
32437
|
"name": name,
|
|
32438
32438
|
"source": source,
|
|
32439
|
-
"overwrite": p.get("overwrite", True),
|
|
32439
|
+
"overwrite": _coerce_bool(p.get("overwrite"), True),
|
|
32440
32440
|
})
|
|
32441
32441
|
out["install"] = install
|
|
32442
32442
|
out["read"] = fuse_plugin("read", {"name": name}) if install.get("success") else None
|
|
32443
32443
|
out["list"] = fuse_plugin("list")
|
|
32444
|
-
if p.get("cleanup", True):
|
|
32444
|
+
if _coerce_bool(p.get("cleanup"), True):
|
|
32445
32445
|
out["remove"] = script_plugin("safe_remove_extension", {"extension_type": "fuse", "name": name})
|
|
32446
32446
|
return out
|
|
32447
32447
|
|
|
@@ -32466,29 +32466,29 @@ def _probe_dctl_lifecycle(p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
32466
32466
|
if isinstance(source, str):
|
|
32467
32467
|
out["has_marker"] = _source_has_marker(source, _DCTL_MARKER)
|
|
32468
32468
|
out["validation"] = dctl("validate", {"source": source})
|
|
32469
|
-
if p.get("include_template_matrix"):
|
|
32469
|
+
if _coerce_bool(p.get("include_template_matrix")):
|
|
32470
32470
|
out["template_matrix"] = _extension_template_matrix()["dctl"]
|
|
32471
|
-
if p.get("install"):
|
|
32471
|
+
if _coerce_bool(p.get("install")):
|
|
32472
32472
|
install = script_plugin("safe_install_extension", {
|
|
32473
32473
|
"extension_type": "dctl",
|
|
32474
32474
|
"name": name,
|
|
32475
32475
|
"source": source,
|
|
32476
32476
|
"category": category,
|
|
32477
32477
|
"subdir": subdir,
|
|
32478
|
-
"overwrite": p.get("overwrite", True),
|
|
32478
|
+
"overwrite": _coerce_bool(p.get("overwrite"), True),
|
|
32479
32479
|
})
|
|
32480
32480
|
out["install"] = install
|
|
32481
32481
|
out["read"] = dctl("read", {"name": name, "category": category, "subdir": subdir}) if install.get("success") else None
|
|
32482
32482
|
out["list"] = dctl("list", {"category": category, "subdir": subdir})
|
|
32483
|
-
if p.get("refresh_luts") and category == "lut":
|
|
32483
|
+
if _coerce_bool(p.get("refresh_luts")) and category == "lut":
|
|
32484
32484
|
out["refresh_luts"] = project_settings("refresh_luts")
|
|
32485
|
-
if p.get("cleanup", True):
|
|
32485
|
+
if _coerce_bool(p.get("cleanup"), True):
|
|
32486
32486
|
out["remove"] = script_plugin("safe_remove_extension", {"extension_type": "dctl", "name": name, "category": category, "subdir": subdir})
|
|
32487
32487
|
return out
|
|
32488
32488
|
|
|
32489
32489
|
|
|
32490
32490
|
def _probe_script_lifecycle(p: Dict[str, Any]) -> Dict[str, Any]:
|
|
32491
|
-
if p.get("execute"):
|
|
32491
|
+
if _coerce_bool(p.get("execute")):
|
|
32492
32492
|
# Refused rather than ignored: silently skipping it would report a
|
|
32493
32493
|
# lifecycle probe as complete for a step it never ran.
|
|
32494
32494
|
return _err(
|
|
@@ -32518,21 +32518,21 @@ def _probe_script_lifecycle(p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
32518
32518
|
if isinstance(source, str):
|
|
32519
32519
|
out["has_marker"] = _source_has_marker(source, _SCRIPT_MARKER)
|
|
32520
32520
|
out["validation"] = script_plugin("validate", {"source": source, "language": language})
|
|
32521
|
-
if p.get("include_template_matrix"):
|
|
32521
|
+
if _coerce_bool(p.get("include_template_matrix")):
|
|
32522
32522
|
out["template_matrix"] = _extension_template_matrix()["script"]
|
|
32523
|
-
if p.get("install"):
|
|
32523
|
+
if _coerce_bool(p.get("install")):
|
|
32524
32524
|
install = script_plugin("safe_install_extension", {
|
|
32525
32525
|
"extension_type": "script",
|
|
32526
32526
|
"name": name,
|
|
32527
32527
|
"source": source,
|
|
32528
32528
|
"category": category,
|
|
32529
32529
|
"language": language,
|
|
32530
|
-
"overwrite": p.get("overwrite", True),
|
|
32530
|
+
"overwrite": _coerce_bool(p.get("overwrite"), True),
|
|
32531
32531
|
})
|
|
32532
32532
|
out["install"] = install
|
|
32533
32533
|
out["read"] = script_plugin("read", {"name": name, "category": category, "language": language}) if install.get("success") else None
|
|
32534
32534
|
out["list"] = script_plugin("list", {"category": category, "language": language})
|
|
32535
|
-
if p.get("cleanup", True):
|
|
32535
|
+
if _coerce_bool(p.get("cleanup"), True):
|
|
32536
32536
|
out["remove"] = script_plugin("safe_remove_extension", {
|
|
32537
32537
|
"extension_type": "script",
|
|
32538
32538
|
"name": name,
|
|
@@ -32543,7 +32543,7 @@ def _probe_script_lifecycle(p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
32543
32543
|
|
|
32544
32544
|
|
|
32545
32545
|
def _extension_boundary_report(p: Dict[str, Any]) -> Dict[str, Any]:
|
|
32546
|
-
include_matrix = p.get("include_template_matrix", True)
|
|
32546
|
+
include_matrix = _coerce_bool(p.get("include_template_matrix"), True)
|
|
32547
32547
|
return {
|
|
32548
32548
|
"capabilities": _extension_capabilities(),
|
|
32549
32549
|
"refresh_restart": {
|
|
@@ -32664,7 +32664,7 @@ def script_plugin(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[
|
|
|
32664
32664
|
if language_filter and language_filter not in _SCRIPT_VALID_LANG:
|
|
32665
32665
|
return _err(f"Invalid language '{language_filter}'. "
|
|
32666
32666
|
f"Valid: {list(_SCRIPT_VALID_LANG)}; aliases: ['python', 'python3']")
|
|
32667
|
-
show_all =
|
|
32667
|
+
show_all = _coerce_bool(p.get("all"), False)
|
|
32668
32668
|
|
|
32669
32669
|
paths = get_resolve_plugin_paths()
|
|
32670
32670
|
if category:
|
|
@@ -32887,7 +32887,7 @@ def knowledge(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[str,
|
|
|
32887
32887
|
return _ok(**_knowledge_mod.get(
|
|
32888
32888
|
str(p["topic"]),
|
|
32889
32889
|
section=str(section) if section else None,
|
|
32890
|
-
inline=
|
|
32890
|
+
inline=_coerce_bool(p.get("inline"), True),
|
|
32891
32891
|
))
|
|
32892
32892
|
if action == "search":
|
|
32893
32893
|
err, _clean = _validate_params(p, {
|