見出し画像

【DaVinci Resolve】外部アプリから操作する方法(2023/12/30追記)

まえがき

火注ゆかなです。
最近はようやく花粉が治まってきて嬉しい限りです。

今回の記事はDaVinci Resolveを外部アプリから操作する方法です。
DaVinci ResolveはLua、もしくはPythonでスクリプトを組んで操作を自動化できるのですが、DaVinci Resolveから他のアプリケーションを操作することはできてもその逆はできませんでした。(とりあえず私は知りません)

でも先日、fuscript.exeというファイルでDaVinci Resolveを外部から操作できるとわかったので記事にまとめておきます。

とはいえ、今回紹介する方法は有償版でしか使えませんし、実行できるのはLuaかPythonのコマンドかスクリプトファイルだけなのですけれど。
GUIは好きな言語で作れるので自由度が上がるかなと思います。

fuscript.exeについての記述はFusion8_Scripting_Guideの24Pあたりで参照できます。

https://documents.blackmagicdesign.com/UserManuals/Fusion8_Scripting_Guide.pdf

2023/12/30追記

Pythonの場合はPython用モジュールを利用することでも外部操作が可能なようです。別に外部操作が不要ならスクリプト内でPython用モジュールをインポートしなくても問題ないのですが、ちょっとしたきっかけで調べて理解が深まったので章を追加しました。



外部から操作する方法(fuscript.exe編)

前提条件

  • DaVinci Resolve Studio(有償版)であること

  • 外部スクリプトの使用許可を「ローカル」以上にしておくこと

「環境設定」>「システム」>「一般」から設定可能

fuscript.exeを使う

ファイルはWindowsなら以下の場所に入っています。
「C:\Program Files\Blackmagic Design\DaVinci Resolve\fuscript.exe」

使い方

基本的にはコマンドラインから第1引数にスクリプトファイル、第2引数以降にスクリプトファイルに対する引数を渡して実行します。
スクリプトファイルに対する引数は arg[1], arg[2], arg[3], … arg[n] という記述でそれぞれアクセスできます。
 arg[0] にはスクリプトファイルのパスが格納されます。

fuscript.exe script.lua arg1 arg2 arg3 ...

試しに実行してみましょう。
今回実行するscript.luaは以下のように、引数の値を標準出力するだけのものとします。

print("Hello World!")
for i = 1, #arg do
	print(arg[i])
end

引数を渡した場合の実行結果は以下の通り。

> fuscript.exe script.lua arg1 引数2 "引数3"

DaVinci Resolve Script Interpreter
Copyright (C) 2005 - 2023 Blackmagic Design Pty. Ltd.

Hello World!
arg1
引数2
引数3

なんかコピーライトが表示されていますが、これは後述する -q オプションをスクリプトファイルパスより前に付けると非表示にできます。
バッチファイルなどで標準出力の内容を扱いたい場合は邪魔になるので、基本的に-qオプションは付けておいた方が良いでしょう。

> fuscript.exe -q script.lua arg1 引数2 "引数3"
Hello World!
arg1
引数2
引数3

オプションの概要

引数を何も指定しない場合、オプションについての説明を取得できます。
以下は現時点でわかっている各オプションの概要です。

-i          :対話モード。
              DaVinci Resolveのコンソールと同じように操作が可能。

-v          :バージョン情報。

-s          :Run as script server
              使い方はよくわからず。サーバー上で指定のスクリプトを動かせる?
-S          :Run as script server with no timeout
              使い方はよくわからず。

-p [appname]:Ping script apps
              とりあえず -p オプションのみで実行すると、DavinciResolve起動中なら
              下記のようにアプリを起動している環境が返される。
              例:  Machine-Name (起動中のローカルホストアドレス?)
                      UserName = ユーザー名
                      Platform = OS(Windosなど)
                        Version = 0.0
                          Hosts:
                                   1 = Fusion
              起動していない場合は何も出力せずに終了する。
-P [appname]:Ping script apps (longer timeout)
              -p オプションのタイムアウトまでの時間が長いバージョン?

-q          :静音モード? 付けるとfuscript実行時に標準出力に表示されるコピーライトを
              非表示にできる。

-b          :デバッグモードで動作させる。

