You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

OpenTimelineIO导入FCP XML无法定位剪辑,如何解决?

问题

我有多个Final Cut Pro和Adobe Premiere项目需要转换为Kdenlive格式,使用OpenTimelineIO编写转换代码后,Kdenlive中项目的剪辑布局与时间码正确,但所有剪辑均缺失。XML文件包含外部驱动器的媒体路径,却被OpenTimelineIO识别为MissingReference,替换%20为空格后问题仍存在。请问如何修改XML文件或通过猴子补丁代码让程序找到外部驱动器中的剪辑?

代码示例:

import opentimelineio as otio

fp = "/path/to/file/ep4_fixed.fcpxml"
output_fp = fp.replace(".fcpxml", ".kdenlive")
assert fp != output_fp

# Load the FCP XML file
input_otio = otio.adapters.read_from_file(fp)

# Extract the first Timeline from the SerializableCollection
timeline = next((item for item in input_otio if isinstance(item, otio.schema.Timeline)), None)

for item in input_otio:
    if isinstance(item, otio.schema.Timeline):
        print("Timeline found")
        print(item)
    else:
        print("Not a timeline: ", item)

if timeline:
    # Save the timeline as a Kdenlive project
    otio.adapters.write_to_file(timeline, output_fp, adapter_name="kdenlive")
else:
    print("No timeline found in the input file.")

XML资产示例:

<asset id="r117" name="Duck" uid="8E011DED69D190CFE0018A6D24E90A31" start="0s" duration="518344/44100s" hasAudio="1" audioSources="1" audioChannels="2" audioRate="44100">
            <media-rep kind="original-media" sig="8E011DED69D190CFE0018A6D24E90A31" src="file:///Volumes/Video/BulgyPT.fcpbundle/ep1-6/Original%20Media/Duck.mp3">
                <bookmark>Ym9va8AEA...EAAAAAAAAAA==</bookmark>
            </media-rep>
            <metadata>
                <md key="com.apple.proapps.mio.ingestDate" value="2020-07-05 18:19:58 +0100"/>
            </metadata>
        </asset>

对应OTIO输出:

otio.schema.Clip(name='Duck', media_reference=otio.schema.MissingReference(name='', available_range=None, available_image_bounds=None, metadata={}), source_range=otio.opentime.TimeRange(start_time=otio.opentime.RationalTime(value=0, rate=25), duration=otio.opentime.RationalTime(value=170, rate=25)), metadata={})

解决方案

方案一:修改XML文件路径格式

OTIO对file://开头的URL路径解析存在兼容性问题,直接改用本地文件系统路径即可:

  • 将XML中media-rep标签的src属性值从file:///Volumes/Video/...格式改为纯本地路径,比如/Volumes/Video/BulgyPT.fcpbundle/ep1-6/Original Media/Duck.mp3(注意把%20替换为真实空格)
  • 确保路径中没有其他URL编码字符,所有特殊字符直接用原始符号

方案二:代码层面修复路径解析

方法1:全局猴子补丁替换路径解析逻辑

在加载XML前,替换OTIO的媒体引用解析逻辑,自动修复MissingReference:

import opentimelineio as otio
from urllib.parse import unquote

# 猴子补丁:重写read_from_file,自动修复媒体路径
original_read = otio.adapters.read_from_file

def patched_read(file_path, **kwargs):
    # 正常加载项目
    project = original_read(file_path, **kwargs)
    
    # 递归修复所有剪辑的媒体引用
    def fix_media(item):
        if isinstance(item, otio.schema.Clip) and isinstance(item.media_reference, otio.schema.MissingReference):
            # 这里可根据实际场景从XML或元数据提取真实路径,示例用给定路径
            original_url = "file:///Volumes/Video/BulgyPT.fcpbundle/ep1-6/Original%20Media/Duck.mp3"
            local_path = unquote(original_url.replace("file://", ""))
            # 替换为有效外部引用
            item.media_reference = otio.schema.ExternalReference(
                target_url=local_path,
                available_range=item.source_range
            )
        # 处理嵌套轨道
        if hasattr(item, "children"):
            for child in item.children:
                fix_media(child)
    
    # 处理整个项目集合或时间线
    if isinstance(project, otio.schema.SerializableCollection):
        for elem in project:
            fix_media(elem)
    else:
        fix_media(project)
    
    return project

# 替换原函数
otio.adapters.read_from_file = patched_read

# 原有转换代码
fp = "/path/to/file/ep4_fixed.fcpxml"
output_fp = fp.replace(".fcpxml", ".kdenlive")
assert fp != output_fp

input_otio = otio.adapters.read_from_file(fp)
timeline = next((item for item in input_otio if isinstance(item, otio.schema.Timeline)), None)

if timeline:
    otio.adapters.write_to_file(timeline, output_fp, adapter_name="kdenlive")
else:
    print("输入文件中未找到时间线。")

方法2:解析XML建立路径映射,精准修复

这种方法更可靠,先从XML中提取所有资产的路径,再映射到OTIO的剪辑上:

import xml.etree.ElementTree as ET
import opentimelineio as otio
from urllib.parse import unquote

# 第一步:解析XML,建立asset ID到本地路径的映射
xml_path = "/path/to/file/ep4_fixed.fcpxml"
tree = ET.parse(xml_path)
root = tree.getroot()
asset_path_map = {}

for asset in root.findall(".//asset"):
    media_rep = asset.find(".//media-rep[@kind='original-media']")
    if media_rep and "src" in media_rep.attrib:
        asset_id = asset.attrib["id"]
        # 解码URL路径为本地路径
        local_path = unquote(media_rep.attrib["src"].replace("file://", ""))
        asset_path_map[asset_id] = local_path

# 第二步:加载OTIO项目并修复媒体引用
input_otio = otio.adapters.read_from_file(xml_path)

def fix_clips(item):
    if isinstance(item, otio.schema.Clip):
        # 从剪辑元数据中获取对应的asset ID(FCPXML导入后会保留该字段)
        asset_id = item.metadata.get("fcpxml", {}).get("asset_id")
        if asset_id in asset_path_map:
            # 替换MissingReference为有效外部引用
            item.media_reference = otio.schema.ExternalReference(
                target_url=asset_path_map[asset_id],
                available_range=item.source_range
            )
    # 递归处理子元素
    if hasattr(item, "children"):
        for child in item.children:
            fix_clips(child)

# 遍历所有项目元素修复
for elem in input_otio:
    fix_clips(elem)

# 第三步:保存为Kdenlive项目
timeline = next((item for item in input_otio if isinstance(item, otio.schema.Timeline)), None)
if timeline:
    output_fp = xml_path.replace(".fcpxml", ".kdenlive")
    otio.adapters.write_to_file(timeline, output_fp, adapter_name="kdenlive")
else:
    print("输入文件中未找到时间线。")

内容的提问来源于stack exchange,提问作者ginjaemocoes

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 00:35:03