davinci-resolve-mcp 2.213.1 → 2.213.3

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 CHANGED
@@ -2,6 +2,79 @@
2
2
 
3
3
  Release history for the DaVinci Resolve MCP Server. The latest release is summarized in the root README; older entries live here to keep the README focused.
4
4
 
5
+ ## What's New in v2.213.3 — the capture docstring says what is restored and what is only reset
6
+
7
+ ### Documentation
8
+
9
+ - **`_playhead_frame_render`'s docstring still said the mark range was not
10
+ readable.** Since v2.213.2 it is: `Timeline.GetMarkInOut` is read before the
11
+ capture and put back afterwards, offset into `SetRenderSettings`' absolute
12
+ frame space. The docstring now lists the three things that actually happen
13
+ on the way out — format and codec genuinely restored, the mark range
14
+ restored when one was set (whole timeline as the fallback), and TargetDir
15
+ and CustomName reset because nothing can read them back — and notes that
16
+ `GetRenderSettings` is still absent as of 21.1. It is the first thing a
17
+ caller reads to decide whether `quality="frame"` is safe against their
18
+ render setup. Contributed in #201 by @billcarroll.
19
+ - **#196 confirmed on Windows.** The reporter pulled v2.213.1 and confirms the
20
+ keyframe fix renders on Windows, which closes the one platform question the
21
+ v2.213.1 notes left open.
22
+
23
+ ### Validation
24
+
25
+ - Docstring only; no behavior changed and no Resolve run required. Full
26
+ offline suite, drift guards and the advanced Node suite green.
27
+
28
+ ## What's New in v2.213.2 — the full transcript on 21.1, and a frame capture that puts the user's mark range back in the right frame space
29
+
30
+ Both contributed by @billcarroll (#199, #200).
31
+
32
+ ### Added
33
+
34
+ - **`media_pool_item get_transcription` reads the whole transcript on Resolve
35
+ 21.1+** through `MediaPoolItem.GetTranscription`, which 21.1 added and which
36
+ does not truncate: `segments` carries `{start, end, text, speaker}` in
37
+ **source** timecode, `language` is reported, and `truncated` is False. Pass
38
+ `include_words` to keep each segment's per-word timings, which are several
39
+ times the bulk of the text. On 21.0.x it falls back to the `Transcription`
40
+ clip property exactly as before, and `source` says which route ran. The
41
+ method is registered in the version ledger as a reported 21.1 surface, so
42
+ `check_version_support` answers for it. (#199)
43
+
44
+ ### Fixed
45
+
46
+ - **`timeline_frame capture` flattened a user's mark range to the whole
47
+ timeline.** Rendering one frame pins the project's render range to that
48
+ frame, and the cleanup could only reset it to the whole timeline because
49
+ there is no `GetRenderSettings` to read the previous range from. The mark
50
+ range is the exception: `Timeline.GetMarkInOut` can be read before the
51
+ capture, and the user's own range now goes back afterwards. A half-set range
52
+ (in point only) is still treated as no range, and with no marks set the old
53
+ whole-timeline fallback applies. (#200)
54
+ - **Adapted on landing: the two calls do not share a frame space.** Resolve
55
+ documents `GetMarkInOut` relative to the timeline start (its own example is
56
+ `in: 0, out: 134`), while `SetRenderSettings` takes absolute record frames —
57
+ measured on Studio 19.1.3.7: on an 86400-start timeline `MarkIn=MarkOut=86420`
58
+ rendered frame 20 and `MarkIn=MarkOut=20` was silently clamped to the start
59
+ and rendered frame 0, one frame, no error. Handed back verbatim, a UI-set
60
+ range would have been "restored" as a clamped range with every readback
61
+ agreeing. A mark below the timeline start is now offset by the start frame;
62
+ one at or above it was written absolute (`SetMarkInOut` stores whatever it
63
+ is given) and is kept. A unit test covers the relative case alongside the
64
+ PR's absolute, half-set and unreadable cases.
65
+ - **The clamp is now in the API ledger** as a measured bug, with the
66
+ relative-vs-absolute trap and the remedy, and
67
+ `docs/reference/api-limitations.md` is regenerated.
68
+
69
+ ### Validation
70
+
71
+ - Both PRs' unit tests plus the relative-range test. Mark-range frame space
72
+ measured live on Studio 19.1.3.7 by rendering single frames under both
73
+ interpretations and matching each against the source frames. The 21.1
74
+ transcript route cannot be exercised here (no 21.1 build); its ledger entry
75
+ and `api_truth` say so. Full offline Python suite, drift guards and the
76
+ advanced Node suite green.
77
+
5
78
  ## What's New in v2.213.1 — Fusion keyframes reach the render; the contact sheet waits for the viewer; the ledger learns Resolve 21.1
6
79
 
7
80
  Reported in #196 by @JosephConroy93, with a repro precise enough to reproduce
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  English | [简体中文](README.zh-CN.md)
4
4
 
5
- [![Version](https://img.shields.io/badge/version-2.213.1-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
5
+ [![Version](https://img.shields.io/badge/version-2.213.3-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
6
6
  [![npm](https://img.shields.io/npm/v/davinci-resolve-mcp.svg?label=npm&color=CB3837)](https://www.npmjs.com/package/davinci-resolve-mcp)
7
7
  [![API Coverage](https://img.shields.io/badge/API%20Coverage-100%25-brightgreen.svg)](docs/reference/api-coverage.md)
8
8
  [![Tools](https://img.shields.io/badge/MCP%20Tools-36%20(353%20full)-blue.svg)](#server-modes)
package/README.zh-CN.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [English](README.md) | 简体中文
4
4
 
5
- [![Version](https://img.shields.io/badge/version-2.213.1-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
5
+ [![Version](https://img.shields.io/badge/version-2.213.3-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
6
6
  [![npm](https://img.shields.io/npm/v/davinci-resolve-mcp.svg?label=npm&color=CB3837)](https://www.npmjs.com/package/davinci-resolve-mcp)
7
7
  [![API Coverage](https://img.shields.io/badge/API%20Coverage-100%25-brightgreen.svg)](docs/reference/api-coverage.md)
8
8
  [![Tools](https://img.shields.io/badge/MCP%20Tools-36%20(353%20full)-blue.svg)](#服务器模式)
@@ -12,7 +12,7 @@
12
12
  [![Python](https://img.shields.io/badge/python-3.10+-green.svg)](https://www.python.org/downloads/)
13
13
  [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT)
14
14
 
15
- > 本翻译对应 v2.213.1 版 README。如与英文原版有出入,以 [英文原版](README.md) 为准。
15
+ > 本翻译对应 v2.213.3 版 README。如与英文原版有出入,以 [英文原版](README.md) 为准。
16
16
 
17
17
  一个 Model Context Protocol (MCP) 服务器,让 AI 助手通过官方脚本 API 控制 DaVinci Resolve Studio(达芬奇)。它提供完整的 API 覆盖,外加带护栏的工作流助手,涵盖剪辑、媒体池整理、渲染设置、审阅标记、调色、Fusion、Fairlight、项目生命周期任务、扩展开发,以及不碰源媒体的媒体分析。
18
18
 
package/docs/SKILL.md CHANGED
@@ -909,8 +909,12 @@ Key actions: `get_name`, `get_metadata(key?)`, `set_metadata(key, value)`,
909
909
  `set_name(name)`, `link_full_resolution_media(path)`,
910
910
  `replace_clip_preserve_sub_clip(path)`, `monitor_growing_file`,
911
911
  `transcribe_audio(use_speaker_detection?)`, `clear_transcription`,
912
- `get_transcription` (read back `{text, truncated, status, has_transcription}`;
913
- `truncated` flags when Resolve's preview cut the text off),
912
+ `get_transcription(include_words?, use_nested_clip_transcription?)` (read back
913
+ `{text, segments, language, source, truncated, status, has_transcription}`; on
914
+ Resolve 21.1+ it uses `MediaPoolItem.GetTranscription`, so `segments` carries
915
+ `{start, end, text, speaker}` in SOURCE timecode and nothing is truncated, and
916
+ on 21.0.x it falls back to the `Transcription` clip property, where `truncated`
917
+ flags a cut-off preview — `source` says which route ran),
914
918
  `perform_audio_classification`,
915
919
  `analyze_for_intellisearch(identify_faces?, is_better_mode?)`, `analyze_for_slate(marker_color?)`,
916
920
  `remove_motion_blur(deblur_option?)` (Resolve 21+; AI Extras / confirm-token gated as noted above),
@@ -12,7 +12,7 @@ that none exists).
12
12
 
13
13
  **Verified on:** DaVinci Resolve Studio 21.0.2
14
14
 
15
- **Totals:** 41 missing capabilities, 50 bugs / unreliable behaviors.
15
+ **Totals:** 41 missing capabilities, 51 bugs / unreliable behaviors.
16
16
 
17
17
  The authoritative source is the runtime-queryable `api_truth` ledger
18
18
  (`resolve_control api_truth "<query>"`); this document is generated from
@@ -541,6 +541,13 @@ values, or automation-hostile modal prompts.
541
541
  - **Workaround / current handling:** Detect the edition BEFORE calling Studio-gated features rather than discovering the gate by tripping it: the product name is 'DaVinci Resolve' on free and 'DaVinci Resolve Studio' on Studio (resolve_control get_version reports it). If a Studio-only call has already returned False on a free build, treat every following failure as suspect: re-run a known-good read, and if that fails too, a modal is blocking and only a human can dismiss it — no API closes it. Known Studio-gated so far: subtitle generation from audio, and audio transcription.
542
542
  - **Tags:** free-edition, studio-only, silent-failure, modal, ai, subtitle, transcription
543
543
 
544
+ ### SetRenderSettings MarkIn/MarkOut below the timeline start are clamped, not refused
545
+
546
+ - **Object:** `Project / Timeline`
547
+ - **Behavior:** SetRenderSettings takes MarkIn/MarkOut as ABSOLUTE record frames, and a value below the timeline's start frame is silently clamped to the start: measured on Studio 19.1.3.7 (2026-09-08) on an 86400-start timeline, MarkIn=MarkOut=86420 rendered timeline frame 20 and MarkIn=MarkOut=20 rendered frame 0 — one frame, True from SetRenderSettings, no error anywhere. The trap is that Timeline.GetMarkInOut reports the user's marks RELATIVE to the timeline start (Blackmagic's own README example is {'in': 0, 'out': 134}; the 21.1 stub says 'record frame relative to timeline start'), so feeding its output straight into SetRenderSettings renders the wrong range with every readback agreeing. SetMarkInOut itself stores whatever number it is given (10 reads back 10, 86410 reads back 86410), so a script-written range can be in either space.
548
+ - **Workaround / current handling:** Offset GetMarkInOut values by Timeline.GetStartFrame() before passing them to SetRenderSettings when they fall below the start frame (timeline_frame capture does this when it puts a user's range back). Verify a render range from the delivered frames, never from the settings call's return.
549
+ - **Tags:** render, silent-failure, frame-space, mark-range
550
+
544
551
  ### SetRenderSettings ExportSubtitle / SubtitleFormat had no observable effect
545
552
 
546
553
  - **Object:** `Project (render settings)`
package/install.py CHANGED
@@ -37,7 +37,7 @@ from src.utils.update_check import (
37
37
 
38
38
  # ─── Version ──────────────────────────────────────────────────────────────────
39
39
 
40
- VERSION = "2.213.1"
40
+ VERSION = "2.213.3"
41
41
  # Only hard floor: mcp[cli] requires Python 3.10+. There is no upper bound —
42
42
  # Resolve's scripting bridge loads into newer interpreters on recent builds
43
43
  # (Python 3.14 verified against Resolve Studio 20.3.2). Older Resolve builds
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "davinci-resolve-mcp",
3
- "version": "2.213.1",
3
+ "version": "2.213.3",
4
4
  "description": "NPM bootstrapper for the DaVinci Resolve MCP Server.",
5
5
  "license": "MIT",
6
6
  "author": "Samuel Gursky <samgursky@gmail.com>",
@@ -87,7 +87,7 @@ if not logging.getLogger().handlers:
87
87
  handlers=[logging.StreamHandler()],
88
88
  )
89
89
 
90
- VERSION = "2.213.1"
90
+ VERSION = "2.213.3"
91
91
  logger = logging.getLogger("davinci-resolve-mcp")
92
92
  logger.info(f"Starting DaVinci Resolve MCP Server v{VERSION}")
93
93
  logger.info(f"Detected platform: {get_platform()}")
package/src/server.py CHANGED
@@ -11,7 +11,7 @@ Usage:
11
11
  python src/server.py --full # Start the 353-tool granular server instead
12
12
  """
13
13
 
14
- VERSION = "2.213.1"
14
+ VERSION = "2.213.3"
15
15
 
16
16
  import base64
17
17
  import os
@@ -14866,12 +14866,18 @@ def _playhead_frame_render(proj, tl, p: Dict[str, Any]):
14866
14866
  A single-frame render honours the grade, Fusion and titles, is frame-exact,
14867
14867
  runs in well under a second, and needs no GUI panel or foreground window.
14868
14868
 
14869
- The cost is that render settings are project-level state. Format and codec
14870
- are readable and are restored; the rest (TargetDir, CustomName, mark range)
14871
- is NOT readable on builds without GetRenderSettings, so this resets those to
14872
- sane values rather than truly restoring them. Callers who need a strictly
14873
- side-effect-free read should use quality="thumbnail" and accept per-clip
14874
- granularity.
14869
+ The cost is that render settings are project-level state, and there is still
14870
+ no GetRenderSettings to read them back from (absent as of 21.1). Three
14871
+ different things happen on the way out:
14872
+ - Format and codec are readable via GetCurrentRenderFormatAndCodec and are
14873
+ genuinely restored.
14874
+ - The mark range is readable via Timeline.GetMarkInOut, so a range the
14875
+ caller had set is put back (offset into SetRenderSettings' absolute
14876
+ frame space); with no range set it falls back to the whole timeline.
14877
+ - TargetDir and CustomName are readable from nowhere, so they are reset to
14878
+ sane values rather than restored.
14879
+ Callers who need a strictly side-effect-free read should use
14880
+ quality="thumbnail" and accept per-clip granularity.
14875
14881
  """
14876
14882
  fmt = str(p.get("format", "jpg")).lower().lstrip(".")
14877
14883
  if fmt == "jpeg":
@@ -14934,6 +14940,40 @@ def _playhead_frame_render(proj, tl, p: Dict[str, Any]):
14934
14940
  original_tc = tl.GetCurrentTimecode()
14935
14941
  except Exception:
14936
14942
  pass
14943
+ # The capture pins the render range to the captured frame, and there is no
14944
+ # GetRenderSettings to read the surrounding settings back from (still absent
14945
+ # in 21.1). The mark range is the exception: Timeline.GetMarkInOut reports it
14946
+ # in the same record-frame space SetRenderSettings takes, so a range the user
14947
+ # set can be captured here and put back below instead of being flattened to
14948
+ # the whole timeline.
14949
+ original_marks = None
14950
+ try:
14951
+ marks = (tl.GetMarkInOut() or {}).get("video") or {}
14952
+ if "in" in marks and "out" in marks:
14953
+ original_marks = marks
14954
+ except Exception:
14955
+ pass
14956
+ # GetMarkInOut reports marks RELATIVE to the timeline start (Blackmagic's
14957
+ # own example is {'in': 0, 'out': 134}; the 21.1 stub says "record frame
14958
+ # relative to timeline start"), while SetRenderSettings MarkIn/MarkOut are
14959
+ # ABSOLUTE record frames — measured on Studio 19.1.3.7: on an 86400-start
14960
+ # timeline MarkIn=MarkOut=86420 rendered frame 20, and MarkIn=MarkOut=20 was
14961
+ # silently clamped to the start and rendered frame 0, one frame, no error.
14962
+ # Handing a relative range straight back would "restore" a clamped range
14963
+ # with no signal. A mark below the timeline start is therefore relative and
14964
+ # is offset; one at or above it was written absolute (SetMarkInOut stores
14965
+ # whatever it is given) and is kept as-is.
14966
+ if original_marks:
14967
+ try:
14968
+ tl_start = int(round(float(tl.GetStartFrame())))
14969
+ original_marks = {
14970
+ key: (int(original_marks[key]) + tl_start
14971
+ if int(original_marks[key]) < tl_start
14972
+ else int(original_marks[key]))
14973
+ for key in ("in", "out")
14974
+ }
14975
+ except Exception:
14976
+ original_marks = None
14937
14977
 
14938
14978
  job = None
14939
14979
  try:
@@ -15027,16 +15067,28 @@ def _playhead_frame_render(proj, tl, p: Dict[str, Any]):
15027
15067
  original_fc.get("format"), original_fc.get("codec"),
15028
15068
  restore_exc or "SetCurrentRenderFormatAndCodec returned False",
15029
15069
  )
15030
- # Best-effort, not a restore: without GetRenderSettings there is nothing
15031
- # to restore FROM, so put the mark range back to the whole timeline
15032
- # rather than leaving it pinned to the captured frame.
15070
+ # Still not a full restore GetRenderSettings does not exist, so the
15071
+ # other settings cannot be read back. The mark range can: put the user's
15072
+ # own range back when they had one, and fall back to the whole timeline
15073
+ # when they did not, so the range is never left pinned to the captured
15074
+ # frame for the next render job to inherit.
15075
+ # (original_marks is already in SetRenderSettings' absolute space — see
15076
+ # the offset above.)
15033
15077
  try:
15034
- proj.SetRenderSettings({
15035
- "SelectAllFrames": True,
15036
- "MarkIn": tl.GetStartFrame(),
15037
- "MarkOut": tl.GetEndFrame(),
15038
- "CustomName": "",
15039
- })
15078
+ if original_marks:
15079
+ restored_marks = {
15080
+ "SelectAllFrames": False,
15081
+ "MarkIn": original_marks["in"],
15082
+ "MarkOut": original_marks["out"],
15083
+ }
15084
+ else:
15085
+ restored_marks = {
15086
+ "SelectAllFrames": True,
15087
+ "MarkIn": tl.GetStartFrame(),
15088
+ "MarkOut": tl.GetEndFrame(),
15089
+ }
15090
+ restored_marks["CustomName"] = ""
15091
+ proj.SetRenderSettings(restored_marks)
15040
15092
  except Exception:
15041
15093
  pass
15042
15094
  try:
@@ -21312,11 +21364,15 @@ def media_pool_item(action: str, params: Optional[Dict[str, Any]] = None) -> Dic
21312
21364
  get_unique_id(clip_id) -> {id}
21313
21365
  transcribe_audio(clip_id, use_speaker_detection?, background?) -> {success | job_id} — use_speaker_detection is Resolve 21+; background=true returns a job_id (poll resolve_control job_status)
21314
21366
  clear_transcription(clip_id) -> {success}
21315
- get_transcription(clip_id) -> {text, truncated, status, has_transcription}
21316
- Read a clip's transcription. `truncated` flags when Resolve's preview
21317
- property cut the text off (the full transcript is longer). Clip-level and
21318
- separate from the timeline subtitle transcript (timeline.get_transcript)
21319
- that propose_cuts uses.
21367
+ get_transcription(clip_id, include_words?, use_nested_clip_transcription?) -> {text, segments, language, source, truncated, status, has_transcription}
21368
+ Read a clip's transcription. On Resolve 21.1+ this uses
21369
+ MediaPoolItem.GetTranscription, which does not truncate: `segments`
21370
+ carries {start, end, text, speaker} in SOURCE timecode and `truncated`
21371
+ is False. Pass include_words=true to keep each segment's per-word
21372
+ timings. On 21.0.x it falls back to the `Transcription` clip property,
21373
+ `segments` is null, and `truncated` flags a cut-off preview; `source`
21374
+ says which route ran. Clip-level and separate from the timeline subtitle
21375
+ transcript (timeline.get_transcript) that propose_cuts uses.
21320
21376
  extract_frames(clip_id, timestamps, output_dir?) -> {frame_paths, output_dir, count, errors}
21321
21377
  Extract still JPEGs from the clip's source at the given timestamps (seconds)
21322
21378
  via ffmpeg. Source-safe: reads source, writes only to a scratch dir.
@@ -21569,13 +21625,45 @@ def media_pool_item(action: str, params: Optional[Dict[str, Any]] = None) -> Dic
21569
21625
  status = clip.GetClipProperty("Transcription Status")
21570
21626
  except Exception:
21571
21627
  status = None
21572
- return {
21628
+ out = {
21573
21629
  "clip_id": p.get("clip_id"),
21574
21630
  "text": text,
21575
21631
  "truncated": _is_truncated(text),
21576
21632
  "status": status or None,
21577
21633
  "has_transcription": bool(text.strip()),
21634
+ "segments": None,
21635
+ "language": None,
21636
+ "source": "clip_property",
21578
21637
  }
21638
+ # The `Transcription` clip property is a preview and stops at an
21639
+ # ellipsis. Resolve 21.1 added a real accessor that does not truncate,
21640
+ # so prefer it and keep the property as the 21.0.x fallback. Verified on
21641
+ # Studio 21.1.0.14: 1550 segments with per-word start/end timecodes.
21642
+ # Segment timecodes are SOURCE timecodes, not timeline positions.
21643
+ if _has_method(clip, "GetTranscription"):
21644
+ try:
21645
+ full = clip.GetTranscription(bool(p.get("use_nested_clip_transcription", False)))
21646
+ except Exception:
21647
+ full = None
21648
+ segs = full.get("segments") if isinstance(full, dict) else None
21649
+ if segs:
21650
+ if not p.get("include_words"):
21651
+ # `words` is several times the bulk of the segment text and
21652
+ # most callers want segment-level timing. Opt in for it.
21653
+ segs = [{k: v for k, v in seg.items() if k != "words"}
21654
+ if isinstance(seg, dict) else seg for seg in segs]
21655
+ joined = " ".join(seg.get("text", "") for seg in segs
21656
+ if isinstance(seg, dict)).strip()
21657
+ out.update({
21658
+ "segments": segs,
21659
+ "language": full.get("language"),
21660
+ "source": "get_transcription",
21661
+ "truncated": False,
21662
+ })
21663
+ if joined:
21664
+ out["text"] = joined
21665
+ out["has_transcription"] = True
21666
+ return out
21579
21667
  elif action == "extract_frames":
21580
21668
  return _extract_clip_frames(clip, p)
21581
21669
  elif action == "perform_audio_classification":
@@ -1495,6 +1495,31 @@ API_TRUTH: List[Dict[str, Any]] = [
1495
1495
  "subtitle", "transcription"],
1496
1496
  "submit": "bug",
1497
1497
  },
1498
+ {
1499
+ "symbol": "SetRenderSettings MarkIn/MarkOut below the timeline start are clamped, not refused",
1500
+ "object": "Project / Timeline",
1501
+ "reality": "SetRenderSettings takes MarkIn/MarkOut as ABSOLUTE record "
1502
+ "frames, and a value below the timeline's start frame is "
1503
+ "silently clamped to the start: measured on Studio 19.1.3.7 "
1504
+ "(2026-09-08) on an 86400-start timeline, MarkIn=MarkOut=86420 "
1505
+ "rendered timeline frame 20 and MarkIn=MarkOut=20 rendered "
1506
+ "frame 0 — one frame, True from SetRenderSettings, no error "
1507
+ "anywhere. The trap is that Timeline.GetMarkInOut reports the "
1508
+ "user's marks RELATIVE to the timeline start (Blackmagic's own "
1509
+ "README example is {'in': 0, 'out': 134}; the 21.1 stub says "
1510
+ "'record frame relative to timeline start'), so feeding its "
1511
+ "output straight into SetRenderSettings renders the wrong range "
1512
+ "with every readback agreeing. SetMarkInOut itself stores "
1513
+ "whatever number it is given (10 reads back 10, 86410 reads "
1514
+ "back 86410), so a script-written range can be in either space.",
1515
+ "recommended": "Offset GetMarkInOut values by Timeline.GetStartFrame() before "
1516
+ "passing them to SetRenderSettings when they fall below the "
1517
+ "start frame (timeline_frame capture does this when it puts "
1518
+ "a user's range back). Verify a render range from the "
1519
+ "delivered frames, never from the settings call's return.",
1520
+ "tags": ["render", "silent-failure", "frame-space", "mark-range"],
1521
+ "submit": "bug",
1522
+ },
1498
1523
  {
1499
1524
  "symbol": "SetRenderSettings ExportSubtitle / SubtitleFormat had no observable effect",
1500
1525
  "object": "Project (render settings)",
@@ -165,6 +165,18 @@ _EVIDENCE_GATES: List[Dict[str, Any]] = [
165
165
  "note": "Reported against 21.0.4.5. Not reachable through this server yet.",
166
166
  "issue": 131,
167
167
  },
168
+ {
169
+ "symbol": "MediaPoolItem.GetTranscription",
170
+ "introduced_in": "21.1",
171
+ "source": "reported",
172
+ "note": "Reported against Studio 21.1.0.14 (PR #199): the untruncated "
173
+ "transcript, {language, segments[{start, end, text, speaker, "
174
+ "words[]}]} in SOURCE timecode. media_pool_item get_transcription "
175
+ "prefers it when present and falls back to the truncated "
176
+ "'Transcription' clip property below 21.1; `source` in the "
177
+ "result says which route ran.",
178
+ "issue": 199,
179
+ },
168
180
  {
169
181
  "symbol": "Project.SetRenderSettings UseFullExtents",
170
182
  "introduced_in": "21.0.4",