Class: ButterCut::FCPX

Inherits:
EditorBase show all
Defined in:
lib/buttercut/fcpx.rb

Overview

Final Cut Pro X (FCPXML 1.8) implementation.

Constant Summary collapse

FORMAT_ID =
"r1".freeze

Constants inherited from EditorBase

EditorBase::DEFAULT_INITIAL_OFFSET, EditorBase::DEFAULT_START_TIME, EditorBase::DEFAULT_VOLUME_ADJUSTMENT

Instance Attribute Summary

Attributes inherited from EditorBase

#clips, #initial_offset, #volume_adjustment

Instance Method Summary collapse

Methods inherited from EditorBase

#add_fractions, #audio_sample_rate, #build_asset_map, #build_timeline_clips, #clip_timecode_fraction, #clip_timecode_string, #color_space, #drop_frame_timecode?, #drop_frames_for_rate, #duration_to_fraction, #escape_xml, #extract_metadata, #format_audio_rate, #format_color_space, #format_frame_duration, #format_frame_rate, #format_height, #format_nominal_frame_rate, #format_width, #fraction_to_rational, #frame_duration, #frame_duration_rational_for, #frame_rate, #frames_for_fraction, #gcd, #generate_uuid, #get_absolute_path, #get_basename, #get_filename, #initialize, #nominal_frame_rate, #path_to_file_url, #round_to_frame_boundary, #save, #seconds_to_fraction, #subtract_fractions, #time_value_zero?, #video_duration, #video_height, #video_width

Constructor Details

This class inherits a constructor from ButterCut::EditorBase

Instance Method Details

#to_xmlObject

Raises:

  • (ArgumentError)


9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/buttercut/fcpx.rb', line 9

def to_xml
  raise ArgumentError, "No clips provided" if clips.empty?

  asset_map = build_asset_map
  timeline_frame_duration = format_frame_duration
  timeline_clips, sequence_duration = build_timeline_clips(asset_map, timeline_frame_duration)

  event_uid = generate_uuid
  project_uid = generate_uuid

  first_path = clips.first[:path]
  first_filename = get_filename(first_path)
  project_basename = get_basename(first_filename)
  event_name = project_basename
  timestamped_project_name = "#{project_basename} #{timestamp_suffix}"

  builder = Nokogiri::XML::Builder.new(encoding: 'utf-8') do |xml|
    xml.fcpxml(version: '1.8') do
      xml.resources do
        xml.format(
          id: FORMAT_ID,
          height: format_height,
          width: format_width,
          frameDuration: format_frame_duration,
          colorSpace: format_color_space
        )

        asset_map.each_value do |asset|
          xml.asset(
            id: asset[:asset_id],
            name: asset[:filename],
            uid: asset[:asset_uid],
            src: asset[:file_url],
            start: asset[:timecode],
            audioRate: asset[:audio_rate],
            hasAudio: '1',
            hasVideo: '1',
            format: FORMAT_ID,
            duration: asset[:asset_duration]
          )
        end
      end

      xml.library(location: './') do
        xml.event(name: event_name, uid: event_uid) do
          xml.project(name: timestamped_project_name, uid: project_uid, modDate: '2025-10-31 17:25:16 GMT-7') do
            xml.sequence(duration: sequence_duration, format: FORMAT_ID, tcStart: '0s', audioRate: '48k') do
              xml.spine do
                timeline_clips.each do |clip|
                  xml.send('asset-clip',
                    name: clip[:filename],
                    ref: clip[:asset_id],
                    start: clip[:start],
                    offset: clip[:timeline_offset],
                    duration: clip[:duration],
                    audioRole: 'dialogue'
                  ) do
                    xml.send('adjust-volume', amount: volume_adjustment)
                  end
                end
              end
            end
          end
        end
      end
    end
  end

  builder.to_xml
end