RESOLVE API / FIELD GUIDE / 03

How to Get the Current DaVinci Resolve Project in Python

Application, project and active timeline are three different layers of state.

Obtain Project Manager, check the current project name and ID, and read timeline count and the active Timeline. An absent timeline is allowed; implicit loading and project creation are excluded.

THE RESULT

Read a limited current-project and timeline report without choosing another target for the user.

PYTHON / RESOLVESTUDY MODEL
01manager = resolve.GetProjectManager()

The manager belongs to the connected Project Library, not to a media folder on disk.

CHECK

The manager object exists before further calls.

Switching steps changes the explanation. Nothing connects to Resolve or runs on your computer.

01 / BEFORE THE CODE

Set the conditions before you run.

01

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.

02

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.

03

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.

04

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.

ResolveGetProjectManager()
Input
No arguments.
Returns
ProjectManager for the current library.
Check
Require a non-empty object.
ProjectManagerGetCurrentProject()
Input
No arguments.
Returns
Currently open Project.
Check
Stop when absent; do not open a similarly named project.
ProjectGetName() / GetUniqueId()
Input
No arguments.
Returns
Readable name and identifier.
Check
A name is not a globally unique address across libraries.
ProjectGetTimelineCount()
Input
No arguments.
Returns
Timeline count.
Check
0 is valid; do not turn None or invalid types into zero.
ProjectGetCurrentTimeline()
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.

Two files in the same folder

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) ↓
current_project.py
"""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.

resolve_api_connection.py
"""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

Read the current project
python current_project.py

Resolve must be running with a test project open; keep the helper alongside the script.

Check an expected name

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

  1. Get the current object

    Ask Project Manager for the already-open project without searching or loading.

  2. Read its identity

    Validate name and ID; --expect-project optionally constrains the exact name.

  3. Allow an empty Timeline

    Return null when no timeline is active; read timeline count independently.

  4. Re-read the project

    Compare the current project ID to the initial ID before returning.

Boundaries to keep

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

  1. Open an empty test project and read a report: count and the active Timeline are checked separately.
  2. Create a timeline manually and read again. Match the name and ID to the intended object.
  3. Run with a deliberately different --expect-project and verify that execution stops without opening anything.
ACCEPT WHEN

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.

  1. 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.

  2. 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.

  3. 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.

  4. 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.

KEEP BUILDING

Continue with a precise next task.

API / Getting Started

How to Connect to DaVinci Resolve with the Python API

Set up external Python for Resolve Studio using the installed Blackmagic module, Windows/macOS/Linux paths and a product/version check. No project opening, hidden retries or random pip package.

API / Projects

How to Create a DaVinci Resolve Project with the Python API

Controlled CreateProject in a local Project Library: check name, library and folder, require a separate --apply, stop if a project is open and verify saving. No hidden LoadProject or automatic deletion.

API / Timeline

Read timeline FPS through the API

Read timelineFrameRate from Timeline and Project independently, preserve the original string and handle fractional labels and DF. Never substitute a rate after failure or guess the underlying timebase.

Back to the API library →MCP and AI assistants →