Source code for camtasia.operations.merge
"""Merge tracks from one project into another."""
from __future__ import annotations
import copy
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from camtasia.project import Project
def _remap_clip_ids(clip_data: dict, id_counter: list[int], id_map: dict[int, int]) -> None:
"""Recursively remap all id and src fields in a clip and its children."""
old_id = clip_data.get('id')
if old_id is not None:
new_id = id_counter[0]
id_counter[0] += 1
clip_data['id'] = new_id
if 'src' in clip_data and clip_data['src'] in id_map:
clip_data['src'] = id_map[clip_data['src']]
for key in ('video', 'audio'):
if key in clip_data:
_remap_clip_ids(clip_data[key], id_counter, id_map)
for track in clip_data.get('tracks', []):
for media in track.get('medias', []):
_remap_clip_ids(media, id_counter, id_map)
[docs]
def merge_tracks(
source: Project,
target: Project,
*,
offset_seconds: float = 0.0,
) -> int:
"""Copy all non-empty tracks from source into target.
Clips are offset by offset_seconds on the target timeline.
Media entries are copied to the target's source bin with new IDs.
Args:
source: Project to copy tracks from.
target: Project to copy tracks into.
offset_seconds: Time offset for all copied clips.
Returns:
Number of tracks copied.
"""
from camtasia.timing import seconds_to_ticks
offset_ticks = seconds_to_ticks(offset_seconds)
# Build media ID mapping (source ID -> target ID)
id_map: dict[int, int] = {}
for media in source.media_bin:
existing = target.find_media_by_name(media.identity)
if existing:
id_map[media.id] = existing.id
else:
new_id = target.media_bin.next_id()
entry = copy.deepcopy(media._data)
entry['id'] = new_id
target._data['sourceBin'].append(entry)
id_map[media.id] = new_id
# Copy non-empty tracks
count = 0
for track in source.timeline.tracks:
if len(track) == 0:
continue
new_track = target.timeline.add_track(track.name)
id_counter = [target.timeline.next_clip_id()]
for clip_data in track._data.get('medias', []):
new_clip = copy.deepcopy(clip_data)
_remap_clip_ids(new_clip, id_counter, id_map)
new_clip['start'] = new_clip.get('start', 0) + offset_ticks
new_track._data.setdefault('medias', []).append(new_clip)
count += 1
return count