01 / BEFORE THE CODE
Set the conditions before you run.
Running Resolve Studio
The primary route is external local Python with desktop Studio. Blackmagic’s 21.1 announcement assigns Python scripting to Studio; past success in Free does not establish current compatibility. Do not use a Fusion connection as an edition workaround.
Matching Python and installed library
The examples use Python 3.8+ syntax. Separately, use a compatible 64-bit Python interpreter and the native library from the same Resolve installation. Check the installed README for Python version and architecture requirements. “Python 3 installed” is not sufficient binary-compatibility evidence.
Local access, not network access
For an external script, check Preferences → System → General → External scripting using → Local where available in your version. These same-machine examples do not need Network. Running as administrator is not a general-purpose fix.
Open test project
This task requires an already-open project. An empty project without timelines is valid. Do not switch projects or run other automation during the read.
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.
GetProjectManager()- Input
- No arguments.
- Returns
- ProjectManager for the current library.
- Check
- Require a non-empty object.
GetCurrentProject()- Input
- No arguments.
- Returns
- Currently open Project.
- Check
- Stop when absent; do not open a similarly named project.
GetName() / GetUniqueId()- Input
- No arguments.
- Returns
- Readable name and identifier.
- Check
- A name is not a globally unique address across libraries.
GetTimelineCount()- Input
- No arguments.
- Returns
- Timeline count.
- Check
- 0 is valid; do not turn None or invalid types into zero.
GetCurrentTimeline()- Input
- No arguments.
- Returns
- Active Timeline, which may be absent.
- Check
- Never call Timeline methods on an empty result.
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
A manager is not a project
GetProjectManager returns the object for library and project operations. Call GetCurrentProject on that manager. Read the project name through project.GetName(), not print(project): a native-object representation is not a substitute for its properties.
The request does not find or open a named project. That is a useful boundary before automation: the user selects the working target in Resolve and the script checks the selected state.[3]
04 / UNDERSTAND
Why the name alone is not enough
The same names can occur across different Project Libraries and folders. --expect-project is an exact-name guard, not a universal address or permission to edit. The report also includes GetUniqueId so the current object can be compared by more than its label.
Keep the selected library context separately for a persistent workflow. Do not assume that copying or exporting/importing a project must preserve its ID. Here the identifier is used to compare the current object within one short read sequence.
Names and IDs in a locally executed report are real. Before sharing the JSON with a client or support, check whether a project name contains personal or internal information.[3]
05 / UNDERSTAND
A project without a timeline is a valid case
GetTimelineCount reports how many timelines exist; GetCurrentTimeline returns the active object. This example accepts count=0 and current_timeline=null. The next task, such as a Media Pool import, might not require an active timeline at all.
For an operation targeting a particular Timeline, add an explicit requirement: obtain that object, check its name/ID, then continue. Do not use GetTimelineByIndex(1) automatically as “the intended timeline”: list order does not express user intent.[3]
06 / UNDERSTAND
Re-reading is a guard, not a transaction
Before returning, the example requests the current project again and compares its ID. A detected project switch causes an error and discards the report. This catches some changes between API calls.
The read sequence is not atomic: a timeline may change within one project, a project may switch away and back, or another participant may modify content. We therefore do not call this a fully consistent snapshot. Future writes need their own preconditions and coordination with other actors.[3]
07 / UNDERSTAND
Use the report without hiding dependencies
Keep the chain explicit: application → manager → project → optional timeline. Do not hide project lookup inside a helper that silently creates a new one on failure. That becomes especially important when the caller later deletes clips or starts a render.
After a successful read, move to a separate Media Pool or timeline guide. This report does not establish that a range is correct, footage is online or export settings match the brief.[3]
PYTHON / COMPLETE EXAMPLE
Read the code. Then run it deliberately.
Download current_project.py and resolve_api_connection.py, or use the five-script bundle. The helper is our code; the official DaVinciResolveScript module and native library must come from your Resolve installation.
Download all examples + instructions (.zip) ↓"""Read the current project and optional timeline. No project is opened or created."""
import argparse
import json
from resolve_api_connection import connect, project_manager
def read_current(manager, expected_name=None):
project = manager.GetCurrentProject()
if not project:
raise RuntimeError("No current project. Open the intended project manually.")
name, project_id = project.GetName(), project.GetUniqueId()
if not isinstance(name, str) or not isinstance(project_id, str) or not project_id:
raise RuntimeError("Project identity is incomplete")
if expected_name is not None and name != expected_name:
raise RuntimeError("Current project name does not match --expect-project")
timeline = project.GetCurrentTimeline()
count = project.GetTimelineCount()
if not isinstance(count, int) or isinstance(count, bool) or count < 0:
raise RuntimeError("Timeline count could not be read")
result = {
"project": {"name": name, "id": project_id},
"timeline_count": count,
"current_timeline": None,
}
if timeline:
timeline_name = timeline.GetName()
timeline_id = timeline.GetUniqueId()
if not timeline_name or not timeline_id:
raise RuntimeError("Timeline identity is incomplete")
result["current_timeline"] = {"name": timeline_name, "id": timeline_id}
current = manager.GetCurrentProject()
if not current or current.GetUniqueId() != project_id:
raise RuntimeError("Active project changed during reading. Discard this report and inspect Resolve.")
# Two identity reads detect some changes; they do not make the snapshot atomic.
return result
def main(argv=None):
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--expect-project", help="Optional exact name check, not a globally unique identity")
args = parser.parse_args(argv)
result = read_current(project_manager(connect()), args.expect_project)
print(json.dumps(result, ensure_ascii=False, indent=2))
if __name__ == "__main__":
main()
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
Read the current project
python current_project.pyResolve must be running with a test project open; keep the helper alongside the script.
Check an expected name
python current_project.py --expect-project "API_Demo_Project"Exact-name comparison only; this does not search for or open a project.
What the code does
Get the current object
Ask Project Manager for the already-open project without searching or loading.
Read its identity
Validate name and ID; --expect-project optionally constrains the exact name.
Allow an empty Timeline
Return null when no timeline is active; read timeline count independently.
Re-read the project
Compare the current project ID to the initial ID before returning.
Zero timelines is not a connection failure.
A Project Library is not a directory of source video files.
The report includes names and IDs; inspect it before publishing.
DEBUG / OBSERVATION FIRST
An error is a starting point, not a diagnosis.
No current project
Possible reasonNo project is open even though the application may be available.
Next checkOpen the intended test project manually. Do not add a similarly named fallback.
Current project name does not match
Possible reason--expect-project differs from the actual name.
Next checkCheck the selected project and exact spelling. Do not remove the guard just to proceed.
current_timeline=null
Possible reasonNo active timeline.
Next checkValid for this project read; choose an explicit Timeline separately for later operations.
Active project changed during reading
Possible reasonInitial and final project IDs differ.
Next checkDiscard the partial report. Stop concurrent switches and repeat only the read.
PRACTICE / YOUR OWN EVIDENCE
Three states of one workstation
- Open an empty test project and read a report: count and the active Timeline are checked separately.
- Create a timeline manually and read again. Match the name and ID to the intended object.
- Run with a deliberately different --expect-project and verify that execution stops without opening anything.
Absent project, empty Timeline and name mismatch remain distinct; none of the cases calls LoadProject, CreateProject or SetCurrentTimeline.
This is a checklist for your test, not a report of a test already performed on the website.
FAQ
Questions that matter in practice.
Does GetCurrentProject return a string?
No. It returns a project object. Call GetName() for the name and GetUniqueId() for the identifier.[3]
Why is there a project but no Timeline?
They are different objects. A project can contain no timelines or have no active Timeline in the observed state. Check the result separately.[3]
Can it tell me the .drp path?
The current project lives in a Project Library; a .drp is a separate export. A project name is not a .drp path, and GetCurrentProject does not create an export.[3]
Does re-reading prevent every race?
No. It detects some project switches but does not make the read sequence atomic or block another user or agent.
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.