Flame 2027.2から.otioファイルの書き出しに対応したため、DaVinci Resolveから書き出した.otioファイルとの内部構造を比較しました。

その過程で、FlameのConformにおけるName Strict / File Name Strictの照合について、2027.1と2027.2で挙動が異なることを確認しました。

今回はDaVinci ResolveとAdobe Premiere Proから同じ編集内容の.otioファイルを書き出し、.movファイルと.exrイメージシーケンスを使用して検証しています。

Clip.2.nameとName Strict

DaVinci ResolveやPremiere Proから書き出した.otioファイルでは、Clip.2.nameにファイルの拡張子が含まれています。

.movの場合:

.exrイメージシーケンスの場合:

これらの名前はFlameのセグメントネームに反映されます。

一方、Flameに読み込んだメディアのクリップネームには、拡張子やフレーム範囲が含まれていないため、そのままではName Strictで一致しません。

そこで、Clip.2.nameを次のように修正しました。

この変更だけで、Premiere Proの.mov、DaVinci Resolveの.movと.exrイメージシーケンスのいずれも、Name Strictによるリンクに成功しました。

File Name Strictのバージョンによる違い

.exrイメージシーケンスでは、Flame 2027.1と2027.2で異なる結果になりました。

検証内容2027.12027.2
Clip.2.nameのみ修正:Name Strict成功成功
Clip.2.nameのみ修正:File Name Strict失敗成功
Clip.2.nameとname_prefixを修正:File Name Strict成功    追加修正不要

Flame 2027.1では、以前の記事で紹介したように、ImageSequenceReference.1.name_prefixも修正する必要がありました。

しかし、Flame 2027.2ではname_prefixを変更しなくても、File Name Strictで正常にリンクできることを確認しました。

このため、2027.2では.otioファイルの修正処理を簡略化できます。

Pythonスクリプトの整理

今回の検証に合わせて、Premiere Pro / DaVinci Resolve共通のPythonスクリプトotio_prepare_for_flame.pyを作成しました。

このスクリプトでは、Clip.2.nameのみを修正します。

  • .mov:ファイルの拡張子を削除
  • .exr:フレーム範囲と拡張子を削除
  • Media Reference、メディアパス、タイミング情報は変更しない
otio_prepare_for_flame.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Prepare Premiere Pro / DaVinci Resolve OTIO for Flame Name Strict conform.

