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()
|
self.fps = self._detect_fps()
|
||||||
# Lazily filled on the first generated title; see _unique_text_style_id.
|
# Lazily filled on the first generated title; see _unique_text_style_id.
|
||||||
self._text_style_ids: Optional[set] = None
|
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_resource_index()
|
||||||
self._build_clip_index()
|
self._build_clip_index()
|
||||||
|
|
||||||
|
|||||||
@@ -125,6 +125,7 @@ class CutMixin:
|
|||||||
new_clip, current_start, segment_duration
|
new_clip, current_start, segment_duration
|
||||||
)
|
)
|
||||||
self._reassign_text_style_ids(new_clip)
|
self._reassign_text_style_ids(new_clip)
|
||||||
|
self._reassign_tracking_shape_ids(new_clip)
|
||||||
|
|
||||||
spine.insert(clip_index + len(new_clips), new_clip)
|
spine.insert(clip_index + len(new_clips), new_clip)
|
||||||
new_clips.append(new_clip)
|
new_clips.append(new_clip)
|
||||||
@@ -243,6 +244,7 @@ class CutMixin:
|
|||||||
new_clip.set('duration', seg_duration.to_fcpxml())
|
new_clip.set('duration', seg_duration.to_fcpxml())
|
||||||
self._filter_children_for_segment(new_clip, seg_start, seg_duration)
|
self._filter_children_for_segment(new_clip, seg_start, seg_duration)
|
||||||
self._reassign_text_style_ids(new_clip)
|
self._reassign_text_style_ids(new_clip)
|
||||||
|
self._reassign_tracking_shape_ids(new_clip)
|
||||||
spine.insert(clip_index + len(new_clips), new_clip)
|
spine.insert(clip_index + len(new_clips), new_clip)
|
||||||
new_clips.append(new_clip)
|
new_clips.append(new_clip)
|
||||||
current_offset = current_offset + seg_duration
|
current_offset = current_offset + seg_duration
|
||||||
|
|||||||
@@ -164,6 +164,44 @@ class TitlesMixin:
|
|||||||
for ref_el in clip.findall(f".//text-style[@ref='{old_id}']"):
|
for ref_el in clip.findall(f".//text-style[@ref='{old_id}']"):
|
||||||
ref_el.set('ref', new_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(
|
def _make_text_title_clip(
|
||||||
self,
|
self,
|
||||||
effect_id: str,
|
effect_id: str,
|
||||||
|
|||||||
Reference in New Issue
Block a user