01 / BEFORE THE CODE
До кода — условия запуска.
Запущенный Resolve Studio
Основной маршрут — внешний локальный Python и настольная Studio. В релизе 21.1 Blackmagic относит Python scripting к Studio; прежний опыт с Free не подтверждает текущую совместимость. Не используйте Fusion-подключение как обход ограничения редакции.
Python и библиотека одной установки
Для синтаксиса этих примеров нужен Python 3.8 или новее. Отдельно нужны совместимые 64-битные Python и native-библиотека Resolve. Номер Python, архитектуру и ограничения сверяйте с README своей установки. Наличие Python 3 само по себе не гарантирует загрузку бинарного модуля.
Локальный доступ, без сети
Для внешнего скрипта проверьте Preferences → System → General → External scripting using → Local, если этот пункт есть в вашей версии. Network не нужен для примеров на этом компьютере. Не повышайте права процесса до администратора как универсальное исправление.
Локальная Disk Project Library
Этот учебный скрипт намеренно не работает с PostgreSQL/Cloud. Выберите локальную библиотеку и папку вручную. Это ограничение примера, не общий запрет Resolve API.
Сохранить и закрыть текущий проект
Перед применением самостоятельно сохраните и закройте уже открытый проект, оставив Project Manager. Скрипт не закрывает, не сохраняет и не заменяет его за вас. Если API продолжает показывать открытый проект — остановитесь.
Ориентируйтесь на актуальные требования Studio и документацию установленной версии, а не на старые инструкции для консоли или Free.[1][4][3]
02 / API CONTRACT
Какой объект. Какой метод. Какой результат.
GetCurrentDatabase()- Вход
- Без аргументов.
- Возвращает
- Сведения о текущей библиотеке.
- Проверить
- Наш сценарий принимает только DbType=Disk с непустым DbName.
GetCurrentFolder() / GetProjectListInCurrentFolder()- Вход
- Без аргументов.
- Возвращает
- Текущая папка и список имён в ней.
- Проверить
- Не трактовать сбой чтения как пустую папку; имя проверяется в текущем контексте.
CreateProject(name)- Вход
- Имя нового проекта, не путь к файлу.
- Возвращает
- Project при успехе; документация описывает пустой результат при неуникальном имени.
- Проверить
- Вызывается один раз. Пустой результат не вызывает LoadProject или повтор.
GetName() / GetUniqueId()- Вход
- Без аргументов.
- Возвращает
- Имя и ID созданного объекта.
- Проверить
- Сопоставить с текущим проектом до сохранения.
SaveProject()- Вход
- Без аргументов; сохраняется текущий проект.
- Возвращает
- Булево подтверждение сохранения.
- Проверить
- Не вызывать до проверки идентичности; ошибка не означает откат CreateProject.
Контракты методов Resolve описаны Blackmagic; Python и авторские helper обозначены отдельно. Дополнительные проверки — устройство наших примеров. Непустой объект не доказывает совместимость всего API.[3]
03 / UNDERSTAND
Что создаётся и где
CreateProject создаёт проект в контексте Project Manager. Имя API_Demo_Project — не путь, не каталог с исходниками и не имя экспортируемого .drp-файла. Локальная Project Library и её выбранная папка должны быть проверены до операции.
В README 21.0.4 у CreateProject также описан необязательный mediaLocationPath. Он относится к размещению медиа проекта, а не к переключению библиотеки. В этом вводном примере используется однопараметрический вызов; дополнительный аргумент не нужен для цели урока.
Мы ограничиваем выполнение локальной библиотекой Disk и выбранной папкой, чтобы не соединять первый опыт создания проекта с управлением серверной или облачной библиотекой. Это граница нашего кода, а не утверждение, что Resolve не умеет работать с другими библиотеками.[3]
04 / UNDERSTAND
Сначала посмотрите план
Без --apply код выполняет только чтение. Он показывает library, db_type, folder, name_available и project_open. При занятом имени или открытом проекте план всё равно может быть напечатан — это наблюдение, а не разрешение на создание.
План не резервирует имя и не блокирует Project Manager. Перед применением сохраните и закройте открытый проект вручную. Проверьте библиотеку и строку folder в выводе. Корень может быть представлен пустой строкой: переносите фактическое значение, не подставляйте придуманное Root.
Имена ограничены в нашем примере 1–96 символами без внешних пробелов, управляющих символов и разделителей пути. Это редакционное ограничение учебного скрипта. Оно не объявляется официальным пределом длины имени Resolve.[3]
05 / UNDERSTAND
Применение — отдельная команда
Для создания нужны --apply, --expect-library и --expect-folder. Скрипт повторно читает состояние и сравнивает его с ожидаемой целью. Если проект уже открыт, имя занято или выбранная папка изменилась, он останавливается до CreateProject.
Не запускайте параллельно второй экземпляр скрипта и не меняйте библиотеку/папку в интерфейсе. Повторное чтение сокращает вероятность ошибки, но не создаёт атомарную транзакцию и не защищает от всех действий другого процесса между проверкой и записью.
После успешного CreateProject нужно сверить имя и ID возвращённого объекта с текущим проектом. Только затем выполняется SaveProject. Он сохраняет текущий проект, поэтому проверка правильной цели здесь важнее самого факта, что CreateProject вернул непустое значение.[3]
06 / UNDERSTAND
Почему здесь нет «создать или открыть»
Старый подход CreateProject → при неудаче LoadProject объединяет два разных намерения. Сбой создания не доказывает, что существующий одноимённый проект является правильной целью. Особенно опасно продолжать после этого импорт, удаление или рендер.
Этот пример не переиспользует существующий проект и не добавляет числовой суффикс молча. Повторный запуск с тем же именем должен остановиться или показать name_available=false в плане. Для сценария «открыть известный проект» нужен отдельный, явно описанный режим с проверкой идентичности.[3]
07 / UNDERSTAND
Что делать после неполного результата
Если CreateProject вызван, но объект не получен или Python потерял ответ, не считайте операцию откатившейся. Проверьте Project Manager, список проектов и текущий проект. Скрипт не повторяет создание и не удаляет ничего в качестве автоматического восстановления.
Если проект получен, но SaveProject не подтвердил успех, проект уже мог появиться в библиотеке. Сохраните наблюдение и проверьте его вручную. Не запускайте весь файл с новым именем, пока не разобрались с первым результатом.
Статус created_and_save_acknowledged означает ровно два факта: объект создан и API подтвердил SaveProject после сверки идентичности. Долговременное сохранение, повторное открытие и резервная копия проверяются отдельно в интерфейсе и не приписываются этому статусу.[3]
PYTHON / COMPLETE EXAMPLE
Прочитайте код. Затем запускайте осознанно.
Скачайте create_project.py и resolve_api_connection.py либо возьмите комплект из пяти скриптов. Helper написан нами; официальный модуль DaVinciResolveScript и нативная библиотека должны браться из вашей установки Resolve.
Скачать все примеры с инструкцией (.zip) ↓"""Plan by default. Create ONE project in a local Disk library only after --apply.
Close/save any existing project manually first. Do not interact with Project
Manager while this script runs. There is no atomic transaction or automatic rollback.
"""
import argparse
import json
from resolve_api_connection import connect, project_manager
class CreationOutcomeError(RuntimeError):
"""A write may have happened. Inspect Resolve; never retry this blindly."""
def validate_name(name):
# These are limits of this tutorial, not documented Resolve name limits.
if not isinstance(name, str) or not name or name != name.strip() or len(name) > 96:
raise ValueError("Use 1-96 characters without outer whitespace")
if any(ord(ch) < 32 or ch in '/\\' for ch in name):
raise ValueError("Control characters and path separators are not accepted here")
return name
def inspect_target(manager, name):
name = validate_name(name)
database = manager.GetCurrentDatabase()
if not isinstance(database, dict) or database.get("DbType") != "Disk" or not database.get("DbName"):
raise RuntimeError("This tutorial targets a local Disk Project Library only")
folder = manager.GetCurrentFolder()
names = manager.GetProjectListInCurrentFolder()
if not isinstance(folder, str) or not isinstance(names, list) or not all(isinstance(x, str) for x in names):
raise RuntimeError("Could not read the current Project Manager folder")
return {
"library": database["DbName"],
"db_type": database["DbType"],
"folder": folder,
"name": name,
"name_available": name not in names,
"project_open": bool(manager.GetCurrentProject()),
}
def create_once(manager, name, apply=False, expected_library=None, expected_folder=None):
before = inspect_target(manager, name)
if not apply:
return {"status": "plan_only", "writes": 0, "target": before}
if expected_library is None or expected_folder is None:
raise ValueError("--apply requires --expect-library and --expect-folder, copied from the inspected target")
if before["library"] != expected_library or before["folder"] != expected_folder:
raise RuntimeError("Project Library/folder differs from the explicitly expected target")
if before["project_open"]:
raise RuntimeError("An existing project is open. Save/close it yourself; this script will not do so.")
if not before["name_available"]:
raise RuntimeError("Name already exists. No LoadProject fallback is permitted.")
# Re-read immediately before writing. This is a guard, not a lock.
if inspect_target(manager, name) != before:
raise RuntimeError("Project Manager state changed. No creation was attempted.")
try:
project = manager.CreateProject(name)
except Exception as exc:
raise CreationOutcomeError("CreateProject outcome unknown. Inspect Project Manager before any retry.") from exc
if not project:
raise CreationOutcomeError("CreateProject returned no object. No fallback, retry or deletion was attempted.")
try:
current = manager.GetCurrentProject()
project_id = project.GetUniqueId()
if not project_id or not current or current.GetUniqueId() != project_id or project.GetName() != name:
raise CreationOutcomeError("Creation returned an object, but current-project identity is not confirmed")
if not manager.SaveProject():
raise CreationOutcomeError("Project was created; SaveProject did not acknowledge success. Inspect it manually.")
except CreationOutcomeError:
raise
except Exception as exc:
raise CreationOutcomeError("Creation happened; verification/save outcome is uncertain. Do not repeat creation.") from exc
return {"status": "created_and_save_acknowledged", "target": before, "project_id": project_id}
def main(argv=None):
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("name", help="New project name; not a filesystem path")
parser.add_argument("--apply", action="store_true", help="Allow one creation and save after all guards pass")
parser.add_argument("--expect-library")
parser.add_argument("--expect-folder", help="Exact folder string from plan; an empty string may represent root")
args = parser.parse_args(argv)
validate_name(args.name)
result = create_once(project_manager(connect()), args.name, args.apply, args.expect_library, args.expect_folder)
print(json.dumps(result, ensure_ascii=False, indent=2))
if __name__ == "__main__":
main()
Общий helper подключения — посмотреть и скачать
Этот файл находит и загружает установленный мост. При импорте Python может записывать кэш байткода, а нативный вызов — зависнуть; это не песочница и не механизм тайм-аута. Используйте доверенную локальную установку.
"""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
Команды запуска
Только план
python create_project.py "API_Demo_Project"Не создаёт проект. Значения library/folder нужно сверить с Project Manager.
Создать после проверки
python create_project.py "API_Demo_Project" --apply --expect-library "Local Database" --expect-folder "API_Tests"Local Database и API_Tests — только примеры. Замените их точными значениями из вашего плана; убедитесь, что это тестовая цель.
Что делает код
Проверить цель
Прочитать локальную библиотеку, папку, занятость имени и наличие открытого проекта.
Отделить план
Без --apply вернуть только план; не сохранять и не закрывать чужой проект.
Создать один раз
После повторной проверки отправить один CreateProject; никаких LoadProject и скрытых повторов.
Сверить и сохранить
Проверить ID текущего объекта и затем получить подтверждение SaveProject.
Изменяющий вызов существует только в явно выбранном режиме --apply.
Никаких автоматических LoadProject, CloseProject, DeleteProject, SetCurrentDatabase и переименования цели.
После неопределённого результата сначала проверьте состояние; не повторяйте создание.
DEBUG / OBSERVATION FIRST
Ошибка — отправная точка, не готовый диагноз.
Name already exists
Возможная причинаНазвание найдено в текущей папке.
Следующая проверкаОстановитесь. Не загружайте и не удаляйте существующий проект; выберите другое имя осознанно.
An existing project is open
Возможная причинаAPI видит открытый проект.
Следующая проверкаСохраните и закройте его вручную. Скрипт не делает этого автоматически даже после --apply.
Library/folder differs
Возможная причинаСостояние не совпало с явно указанной целью.
Следующая проверкаПроверьте Project Manager и заново прочитайте план. Не меняйте проверку на «любой доступный» объект.
CreateProject outcome unknown
Возможная причинаВызов отправлен, но результат не подтверждён.
Следующая проверкаПроверьте интерфейс и библиотеку. Не повторяйте создание и не запускайте очистку.
SaveProject did not acknowledge success
Возможная причинаСоздание могло завершиться, сохранение не подтверждено.
Следующая проверкаПроверьте появившийся проект и сохраните его вручную при необходимости. Ошибка сохранения не равна отмене создания.
PRACTICE / YOUR OWN EVIDENCE
Контролируемое создание и повторный запуск
- Выберите локальную тестовую библиотеку и выполните команду без --apply. Сверьте library/folder в Project Manager.
- Сохраните и закройте открытый проект вручную. Примените с точными ожидаемыми библиотекой и папкой; создавайте только ненужный тестовый проект.
- Проверьте имя в Resolve, сохранение и повторное открытие вручную. Затем прочитайте план с тем же именем: name_available должен отражать занятое имя, а не создавать второй проект.
Создан ровно один ожидаемый проект. Не выполнены скрытые загрузка, закрытие, удаление или смена библиотеки. Результат после SaveProject проверен на вашем компьютере.
Это условия вашей проверки, а не отчёт об испытании, уже выполненном на сайте.
FAQ
Вопросы, которые возникают на практике.
SaveProject создаёт .drp?
Нет. SaveProject сохраняет текущий проект в его библиотеке. Для отдельного .drp предусмотрена другая операция экспорта.[3]
Можно ли использовать PostgreSQL или Cloud?
Не этим примером: он намеренно отказывает вне локальной Disk-библиотеки. Это ограничение учебного маршрута, а не описание всех возможностей Project Manager.[3]
Можно ли автоматически открыть проект при совпадении имени?
Это уже другой сценарий. Здесь совпадение означает остановку; оно не подтверждает нужную идентичность и не разрешает следующие правки.
Гарантирует ли --apply безопасность?
Нет. Это явное разрешение только внутри данного скрипта. Оно не блокирует других участников, не создаёт транзакцию и не заменяет резервную копию.
Пустой ответ можно просто повторить?
Нет: после отправленного изменяющего вызова результат может быть неопределённым. Сначала проверьте библиотеку и текущий проект вручную.
SOURCES / VERSION BOUNDARIES
Источники, а не подразумеваемая сертификация.
Сверка источников, офлайн-тесты Python и проверка интерфейса — разные виды подтверждения. Ни один из них не означает, что каждый пример проверен со всеми сборками Studio, ОС и версиями Python. Указанная дата — дата редакционной проверки, не сертификат живого прогона.
- Blackmagic Design — DaVinci Resolve Studio 21.1 release notes
8 сентября 2026. В объявлении выпуска Python scripting отнесён к Studio. Это источник требований редакции, а не протокол испытания наших примеров.
- Blackmagic Design — Support Center
Каталог обновлений проверен 5 октября 2026: опубликована ветка 21.1.1. Последний выпуск не равен минимальной версии каждого метода.
- Blackmagic Scripting README — snapshot 24 July 2026 (Resolve 21.0.4)
Публичная копия первичного README Blackmagic, поддерживаемая X-Raym. По ней сверены базовые сигнатуры; это не официальный хостинг и не README 21.1.1. Оригинал своей установки: Help → Documentation → Developer → Scripting → README.txt; расположение пункта зависит от версии.
- Blackmagic Design — Scripting and Automation
Официальное описание Python/Lua scripting и интеграций Studio. Нельзя переносить обещания Studio на Free без проверки конкретного выпуска.