Only Clip.2.name is modified. Media references, paths, timing and metadata
are preserved. Tested structurally against sample Premiere/Resolve OTIO files;
confirm relinking in Flame for each workflow.
"""

import argparse
import json
import re
from pathlib import Path

# Only recognized media extensions are removed. Extend this list as needed.
MEDIA_EXT = re.compile(
    r"\.(?:exr|dpx|tif|tiff|jpg|jpeg|png|mov|mxf|mp4|m4v|avi|mkv|r3d|ari|braw)$",
    re.IGNORECASE,
)
FRAME_RANGE = re.compile(r"\[\s*-?\d+\s*-\s*-?\d+\s*\]$")


def clean_clip_name(name: str) -> str:
    """Strip a known media extension and an optional trailing frame range."""
    without_ext = MEDIA_EXT.sub("", name)
    if without_ext == name:
        return name  # Do not alter arbitrary clip names.
    without_range = FRAME_RANGE.sub("", without_ext)
    if without_range != without_ext:
        return without_range.rstrip(".")  # Handles 'B004C0023.[1001-1024].exr'.
    return without_ext


def prepare(data):
    changes = []

    def walk(value):
        if isinstance(value, dict):
            if value.get("OTIO_SCHEMA") == "Clip.2" and isinstance(value.get("name"), str):
                old = value["name"]
                new = clean_clip_name(old)
                if old != new:
                    value["name"] = new
                    changes.append((old, new))
            for child in value.values():
                walk(child)
        elif isinstance(value, list):
            for child in value:
                walk(child)

    walk(data)
    return changes


def main():
    parser = argparse.ArgumentParser(
        description="Strip file extensions and EXR/DPX frame ranges from Clip.2.name only."
    )
    parser.add_argument("input_otio", type=Path, help="Original Premiere/Resolve .otio")
    parser.add_argument("output_otio", type=Path, help="New .otio (original is not overwritten)")
    args = parser.parse_args()

    if args.input_otio.resolve() == args.output_otio.resolve():
        parser.error("Input and output must be different files.")
    if not args.input_otio.is_file():
        parser.error(f"Input file not found: {args.input_otio}")

    data = json.loads(args.input_otio.read_text(encoding="utf-8-sig"))
    changes = prepare(data)
    args.output_otio.write_text(
        json.dumps(data, ensure_ascii=False, indent=4) + "\n", encoding="utf-8"
    )
    for old, new in changes:
        print(f"  {old}  ->  {new}")
    print(f"Changed Clip.2.name: {len(changes)}")
    print(f"Output: {args.output_otio}")
    print("Media references, paths and timing: unchanged")


if __name__ == "__main__":
    main()

Flame 2027.2では、このスクリプトによる修正だけでName Strict / File Name Strictの両方に対応できます。

一方、Flame 2027.1で.exrイメージシーケンスをFile Name Strictで照合する場合は、従来のresolve_otio_prepare_for_flame_strict.pyを使用します。

resolve_otio_prepare_for_flame_strict.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import argparse
import json
import re


def strip_clip_name(name):
    name = re.sub(r"\[\d+-\d+\]", "", name)
    name = re.sub(r"\.[^.]+$", "", name)
    return name.rstrip("._- ")


def process(obj):
    changed_clip_name = 0
    changed_name_prefix = 0

    def walk(item):
        nonlocal changed_clip_name, changed_name_prefix

        if isinstance(item, dict):
            schema = item.get("OTIO_SCHEMA")

            # File Name Strict用:
            # ImageSequenceReference.name を name_prefix にコピー
            if schema == "ImageSequenceReference.1":
                name = item.get("name")
                prefix = item.get("name_prefix")

                if isinstance(name, str) and name != prefix:
                    item["name_prefix"] = name
                    changed_name_prefix += 1

            # Name Strict用:
            # Clip.2.name だけ短くする
            if schema == "Clip.2":
                name = item.get("name")

                if isinstance(name, str):
                    new_name = strip_clip_name(name)

                    if new_name != name:
                        item["name"] = new_name
                        changed_clip_name += 1

            for value in item.values():
                walk(value)

        elif isinstance(item, list):
            for value in item:
                walk(value)

    walk(obj)
    return changed_clip_name, changed_name_prefix


def main():
    parser = argparse.ArgumentParser(
        description="Prepare Resolve OTIO for Flame Name and File Name Strict matching."
    )
    parser.add_argument("input_otio")
    parser.add_argument("output_otio")

    args = parser.parse_args()

    with open(args.input_otio, "r", encoding="utf-8") as f:
        data = json.load(f)

    changed_clip_name, changed_name_prefix = process(data)

    with open(args.output_otio, "w", encoding="utf-8") as f:
        json.dump(data, f, indent=4)

    print(f"Changed Clip.2 names       : {changed_clip_name}")
    print(f"Changed name_prefix fields : {changed_name_prefix}")
    print(f"Input  : {args.input_otio}")
    print(f"Output : {args.output_otio}")


if __name__ == "__main__":
    main()

まとめ

今回の検証で、Flame 2027.2ではDaVinci Resolveの.exrイメージシーケンスに対するFile Name Strictの照合が、2027.1とは異なることが確認できました。

また、Name Strictについては、.otioファイルのClip.2.nameがFlameのセグメントネームに反映されるため、拡張子やフレーム範囲を削除することで正しく照合できることも確認しています。

なお、今回の結果はFlame 2027.1 / 2027.2で使用したテスト素材と.otioファイルによる検証結果です。