01 / BEFORE THE CODE
Set the conditions before you run.
Connection already checked
Use external local Python with Resolve Studio running. Complete the Resolve connection guide first. These examples use Python 3.8+ syntax; interpreter/native-library compatibility is a separate requirement.
An explicit reading/writing scope
Open the intended test project and select a Media Pool folder manually. Reported IDs matter more than matching display names. Do not switch projects, bins or timelines while the script runs. It does not search every project folder or choose a similar clip.
A snapshot, not an edit
The script does not import or relink files. JSON may include full paths, names and metadata: redact it before sharing. A partial report is explicitly flagged and exits with code 2.
Current Studio edition guidance and the installed scripting documentation take precedence over older console or Free-edition tutorials.[1][4][3]
02 / API CONTRACT
Which object. Which method. Which result.
GetTrackCount(trackType)- Input
- video or audio in this example.
- Returns
- Track count for the selected type.
- Check
- Index from 1 through the observed count.
GetItemListInTrack(type, index)- Input
- One existing track.
- Returns
- TimelineItem list.
- Check
- None is not an empty list.
GetMediaPoolItem()- Input
- No arguments.
- Returns
- MediaPoolItem when the link exists.
- Check
- Report an absent link separately.
GetClipProperty()- Input
- No arguments; then inspect the File Path key.
- Returns
- Source property dictionary.
- Check
- A path is neither a full dependency graph nor proof of decoding.
Resolve method contracts are documented by Blackmagic; Python and author helpers are labelled separately. The defensive checks are our example design. A non-empty object is not proof of full API compatibility.[3]
03 / UNDERSTAND
Three distinct entities in a short chain
TimelineItem is a particular occurrence on a track. GetMediaPoolItem returns the linked Media Pool entry when available. GetClipProperty on that entry provides a property dictionary in which the stored File Path can be examined. Neither occurrence name nor source name is used to reconstruct a path.
One MediaPoolItem may appear on V1 several times, so repeated paths can be expected. Do not drop those rows as duplicates without considering the report’s purpose. A source inventory may use a separate table keyed by Media Pool ID, while edit inspection must retain occurrences.[3]
04 / UNDERSTAND
Empty path, missing property and read failure are different
no_media_pool_item means no direct linked Media Pool entry. path_property_missing means the key was absent from a successfully read dictionary. path_empty means the key contained an empty string; path_value_unexpected_type flags another type. read_error means reading failed. A universal empty string would conceal these distinctions.
For stored_path, the original spelling is retained rather than normalized or translated between operating systems for appearance. The example does not claim that one field fully enumerates dependencies for compound clips, multicam, nested timelines, generators, sequences or synchronized audio.[3]
05 / UNDERSTAND
A stored reference does not establish media availability
Filesystem probing is off by default. --check-files performs a local stat on a native absolute path, distinguishing a regular file, absence at probe time, a non-file and an unavailable probe. Accessing network paths can block; these native/filesystem calls have no tutorial timeout.
regular_file_observed only records a filesystem observation at that moment. It does not establish that Resolve decoded the file, is using it instead of a proxy/cache, or has the same access rights. not_found never triggers automatic relink; inspect the volume, mount point and original project environment first.[8][7][3]
06 / UNDERSTAND
One track is an explicit scope, not a project archive
The default is video:1; use --track-type audio for audio explicitly. The report includes the first limit API-list entries and a truncation flag. Track index is checked against GetTrackCount: a nonexistent track raises, while an existing empty track produces a zero-row report.
This diagnoses direct references on one track; it is neither a complete project-dependency collector nor a replacement for archiving. Full paths may expose usernames, clients and network shares. Redact them manually before sharing; the script does not send them to the website or copy source media.[3]
PYTHON / COMPLETE EXAMPLE
Read the code. Then run it deliberately.
Download source_media_paths.py, resolve_api_media.py and resolve_api_connection.py. The bundle includes all six examples, both helpers and RU/EN instructions. Both helpers are fully visible below; read them before --apply.
Download all examples + instructions (.zip) ↓"""Read stored source paths on ONE chosen timeline track, without relinking.
A stored File Path is not a guarantee of existence, decoding or active proxy usage.
Optional filesystem probes may block on disconnected network storage.
"""
import argparse
from pathlib import Path
from resolve_api_connection import connect
from resolve_api_media import context, api_list, uid, properties, path_key, ensure_current, output
def source_row(item, check_files=False):
row = {"timeline_item_id": uid(item), "name": item.GetName()}
media = item.GetMediaPoolItem()
if not media:
return dict(row, status="no_media_pool_item")
row["media_pool_id"] = uid(media)
raw = properties(media)
if "File Path" not in raw:
return dict(row, status="path_property_missing")
path = raw["File Path"]
if not isinstance(path, str):
return dict(row, status="path_value_unexpected_type")
if not path:
return dict(row, status="path_empty")
row.update(status="stored_path", path=path, filesystem="not_checked")
if check_files:
try:
p = Path(path_key(path))
# stat distinguishes an absent path from many access/I/O errors.
import stat
row["filesystem"] = "regular_file_observed" if stat.S_ISREG(p.stat().st_mode) else "not_regular_file"
except FileNotFoundError:
row["filesystem"] = "not_found_at_probe_time"
except (ValueError, OSError) as exc:
row.update(filesystem="probe_unavailable", probe_error_type=type(exc).__name__)
return row
def inspect_track(ctx, kind="video", index=1, limit=100, check_files=False):
if kind not in ("video", "audio") or type(index) is not int or index < 1 or not 1 <= limit <= 500:
raise ValueError("Use video/audio, a 1-based track index and limit 1..500")
count = ctx.timeline.GetTrackCount(kind)
if type(count) is not int or index > count:
raise ValueError("Chosen track does not exist")
items = api_list(ctx.timeline.GetItemListInTrack(kind, index), "Track items")
rows = []
for item in items[:limit]:
try:
rows.append(source_row(item, check_files))
except Exception as exc:
rows.append({"status": "read_error", "error_type": type(exc).__name__})
ensure_current(ctx)
return {"status": "track_report", "target": ctx.target, "track_type": kind, "track_index": index,
"track_item_count": len(items), "truncated": len(items) > limit,
"item_errors": sum(r["status"] == "read_error" for r in rows), "items": rows}
def main(argv=None):
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--track-type", choices=("video", "audio"), default="video")
parser.add_argument("--track", type=int, default=1)
parser.add_argument("--limit", type=int, default=100)
parser.add_argument("--check-files", action="store_true", help="Explicitly probe native filesystem; may block on network storage")
args = parser.parse_args(argv)
report = inspect_track(context(connect(), need_timeline=True), args.track_type, args.track, args.limit, args.check_files)
output(report)
return 2 if report["item_errors"] or report["truncated"] else 0
if __name__ == "__main__":
raise SystemExit(main())
Media helper — targeting, mapping and write checks
The short main script calls this shared implementation. This is our tutorial code, not the official SDK. --apply permits only the selected phase. Checks are not atomic locks, process isolation or an automatic recovery system.
"""Shared tutorial helpers. Local desktop only; no sandbox, lock or API timeout.
A native call may block. Save a test copy yourself and do not edit concurrently.
Paths in reports can be sensitive. No relink, delete, retry or rollback is performed.
Python 3.8+ syntax; live Resolve/Python compatibility is a separate check.
"""
import hashlib
import json
import math
import os
import re
from dataclasses import dataclass
from pathlib import Path
from resolve_api_connection import project_manager
MAX_ITEMS = 500 # Tutorial processing limit, NOT a Resolve limit.
class OutcomeUnknown(RuntimeError):
"""A modifying call was attempted. Inspect actual state before any retry."""
def uid(obj):
if obj is None:
raise RuntimeError("Required API object is absent")
value = obj.GetUniqueId()
if not isinstance(value, str) or not value.strip():
raise RuntimeError("Object identity is unavailable")
return value
def api_list(value, label):
# Do not turn an unavailable API result (None) into an empty collection.
if not isinstance(value, list) or len(value) > MAX_ITEMS:
raise RuntimeError(label + ": expected a list of at most 500 entries")
return value
def properties(item):
result = item.GetClipProperty()
if not isinstance(result, dict) or not all(isinstance(k, str) for k in result):
raise RuntimeError("GetClipProperty did not return a property dictionary")
return result
def path_key(raw):
"""Conservative native path key. Never lowercase or translate another OS path."""
if not isinstance(raw, str) or not raw or any(ord(c) < 32 for c in raw):
raise ValueError("A non-empty path without control characters is required")
if not os.path.isabs(raw):
raise ValueError("An absolute path in this operating system is required")
# Collapsing '..' through a symlink can point at another file: reject instead.
parts = raw.replace(os.altsep, os.sep).split(os.sep) if os.altsep else raw.split(os.sep)
if ".." in parts:
raise ValueError("Resolve parent-directory segments explicitly before using this tutorial")
return os.path.normpath(raw) # Retain case and drive/share identity.
def same_path(left, right):
left, right = path_key(left), path_key(right)
if left == right:
return True
try:
# Resolve aliases only when the local filesystem can prove identity.
return os.path.samefile(left, right)
except OSError:
return False # Unknown equivalence is not a filename-based match.
def prepare_files(raw_paths):
if not isinstance(raw_paths, list) or not 1 <= len(raw_paths) <= MAX_ITEMS:
raise ValueError("Provide 1-500 file paths")
paths, stamps = [], []
for raw in raw_paths:
path = path_key(str(Path(raw).expanduser()))
if not Path(path).is_file():
raise ValueError("Not a readable ordinary file: " + path)
with open(path, "rb"):
pass # Permission check only; not codec/decode validation.
if any(same_path(path, existing) for existing in paths):
raise ValueError("Duplicate input file/alias: " + path)
stat = os.stat(path)
paths.append(path)
stamps.append([path, stat.st_dev, stat.st_ino, stat.st_size, stat.st_mtime_ns])
digest = hashlib.sha256(json.dumps(stamps, ensure_ascii=False, separators=(",", ":")).encode()).hexdigest()
return paths, digest # Metadata digest, NOT a content checksum or a lock.
def folder_files(raw_folder, extensions):
folder = Path(path_key(str(Path(raw_folder).expanduser())))
if not folder.is_dir():
raise ValueError("Source folder does not exist")
ext = {x.strip().lower().lstrip(".") for x in extensions.split(",") if x.strip()}
if not ext or not all(re.fullmatch(r"[a-z0-9]+", x) for x in ext):
raise ValueError("Use explicit comma-separated extensions, e.g. mov,mp4")
# Read one folder only. No recursion, symlink files, sequences or hidden entries.
entries = list(folder.iterdir())
if len(entries) > MAX_ITEMS:
raise ValueError("Folder exceeds tutorial limit of 500 entries")
candidates = [x for x in entries if not x.is_symlink() and x.is_file()
and not x.name.startswith(".") and x.suffix.lower().lstrip(".") in ext]
def natural_key(p):
parts = re.split(r"(\d+)", p.name.casefold())
return (tuple((1, int(x)) if x.isdigit() else (0, x) for x in parts), p.name)
return prepare_files([str(x) for x in sorted(candidates, key=natural_key)])
@dataclass
class Context:
resolve: object
manager: object
project: object
pool: object
folder: object
timeline: object
target: dict
def context(resolve, need_timeline=False):
manager = project_manager(resolve)
project = manager.GetCurrentProject()
if not project:
raise RuntimeError("Open a test project manually")
pool = project.GetMediaPool()
folder = pool.GetCurrentFolder() if pool else None
if not pool or not folder:
raise RuntimeError("Current Media Pool folder is unavailable")
timeline = project.GetCurrentTimeline() if need_timeline else None
target = {"project_id": uid(project), "folder_id": uid(folder)}
if need_timeline:
if not timeline:
raise RuntimeError("Open a test timeline manually")
target["timeline_id"] = uid(timeline)
return Context(resolve, manager, project, pool, folder, timeline, target)
def ensure_current(ctx):
current = context(ctx.resolve, ctx.timeline is not None)
if current.target != ctx.target:
raise RuntimeError("Active project/folder/timeline changed")
def expect_target(ctx, expected):
if not isinstance(expected, dict) or any(expected.get(k) != v for k, v in ctx.target.items()):
raise ValueError("--apply requires all exact target IDs from the reviewed plan")
ensure_current(ctx)
def folder_items(ctx):
entries = api_list(ctx.folder.GetClipList(), "Folder.GetClipList")
ids = [uid(x) for x in entries]
if len(ids) != len(set(ids)):
raise RuntimeError("Duplicate object IDs in folder result")
return entries
def order_by_paths(paths, items, allow_other=False):
"""One-to-one matching by path/file identity. Missing/ambiguous => stop."""
items = api_list(items, "MediaPoolItem list")
observed = []
for item in items:
props = properties(item)
stored = props.get("File Path")
observed.append((uid(item), item, stored if isinstance(stored, str) and stored else None))
if len({x[0] for x in observed}) != len(observed):
raise RuntimeError("Duplicate MediaPoolItem identity")
result, selected = [], set()
for path in paths:
matches = []
for ident, item, stored in observed:
if stored:
try:
match = same_path(path, stored)
except ValueError:
match = False
if match:
matches.append((ident, item))
if len(matches) != 1 or matches[0][0] in selected:
raise RuntimeError("Missing/ambiguous mapping for: " + path)
selected.add(matches[0][0]); result.append(matches[0][1])
if not allow_other and len(selected) != len(observed):
raise RuntimeError("Unrequested or pathless items in import result")
return result
def import_once(ctx, raw_paths, apply=False, expected=None, expected_digest=None):
paths, digest = prepare_files(raw_paths)
before_items = folder_items(ctx)
before_ids = {uid(x) for x in before_items}
collisions = []
for item in before_items:
stored = properties(item).get("File Path")
if isinstance(stored, str) and stored:
try:
collisions.extend(p for p in paths if same_path(p, stored))
except ValueError:
pass # Non-native/compound path is not guessed or converted.
report = {"status": "plan_only", "target": ctx.target, "files": paths,
"files_metadata_sha256": digest, "existing_in_current_folder": collisions,
"duplicate_scope": "current_folder_only", "write_attempted": False}
ensure_current(ctx)
if not apply:
return report, []
expect_target(ctx, expected)
if digest != expected_digest:
raise ValueError("File manifest differs from the reviewed --expect-files-sha256")
if collisions:
raise ValueError("Files already present in current folder. No reimport/reuse fallback")
if {uid(x) for x in folder_items(ctx)} != before_ids or prepare_files(paths)[1] != digest:
raise RuntimeError("Folder contents or input metadata changed before import")
ensure_current(ctx)
try:
imported = ctx.pool.ImportMedia(paths) # Exactly ONE call; no hidden retry.
ensure_current(ctx)
ordered = order_by_paths(paths, imported)
after_ids = {uid(x) for x in folder_items(ctx)}
returned_ids = {uid(x) for x in ordered}
if returned_ids & before_ids or after_ids != before_ids | returned_ids:
raise RuntimeError("Observed folder delta differs from import result")
except Exception as exc:
raise OutcomeUnknown("Import attempted; completeness unconfirmed. Inspect Media Pool; do not retry blindly.") from exc
report.update(status="import_observed_review_in_resolve", write_attempted=True,
mapping=[{"path": p, "media_pool_id": uid(i)} for p, i in zip(paths, ordered)])
return report, ordered
def timeline_rows(timeline):
rows = []
for track_type in ("video", "audio", "subtitle"):
count = timeline.GetTrackCount(track_type)
if type(count) is not int or not 0 <= count <= 128:
raise RuntimeError("Unexpected track count")
for index in range(1, count + 1):
locked = timeline.GetIsTrackLocked(track_type, index)
if type(locked) is not bool:
raise RuntimeError("Track lock state is unavailable")
for item in api_list(timeline.GetItemListInTrack(track_type, index), "Track items"):
source = item.GetMediaPoolItem()
start, end = item.GetStart(True), item.GetEnd(True)
if any(type(x) not in (int, float) or not math.isfinite(x) for x in (start, end)) or end <= start:
raise RuntimeError("Unreadable or invalid timeline item bounds")
rows.append({"id": uid(item), "source_id": uid(source) if source else None,
"type": track_type, "track": index, "start": start, "end": end,
"locked": locked})
if len(rows) > MAX_ITEMS:
raise RuntimeError("Timeline exceeds tutorial processing limit")
if locked:
raise RuntimeError("Unlock test timeline tracks manually before this tutorial")
if len({r["id"] for r in rows}) != len(rows):
raise RuntimeError("Duplicate timeline item identities")
return rows
def append_once(ctx, items, apply=False, expected=None):
if ctx.timeline is None:
raise RuntimeError("This operation needs an active test timeline")
items = api_list(items, "Append inputs")
ids = [uid(i) for i in items]
if not ids or len(ids) != len(set(ids)):
raise ValueError("Choose distinct MediaPoolItem IDs in the intended order")
original = timeline_rows(ctx.timeline)
report = {"status": "plan_only", "target": ctx.target, "ordered_media_pool_ids": ids,
"timeline_is_empty": not original, "write_attempted": False,
"placement": "whole items; default append; audio layout and timing require UI review"}
ensure_current(ctx)
if not apply:
return report
expect_target(ctx, expected)
if original:
raise RuntimeError("This tutorial only writes to an EMPTY test timeline")
available = {uid(x) for x in folder_items(ctx)}
if not set(ids) <= available or timeline_rows(ctx.timeline):
raise RuntimeError("Inputs/empty timeline changed before append")
ensure_current(ctx)
try:
appended = api_list(ctx.pool.AppendToTimeline(items), "AppendToTimeline")
if not appended:
raise RuntimeError("No appended objects returned")
ensure_current(ctx)
observed = timeline_rows(ctx.timeline)
returned_ids = {uid(i) for i in appended}
if len(returned_ids) != len(appended) or not returned_ids <= {r["id"] for r in observed}:
raise RuntimeError("Returned timeline objects could not be reread")
if {r["source_id"] for r in observed} != set(ids):
raise RuntimeError("Observed sources differ from the requested inputs")
order = {ident: idx for idx, ident in enumerate(ids)}
tracks = {(r["type"], r["track"]) for r in observed}
for kind, index in tracks:
track_rows = sorted((r for r in observed if (r["type"], r["track"]) == (kind, index)), key=lambda r: r["start"])
positions = [order[r["source_id"]] for r in track_rows]
if positions != sorted(positions):
raise RuntimeError("Observed source order differs on a track")
except Exception as exc:
raise OutcomeUnknown("Append attempted; result needs inspection. Do not repeat, delete or auto-rollback.") from exc
report.update(status="append_observed_review_audio_and_timing", write_attempted=True,
returned_timeline_item_ids=sorted(returned_ids), observed_items=observed)
return report
def select_ids(ctx, item_ids):
if not item_ids or len(item_ids) != len(set(item_ids)):
raise ValueError("Provide distinct MediaPoolItem IDs")
by_id = {uid(x): x for x in folder_items(ctx)}
if any(ident not in by_id for ident in item_ids):
raise ValueError("Requested item is absent from the current Media Pool folder")
return [by_id[ident] for ident in item_ids]
def add_write_arguments(parser, timeline=False, files=False):
parser.add_argument("--apply", action="store_true", help="Explicitly allow this phase to modify the TEST project")
parser.add_argument("--expect-project-id")
parser.add_argument("--expect-folder-id")
if timeline:
parser.add_argument("--expect-timeline-id")
if files:
parser.add_argument("--expect-files-sha256", help="Metadata manifest digest from reviewed plan, NOT file content hash")
def expected_from(args, timeline=False):
result = {"project_id": args.expect_project_id, "folder_id": args.expect_folder_id}
if timeline:
result["timeline_id"] = args.expect_timeline_id
return result
def output(value):
print(json.dumps(value, ensure_ascii=False, indent=2, allow_nan=False))
Shared connection helper — inspect and download
This file only locates and loads the installed bridge. Native imports can write Python bytecode caches and can block; this is not a sandbox or a timeout mechanism. Use a trusted local installation.
"""Load the installed Blackmagic module; do not install a similarly named package.
This helper uses standard desktop installation paths or RESOLVE_SCRIPT_API.
It does not enable scripting, start Resolve, open projects or retry API calls.
Validate Python/native-library compatibility against your installed README.txt.
"""
from __future__ import annotations
import importlib.util
import os
from pathlib import Path
import struct
import sys
class ResolveConnectionError(RuntimeError):
"""An explicit environment, module, or application-connection failure."""
def scripting_root() -> Path:
configured = os.environ.get("RESOLVE_SCRIPT_API")
if configured is not None:
if not configured.strip() or configured != configured.strip():
raise ResolveConnectionError("RESOLVE_SCRIPT_API is empty or has outer whitespace")
if configured.startswith(('"', "'")) or configured.endswith(('"', "'")):
raise ResolveConnectionError("Do not store literal outer quotes in RESOLVE_SCRIPT_API")
root = Path(os.path.expandvars(configured)).expanduser()
elif sys.platform == "win32":
root = Path(os.environ.get("PROGRAMDATA", r"C:\ProgramData")) / "Blackmagic Design/DaVinci Resolve/Support/Developer/Scripting"
elif sys.platform == "darwin":
root = Path("/Library/Application Support/Blackmagic Design/DaVinci Resolve/Developer/Scripting")
elif sys.platform.startswith("linux"):
root = Path("/opt/resolve/Developer/Scripting")
else:
raise ResolveConnectionError("Set RESOLVE_SCRIPT_API for this platform/installation")
if not root.is_absolute():
raise ResolveConnectionError("RESOLVE_SCRIPT_API must be an absolute Scripting directory")
return root
def module_file() -> Path:
# RESOLVE_SCRIPT_API points to Scripting, not its Modules subdirectory.
return scripting_root() / "Modules" / "DaVinciResolveScript.py"
def load_resolve_module():
if struct.calcsize("P") * 8 != 64:
raise ResolveConnectionError("Use a compatible 64-bit Python interpreter")
source = module_file()
if not source.is_file():
raise ResolveConnectionError("Official DaVinciResolveScript.py not found at: " + str(source))
configured_lib = os.environ.get("RESOLVE_SCRIPT_LIB")
if configured_lib is not None:
lib = Path(os.path.expandvars(configured_lib)).expanduser()
if not lib.is_absolute() or not lib.is_file():
raise ResolveConnectionError("RESOLVE_SCRIPT_LIB must name the installed native library file")
name = "DaVinciResolveScript"
spec = importlib.util.spec_from_file_location(name, source)
if spec is None or spec.loader is None:
raise ResolveConnectionError("Cannot create a loader for the official module")
previous = sys.modules.get(name)
module = importlib.util.module_from_spec(spec)
sys.modules[name] = module
try:
spec.loader.exec_module(module)
# Blackmagic's wrapper can replace its sys.modules entry with a native module.
loaded = sys.modules[name]
if not callable(getattr(loaded, "scriptapp", None)):
raise ResolveConnectionError("Loaded module does not expose scriptapp")
return loaded
except Exception as exc:
if previous is None:
sys.modules.pop(name, None)
else:
sys.modules[name] = previous
raise ResolveConnectionError("Official module was found, but loading it failed") from exc
def connect():
module = load_resolve_module()
try:
resolve = module.scriptapp("Resolve")
except Exception as exc:
raise ResolveConnectionError("scriptapp failed; inspect the original exception") from exc
if not resolve:
raise ResolveConnectionError("No Resolve object: check running Studio, Local access and Python environment")
return resolve
def project_manager(resolve):
manager = resolve.GetProjectManager()
if not manager:
raise ResolveConnectionError("Resolve connected, but Project Manager is unavailable")
return manager
Launch commands
V1 references without filesystem probes
python source_media_paths.py --track-type video --track 1 --limit 100Use python3 on macOS/Linux. The path returned by Resolve is retained.
A1 with explicit file probes
python source_media_paths.py --track-type audio --track 1 --check-filesNetwork probes can block; this is not an audio decoding test.
What the code does
Occurrence
Choose one existing track type and 1-based index. A missing V1 is not treated as a successful empty report.
Source
An occurrence may have no corresponding MediaPoolItem. That is a distinct status, not a reason to invent a path.
Path
The actual File Path is retained. Filesystem availability is probed only with --check-files and does not prove decoding.
Review the helper
The complete shared implementation is visible below the main example. Read its scope and stop conditions, not just the short entry point.
GetMediaPoolItem does not guarantee one ordinary file for every item type.
No RelinkClips, ReplaceClip, import, append or metadata writes are performed.
Project/timeline identity is reread, without claiming a transactional snapshot.
DEBUG / OBSERVATION FIRST
An error is a starting point, not a diagnosis.
Chosen track does not exist
Possible reasonTrack type/index is wrong; indexing starts at 1.
Next checkCheck GetTrackCount and the intended track in the UI.
no_media_pool_item
Possible reasonNo direct MediaPoolItem link exists.
Next checkInspect the item type in Resolve; do not find an arbitrary file by name.
path_property_missing / path_empty
Possible reasonThe key is absent or contains an empty string.
Next checkInspect the full property dictionary; do not guess FilePath/file_path aliases.
probe_unavailable / not_found_at_probe_time
Possible reasonForeign-OS path, restricted access or unavailable storage.
Next checkInspect the environment manually; never auto-relink or replace the source.
PRACTICE / YOUR OWN EVIDENCE
A source clip, a repeated occurrence and a pathless item
- Use one ordinary clip twice on a test V1; add a test generator if available.
- Read without --check-files; compare occurrence/source IDs and inspect the generator’s status.
- For the ordinary local file, repeat with --check-files and separately inspect picture in Resolve.
Repeated occurrences remain visible; missing paths do not become invented files; filesystem probing is separate from API reading.
This is a checklist for your test, not a report of a test already performed on the website.
FAQ
Questions that matter in practice.
Why not return only a path string?
A blank string conflates several states. Statuses distinguish an unsupported relationship from a failed read or missing file.
Is this the active proxy path?
No such claim is made. The stored source File Path is read; active proxy/cache mode and the actually decoded asset are not independently established.[3]
Can I package a project using these paths?
Not as a universal method. The report covers direct references on one track and does not guarantee all dependencies, especially for compound sources.
Why are filesystem checks off by default?
Reading a stored reference does not require storage access; network or disconnected paths may block. An explicit flag separates the tasks.[7]
SOURCES / VERSION BOUNDARIES
Sources, not implied certification.
The source review, offline Python tests and UI checks are different kinds of evidence. None establishes that every example works with every Studio build, operating system or Python version. The date below is the editorial review date, not a live-test certificate.
- Blackmagic Design — DaVinci Resolve Studio 21.1 release notes
8 September 2026. The release announcement assigns Python scripting to Studio. Edition requirements, not a test report for our examples.
- Blackmagic Design — Support Center
Release catalogue checked on 5 October 2026: 21.1.1 is listed. The latest release is not the minimum version of every method.
- Blackmagic Scripting README — snapshot 24 July 2026 (Resolve 21.0.4)
Public mirror of Blackmagic’s original README, maintained by X-Raym. Used for basic signatures; not official hosting and not a 21.1.1 README. Locate your installed Developer/Scripting/README.txt through Resolve’s Help/Documentation menu; exact menu nesting can vary.
- Blackmagic Design — Scripting and Automation
Official description of Studio Python/Lua scripting and integrations. Do not extend Studio capabilities to Free without checking the exact release.
- Python — os.path: samefile, normpath and platform rules
A pathname string is not file identity. Examples never force lowercase; differently spelled paths are equated only when the local filesystem confirms identity.
- Python — pathlib
Directory enumeration, absolute paths and file probes. A matching extension is not codec support or successful decoding.