-l <lang>   :使用する言語の指定。Lua, Py2, Py3, Python2, Python3 のいずれかを指定可能。
              省略するとLuaとなる。
              Python2のスクリプトを実行する場合などは下記のように指定する。
              例:fuscript.exe -l Py2 script.py arg1 arg2 ...

-x <string>:コマンドを文字列で渡して実行可能。
              例:fuscript.exe -x "print('Hello World!')"

とりあえずこんな感じでしょうか。
fuscriptから操作する場合の制限は特になくて、普通にDavinciResolveのコンソールで操作できることは同じようにできそうです。
一応、タイムラインを取得したり、再生位置の操作ができることは確認しました。

ちなみに「外部アプリから操作するからDavinciResolveのGUIは表示しなくても良いやー」という場合、DavinciResolveをヘッドレスモードで実行しておけばOKです。
ヘッドレスモードでの起動は -noguiオプションを付けて実行します。

> Resolve.exe -nogui

完全な自動化は厳しいので、そういう使い方する人がいるかはわかりませんけれど。


外部から操作する方法(Python用モジュール編)(2023/12/30追記)

前提条件

  • Pythonがモジュールのあるフォルダを認識していること

fuscript.exe編と同様に有償版、かつ外部スクリプトがローカル以上というのは同じですが、Python版は公式が用意したモジュール「DaVinciResolveScript」を読み込まないといけません。
(アプリケーション上から実行する場合はモジュールをわざわざインポートする必要はありません)

(ちなみにこのモジュールがbmdライブラリの正体みたいです。dir()で出力されるメソッドが全く同じ。
アプリ上だとDefaultでbmdって別名で読み込んでますが、元のモジュール名だと長いからなんですかね?)

モジュールを参照できるようにするためには環境変数の設定が必要です。
ネットを検索すれば環境変数に「RESOLVE_SCRIPT_API」「RESOLVE_SCRIPT_LIB」「PYTHNPATH」を適切に設定してね~って情報にたどり着くことでしょう。なので環境変数の設定の下りは省略します。

アプリケーションのコンソールでPython3モードを選択してHelloWorldを正常に出力できてるなら基本的に大丈夫だと思います。

こんなふうに実行できてればOK

ただ、sys.pathでPYTHNPATHを出力するとDaVinciResolveScriptモジュールがあるフォルダが認識されてないこともあるかもしれません。
pprint.pprint(sys.path)でモジュール参照先パスを確認できますが、その中に「"C:\ProgramData\Blackmagic Design\DaVinci Resolve\Support\Developer\Scripting\Modules"」が含まれてない場合は認識できませんので、import DaVinciResolveScript でエラーを吐きます。

(なお、DaVinciResolve上でsys.pathを表示した場合と、Pythonファイルを単独で実行した場合では結果が異なります。
具体的にはDaVinciResolve上でsys.pathを表示すると「RESOLVE_SCRIPT_API」「RESOLVE_SCRIPT_LIB」に設定したパスも追加されてるようです。)

私のように環境変数をいくら弄っても解決しない人はsys.path.append()でモジュールのあるフォルダパスを手動追加するのも良いと思います。ファイルの先頭に下記の文を追加するだけです。

import sys
sys.path.append("C:\ProgramData\Blackmagic Design\DaVinci Resolve\Support\Developer\Scripting\Modules")
import DaVinciResolveScript as dvr_script

環境変数と違ってファイルごとに毎回記述する羽目になりますが、このくらいなら大した手間にはならないでしょう。
追加したいパスを記述した「.pth」って拡張子のファイル(ファイル名自体は何でも良い)をスクリプトと同じフォルダに置く方法もありますが、私の場合はそちらは上手くいきませんでした。うーん。

外部操作スクリプトの例

簡単な例として、現在開いているプロジェクトの名前を知りたい場合はこんな感じになります。
dvr_script.scriptapp("Resolve")でDaVinciResolve最上位オブジェクトのResolveを取得できますが、起動していなかったり外部スクリプトが「なし」に設定されている場合はNoneが返却されます。
条件分岐はしておいた方が良いでしょうね。

import sys
sys.path.append("C:\ProgramData\Blackmagic Design\DaVinci Resolve\Support\Developer\Scripting\Modules")
import DaVinciResolveScript as dvr_script

