fix(fcpxml): tracking-shape id duplicado ao cortar clipe com Cinematic
Object-tracker/tracking-shape (dado de rastreamento de objeto preservado do asset original) mantinha o mesmo id em cada deepcopy feito por split_clip/cut_clip_ranges, e o FCP acabava rejeitando o arquivo com "ID tr1 already defined" depois de vários cortes. Mesmo mecanismo do bug já corrigido para text-style-def, agora coberto também para tracking-shape. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
2ad5854570
commit
635d1bb553
@@ -97,6 +97,8 @@ class ModifierCore:
|
||||
self.fps = self._detect_fps()
|
||||
# Lazily filled on the first generated title; see _unique_text_style_id.
|
||||
self._text_style_ids: Optional[set] = None
|
||||
# Lazily filled on the first clip split/cut; see _unique_tracking_shape_id.
|
||||
self._tracking_shape_ids: Optional[set] = None
|
||||
self._build_resource_index()
|
||||
self._build_clip_index()
|
||||
|
||||
|
||||
@@ -125,6 +125,7 @@ class CutMixin:
|
||||
new_clip, current_start, segment_duration
|
||||
)
|
||||
self._reassign_text_style_ids(new_clip)
|
||||
self._reassign_tracking_shape_ids(new_clip)
|
||||
|
||||
spine.insert(clip_index + len(new_clips), new_clip)
|
||||
new_clips.append(new_clip)
|
||||
@@ -243,6 +244,7 @@ class CutMixin:
|
||||
new_clip.set('duration', seg_duration.to_fcpxml())
|
||||
self._filter_children_for_segment(new_clip, seg_start, seg_duration)
|
||||
self._reassign_text_style_ids(new_clip)
|
||||
self._reassign_tracking_shape_ids(new_clip)
|
||||
spine.insert(clip_index + len(new_clips), new_clip)
|
||||
new_clips.append(new_clip)
|
||||
current_offset = current_offset + seg_duration
|
||||
|
||||
@@ -164,6 +164,44 @@ class TitlesMixin:
|
||||
for ref_el in clip.findall(f".//text-style[@ref='{old_id}']"):
|
||||
ref_el.set('ref', new_id)
|
||||
|
||||
def _unique_tracking_shape_id(self, base: str) -> str:
|
||||
"""Return a document-unique ``id`` for a ``<tracking-shape>``."""
|
||||
stem = base or "tr"
|
||||
if self._tracking_shape_ids is None:
|
||||
self._tracking_shape_ids = {
|
||||
ts.get('id') for ts in self.root.findall('.//tracking-shape')
|
||||
}
|
||||
candidate = f"{stem}_0"
|
||||
counter = 0
|
||||
while candidate in self._tracking_shape_ids:
|
||||
counter += 1
|
||||
candidate = f"{stem}_{counter}"
|
||||
self._tracking_shape_ids.add(candidate)
|
||||
return candidate
|
||||
|
||||
def _reassign_tracking_shape_ids(self, clip: ET.Element) -> None:
|
||||
"""Give every ``<tracking-shape>`` inside a just-deepcopy'd *clip* a
|
||||
fresh document-unique id.
|
||||
|
||||
Same mechanism as ``_reassign_text_style_ids``: ``split_clip``/
|
||||
``cut_clip_ranges`` deepcopy the clip once per resulting segment, so
|
||||
Cinematic object-tracking data (``<object-tracker><tracking-shape
|
||||
id="tr1">``, preserved from the source asset's sidecar) keeps the
|
||||
exact same id in every copy. A single cut is harmless — but the
|
||||
batch chain re-cuts the same clip at each step, multiplying the
|
||||
duplicate until the DTD validator rejects the file with "ID tr1
|
||||
already defined".
|
||||
"""
|
||||
for shape in clip.findall('.//tracking-shape'):
|
||||
old_id = shape.get('id')
|
||||
if not old_id:
|
||||
continue
|
||||
base = re.sub(r'_\d+$', '', old_id)
|
||||
new_id = self._unique_tracking_shape_id(base)
|
||||
if new_id == old_id:
|
||||
continue
|
||||
shape.set('id', new_id)
|
||||
|
||||
def _make_text_title_clip(
|
||||
self,
|
||||
effect_id: str,
|
||||
|
||||
Reference in New Issue
Block a user