Source code for camtasia.export.edl

"""Export timeline as CMX 3600 EDL format."""
from __future__ import annotations

from fractions import Fraction
from pathlib import Path
from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from camtasia.project import Project


def _format_timecode(seconds: float, fps: int = 30) -> str:
    """Format seconds as SMPTE timecode HH:MM:SS:FF."""
    sign = '-' if seconds < 0 else ''
    seconds = abs(seconds)
    h = int(seconds // 3600)
    m = int((seconds % 3600) // 60)
    s = int(seconds % 60)
    f = int((seconds % 1) * fps)
    return f'{sign}{h:02d}:{m:02d}:{s:02d}:{f:02d}'


[docs] def export_edl( project: Project, output_path: str | Path, *, title: str = 'Untitled', fps: int = 30, ) -> Path: """Export timeline as a CMX 3600 EDL file. Maps each clip to an EDL event with source file, in/out points, and record in/out points. Args: project: The project to export. output_path: Path for the .edl file. title: EDL title. fps: Frame rate for timecode calculation. Returns: The output path. """ from camtasia.timing import ticks_to_seconds path = Path(output_path) lines = [ f'TITLE: {title}', 'FCM: NON-DROP FRAME', '', ] event_num = 1 for track in project.timeline.tracks: for clip in track.clips: start = ticks_to_seconds(clip.start) end = start + ticks_to_seconds(clip.duration) # Source name from media bin if available source = 'AX' if clip.source_id is not None: try: media = project.media_bin[clip.source_id] source = media.identity except KeyError: pass # Determine edit type is_unified = clip.clip_type == 'UnifiedMedia' video_types = ('VMFile', 'IMFile', 'ScreenVMFile', 'ScreenIMFile', 'PlaceholderMedia', 'Group', 'UnifiedMedia', 'Callout') if clip.clip_type == 'StitchedMedia': # Check sub-clips to determine if video or audio sub_types = {m.get('_type') for m in clip._data.get('medias', [])} # pragma: no cover edit_type = 'V' if sub_types & {'VMFile', 'IMFile', 'ScreenVMFile', 'ScreenIMFile'} else 'A' # pragma: no cover else: edit_type = 'V' if clip.clip_type in video_types else 'A' src_in_offset = ticks_to_seconds(int(Fraction(str(clip.media_start)))) src_in = _format_timecode(src_in_offset, fps) src_out = _format_timecode(src_in_offset + end - start, fps) rec_in = _format_timecode(start, fps) rec_out = _format_timecode(end, fps) lines.append( f'{event_num:03d} {source:<8s} {edit_type} C ' f'{src_in} {src_out} {rec_in} {rec_out}' ) event_num += 1 if is_unified: lines.append( # pragma: no cover f'{event_num:03d} {source:<8s} A C ' # pragma: no cover f'{src_in} {src_out} {rec_in} {rec_out}' # pragma: no cover ) # pragma: no cover event_num += 1 # pragma: no cover lines.append('') path.write_text('\n'.join(lines)) return path