resolve = dvr_script.scriptapp("Resolve")

if resolve is None :
    print("外部からアプリケーションコントロールを取得できませんでした。")
    print("DaVinciResolveを起動していないか、外部スクリプト設定が「なし」になっている可能性があります。")
    print("(外部スクリプト設定は「ローカル」以上に設定する必要がありますが、これは有償版のみの機能です。)")
    print("アプリの起動状態、および環境設定の確認をしてください。処理を終了します。")
    sys.exit(0)

fusion = resolve.Fusion()
projectManager = resolve.GetProjectManager()
project = projectManager.GetCurrentProject()
print(project.GetName())

ところで「C:\ProgramData\Blackmagic Design\DaVinci Resolve\Support\Developer\Scripting\Examples」にpython_get_resolve.pyというファイルがあります。
これの中身、やってることはデフォルトの場所に置かれてるDaVinciResolveScriptモジュール読み込んで、bmd.scriptapp("Resolve")をGetResolveって違う名前で返してるだけです。
どちらのモジュールを使ってもあんまり変わらないので、好きな方を使えば良いのではないでしょうか。。

import sys
sys.path.append("C:\ProgramData\Blackmagic Design\DaVinci Resolve\Support\Developer\Scripting\Modules")
sys.path.append("C:\ProgramData\Blackmagic Design\DaVinci Resolve\Support\Developer\Scripting\Examples")
from python_get_resolve import GetResolve

resolve = GetResolve()  # dvr_script.scriptapp("Resolve")とやってることはほぼ同じ

if resolve is None :
    print("外部からアプリケーションコントロールを取得できませんでした。")
    print("DaVinciResolveを起動していないか、外部スクリプト設定が「なし」になっている可能性があります。")
    print("(外部スクリプト設定は「ローカル」以上に設定する必要がありますが、これは有償版のみの機能です。)")
    print("アプリの起動状態、および環境設定の確認をしてください。処理を終了します。")
    sys.exit(1)

fusion = resolve.Fusion()
projectManager = resolve.GetProjectManager()
project = projectManager.GetCurrentProject()
print(project.GetName())

デバッグ効率が上がるかも

外部からアプリケーションを操作する必要がなくてもPythonでツールを組んでいるならデバッグ効率が上がるかもしれません。
というのも、外部から実行できない場合は

  1. ソースコードを修正する

  2. DaVinciResolveアプリに切り替える

  3. ワークスペース→スクリプト→目的のスクリプトを選んで実行

  4. 開発用エディタに切り替える

という4つの手順を踏むことになります。
3.の部分はショートカットキーを割り当てることで省略できますが、それでもちょっと面倒です。スクリプトに当てたショートカットキーって、別のスクリプトを追加するとズレることもありますし……。

しかし、外部から実行できる場合は

  1. ソースコードを修正する

  2. 開発環境から直接ソースコードを実行

2つの手順で済みます。
ディスプレイが2つ以上なら片方のディスプレイに統合開発環境かエディタを表示、もう片方にはDaVinciResolveを表示しておくことで、アプリの切り替えも不要です。
ウィンドウの切り替えが減るのは地味にありがたい。

Luaで組んでる場合はfuscript.exeの引数として渡して実行するような設定にすれば良いかもしれませんね。

ちなみにスクリプトを組む速度自体はあまり上がらないと思います。
DaVinciResolveScriptモジュールを読み込んでも補完機能が働かないので、ドキュメントとにらめっこするのは変わりません。
期待してただけに残念です。


あとがき

外部アプリケーションから実行する方法は以上です。

正直なところ、-sオプションは使い方が良くわかりませんし、-pオプションは appnameが何を指定すれば良いのかわかりませんし、-bオプションに至ってはLuaのデバッグモード自体がよくわからない……。

わからないことが結構残っていますが、ひとまず凝った使い方をしない限りはこれでもそこそこ使えるかと思います。

詳しい使い方は各自でお調べください。

それでは、ここまで読んでいただきありがとうございました。
この記事の内容が皆様のお役に立ちますように。

サポートしていただけるとその分の価値を提供できてるんだなって励みになります。