edl 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,7 @@ module EDL
2
2
  # When initialized with a file and passed an EDL, will generate thumbnail images
3
3
  # of the first frame of every event. It is assumed that the movie file starts at the same
4
4
  # frame as the first EDL event.
5
- class Grabber
5
+ class Grabber #:nodoc:
6
6
  attr_accessor :ffmpeg_bin, :offset
7
7
  def initialize(with_file)
8
8
  @source_path = with_file
@@ -22,8 +22,7 @@ module EDL
22
22
  end
23
23
 
24
24
  def generate_grab(evt, at, to_file)
25
- # cmd = "#{ffmpeg_bin} -i #{@source_path} -an -ss #{at} -vframes 1 -r #{at.fps} -y #{to_file}%d.jpg"
26
- cmd = "#{ffmpeg_bin} -i #{@source_path} -an -ss #{at} -vframes 1 -y #{to_file}%d.jpg"
25
+ cmd = "#{ffmpeg_bin} -i #{@source_path} -an -ss #{at.with_frames_as_fraction} -vframes 1 -y #{to_file}%d.jpg"
27
26
  `#{cmd}`
28
27
  end
29
28
  end
@@ -0,0 +1,45 @@
1
+ module EDL
2
+ # Represents a timewarp. Will be placed in EDL::Event#timewarp
3
+ # For a reversed clip, the source start we get from the EDL in the src start
4
+ # is the LAST used frame. For a pos rate clip, the source start is the bona fide source start. Nice eh?
5
+ class Timewarp
6
+
7
+ # What is the actual framerate of the clip (float)
8
+ attr_accessor :actual_framerate
9
+ attr_accessor :clip #:nodoc:
10
+
11
+ # Does this timewarp reverse the clip?
12
+ def reverse?
13
+ @actual_framerate < 0
14
+ end
15
+
16
+ # Get the speed in percent
17
+ def speed_in_percent
18
+ (@actual_framerate / @clip.rec_start_tc.fps) * 100
19
+ end
20
+ alias_method :speed, :speed_in_percent
21
+
22
+ # Compute the length of the clip we need to capture. The length is computed in frames and
23
+ # is always rounded up (better one frame more than one frame less!)
24
+ def actual_length_of_source
25
+ # First, get the length of the clip including a transition. This is what we are scaled to.
26
+ target_len = @clip.rec_length_with_transition.to_f
27
+ # Determine the framerate scaling factor, this is the speed
28
+ factor = @actual_framerate / @clip.rec_start_tc.fps
29
+ (target_len * factor).ceil.abs
30
+ end
31
+
32
+ # What is the starting frame for the captured clip? If we are a reverse, then the src start of the
33
+ # clip is our LAST frame, otherwise it's the first
34
+ def source_used_from
35
+ # TODO: account for the 2 frame deficiency which is suspicious
36
+ compensation = 2
37
+ reverse? ? (@clip.src_start_tc - actual_length_of_source + compensation) : @clip.src_start_tc
38
+ end
39
+
40
+ # Where to end the capture? This is also dependent on whether we are a reverse or not
41
+ def source_used_upto
42
+ source_used_from + actual_length_of_source
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,23 @@
1
+ module EDL
2
+ # Represents a transition. We currently only support dissolves and SMPTE wipes
3
+ # Will be avilable as EDL::Clip#transition
4
+ class Transition
5
+
6
+ # Length of the transition in frames
7
+ attr_accessor :duration
8
+
9
+ # Which effect is used (like CROSS DISSOLVE)
10
+ attr_accessor :effect
11
+ end
12
+
13
+ # Represents a dissolve
14
+ class Dissolve < Transition
15
+ end
16
+
17
+ # Represents an SMPTE wipe
18
+ class Wipe < Transition
19
+
20
+ # Which SMPTE wipe is needed
21
+ attr_accessor :smpte_wipe_index
22
+ end
23
+ end
Binary file
@@ -0,0 +1,9 @@
1
+ TITLE: REVSIMPLE
2
+ FCM: NON-DROP FRAME
3
+
4
+ 001 FOO V C 01:00:39:23 01:01:19:23 01:00:00:00 01:00:40:00
5
+ * FROM CLIP NAME: BARS_40S.MOV
6
+ * COMMENT:
7
+ M2 FOO -025.0 01:00:39:23
8
+
9
+ 
@@ -0,0 +1,11 @@
1
+ TITLE: SPEEDUP_AND_FADEOUT
2
+ FCM: NON-DROP FRAME
3
+
4
+ 001 FOO V C 01:00:00:00 01:00:27:14 01:00:00:00 01:00:27:14
5
+ 001 BL V D 025 00:00:00:00 00:00:01:00 01:00:27:14 01:00:28:14
6
+ * EFFECT NAME: CROSS DISSOLVE
7
+ * FROM CLIP NAME: BARS_40S.MOV
8
+ * COMMENT:
9
+ M2 FOO 035.0 01:00:00:00
10
+
11
+ 
@@ -0,0 +1,11 @@
1
+ TITLE: TESTREV
2
+ FCM: NON-DROP FRAME
3
+
4
+ 001 FOO V C 01:00:39:23 01:01:07:12 01:00:00:00 01:00:27:14
5
+ 001 BL V D 025 00:00:00:00 00:00:01:00 01:00:27:14 01:00:28:14
6
+ * EFFECT NAME: CROSS DISSOLVE
7
+ * FROM CLIP NAME: BARS_40S.MOV
8
+ * COMMENT:
9
+ M2 FOO -035.0 01:00:39:23
10
+
11
+ 
@@ -2,7 +2,4 @@ TITLE: SPLICEME
2
2
  FCM: NON-DROP FRAME
3
3
 
4
4
  001 006I V C 06:42:50:18 06:42:52:13 01:00:00:00 01:00:01:20
5
-
6
- 002 006I V C 06:42:52:14 06:42:52:16 01:00:01:21 01:00:01:23
7
-
8
- 003 006I V C 06:42:52:17 06:42:52:24 01:00:01:24 01:00:02:06
5
+ 002 006I V C 06:42:52:14 06:42:52:16 01:00:10:21 01:00:10:23
@@ -4,155 +4,298 @@ require File.dirname(__FILE__) + '/../lib/edl/grabber'
4
4
 
5
5
  require 'rubygems'
6
6
  require 'test/unit'
7
+ require 'test/spec'
7
8
  require 'flexmock'
8
9
  require 'flexmock/test_unit'
9
10
 
10
- TRAILER_EDL = File.dirname(__FILE__) + '/samples/TRAILER_EDL.edl'
11
- SIMPLE_DISSOLVE = File.dirname(__FILE__) + '/samples/SIMPLE_DISSOLVE.EDL'
12
- SPLICEME = File.dirname(__FILE__) + '/samples/SPLICEME.EDL'
13
- SIMPLE_TIMEWARP = File.dirname(__FILE__) + '/samples/TIMEWARP.EDL'
14
- SLOMO_TIMEWARP = File.dirname(__FILE__) + '/samples/TIMEWARP_HALF.EDL'
15
- FORTY_FIVER = File.dirname(__FILE__) + '/samples/45S_SAMPLE.EDL'
16
- REVERSE = File.dirname(__FILE__) + '/samples/REVERSE.EDL'
11
+ TRAILER_EDL = File.dirname(__FILE__) + '/samples/TRAILER_EDL.edl'
12
+ SIMPLE_DISSOLVE = File.dirname(__FILE__) + '/samples/SIMPLE_DISSOLVE.EDL'
13
+ SPLICEME = File.dirname(__FILE__) + '/samples/SPLICEME.EDL'
14
+ SIMPLE_TIMEWARP = File.dirname(__FILE__) + '/samples/TIMEWARP.EDL'
15
+ SLOMO_TIMEWARP = File.dirname(__FILE__) + '/samples/TIMEWARP_HALF.EDL'
16
+ FORTY_FIVER = File.dirname(__FILE__) + '/samples/45S_SAMPLE.EDL'
17
+ AVID_REVERSE = File.dirname(__FILE__) + '/samples/REVERSE.EDL'
18
+ SPEEDUP_AND_FADEOUT = File.dirname(__FILE__) + '/samples/SPEEDUP_AND_FADEOUT.EDL'
19
+ SPEEDUP_REVERSE_AND_FADEOUT = File.dirname(__FILE__) + '/samples/SPEEDUP_REVERSE_AND_FADEOUT.EDL'
20
+ FCP_REVERSE = File.dirname(__FILE__) + '/samples/FCP_REVERSE.EDL'
17
21
 
18
- class TestEvent < Test::Unit::TestCase
19
- def test_attributes_defined
22
+ class String
23
+ def tc(fps = Timecode::DEFAULT_FPS)
24
+ Timecode.parse(self, fps)
25
+ end
26
+ end
27
+
28
+ context "An Event should" do
29
+ specify "define the needed attributes" do
20
30
  evt = EDL::Event.new
21
31
  %w( num reel track src_start_tc src_end_tc rec_start_tc rec_end_tc ).each do | em |
22
- assert_respond_to evt, em
32
+ evt.should.respond_to em
23
33
  end
24
34
  end
25
- end
35
+
36
+ specify "support hash initialization" do
37
+ evt = EDL::Event.new(:src_start_tc => "01:00:00:00".tc)
38
+ evt.src_start_tc.should.equal "01:00:00:00".tc
39
+ end
26
40
 
27
- class Test::Unit::TestCase
28
- def parse_evt(matcher_klass, line)
29
- stack = []
30
- matcher_klass.new.apply(stack, line)
31
- stack.pop
41
+ specify "support block initialization" do
42
+ evt = EDL::Event.new do | e |
43
+ e.src_start_tc = "01:00:00:04".tc
44
+ end
45
+ evt.src_start_tc.should.equal "01:00:00:04".tc
32
46
  end
33
- end
34
47
 
35
- class TestParser < Test::Unit::TestCase
36
- def test_inst
37
- assert_nothing_raised { EDL::Parser.new }
48
+ specify "respond to ends_with_transition? with false if outgoing_transition_duration is zero" do
49
+ evt = EDL::Event.new
50
+ evt.outgoing_transition_duration = 0
51
+ evt.ends_with_transition?.should.equal false
38
52
  end
39
53
 
54
+ specify "respond to ends_with_transition? with true if outgoing_transition_duration set above zero" do
55
+ evt = EDL::Event.new
56
+ evt.outgoing_transition_duration = 24
57
+ evt.ends_with_transition?.should.equal true
58
+ end
59
+
60
+ specify "respond to has_timewarp? with false if no timewarp assigned" do
61
+ evt = EDL::Event.new(:timewarp => nil)
62
+ evt.has_timewarp?.should.equal false
63
+ end
64
+
65
+ specify "respond to has_timewarp? with true if a timewarp is assigned" do
66
+ evt = EDL::Event.new(:timewarp => true)
67
+ evt.has_timewarp?.should.equal true
68
+ end
69
+
70
+ specify "report rec_length as a difference of record timecodes" do
71
+ evt = EDL::Event.new(:rec_start_tc => "1h".tc, :rec_end_tc => "1h 10s 2f".tc )
72
+ evt.rec_length.should.equal "10s 2f".tc.to_i
73
+ end
74
+
75
+ specify "report rec_length_with_transition as a difference of record timecodes if no transition set" do
76
+ evt = EDL::Event.new(:rec_start_tc => "1h".tc, :rec_end_tc => "1h 10s 2f".tc, :outgoing_transition_duration => 0)
77
+ evt.rec_length_with_transition.should.equal "10s 2f".tc.to_i
78
+ end
79
+
80
+ specify "add transition length to rec_length_with_transition if a transition is set" do
81
+ evt = EDL::Event.new(:rec_start_tc => "1h".tc, :rec_end_tc => "1h 10s 2f".tc, :outgoing_transition_duration => 10)
82
+ evt.rec_length_with_transition.should.equal("10s 2f".tc.to_i + 10)
83
+ end
84
+
85
+ specify "return a default array for comments" do
86
+ EDL::Event.new.comments.should.be.kind_of Enumerable
87
+ end
88
+
89
+ specify "respond false to has_transition? if incoming transition is set" do
90
+ EDL::Event.new(:transition => nil).has_transition?.should.equal false
91
+ end
92
+
93
+ specify "respond true to has_transition? if incoming transition is set" do
94
+ EDL::Event.new(:transition => true).has_transition?.should.equal true
95
+ end
96
+
97
+ specify "respond true to black? if reel is BL" do
98
+ EDL::Event.new(:reel => "BL").should.black
99
+ EDL::Event.new(:reel => "001").should.not.black
100
+ end
101
+
102
+ specify "respond true to generator? if reel is BL or AX" do
103
+ EDL::Event.new(:reel => "BL").should.generator
104
+ EDL::Event.new(:reel => "AX").should.generator
105
+ EDL::Event.new(:reel => "001").should.not.generator
106
+ end
107
+
108
+ specify "report src_length as rec_length_with_transition" do
109
+ e = EDL::Event.new(:rec_start_tc => "2h".tc, :rec_end_tc => "2h 2s".tc)
110
+ e.src_length.should.equal "2s".tc.to_i
111
+ end
112
+
113
+ specify "support capture_length as an alias to src_length" do
114
+ tw = flexmock
115
+ tw.should_receive(:actual_length_of_source).and_return(:something)
116
+ e = EDL::Event.new(:timewarp => tw)
117
+ e.src_length.should.equal e.capture_length
118
+ end
119
+
120
+ specify "delegate src_length to the timewarp if it is there" do
121
+ tw = flexmock
122
+ tw.should_receive(:actual_length_of_source).and_return(:something).once
123
+ e = EDL::Event.new(:timewarp => tw)
124
+ e.src_length.should.equal :something
125
+ end
40
126
 
41
- def test_inits_matchers_with_framerate
127
+ specify "report reverse? and reversed? based on the timewarp" do
128
+ e = EDL::Event.new(:timewarp => nil)
129
+ e.should.not.be.reverse
130
+ e.should.not.be.reversed
131
+
132
+ tw = flexmock
133
+ tw.should_receive(:reverse?).and_return(true)
134
+
135
+ e = EDL::Event.new(:timewarp => tw)
136
+ e.should.be.reverse
137
+ e.should.be.reversed
138
+ end
139
+ end
140
+
141
+ context "A Parser should" do
142
+
143
+ specify "store the passed framerate" do
144
+ p = EDL::Parser.new(45)
145
+ p.should.respond_to :fps
146
+ p.fps.should.equal 45
147
+ end
148
+
149
+ specify "return matchers tuned with the passed framerate" do
42
150
  p = EDL::Parser.new(30)
43
151
  matchers = p.get_matchers
44
152
  event_matcher = matchers.find{|e| e.is_a?(EDL::EventMatcher) }
45
- assert_equal 30, event_matcher.fps
153
+ event_matcher.fps.should.equal 30
46
154
  end
47
155
 
48
- def test_timecode_from_elements
156
+ specify "create a Timecode from stringified elements" do
49
157
  elems = ["08", "04", "24", "24"]
50
- assert_nothing_raised { @tc = EDL::Parser.timecode_from_line_elements(elems, 30) }
51
- assert_kind_of Timecode, @tc
52
- assert_equal "08:04:24:24", @tc.to_s
53
- assert_equal 30, @tc.fps
54
- assert elems.empty?, "The elements used for timecode should have been removed from the array"
158
+ lambda{ @tc = EDL::Parser.timecode_from_line_elements(elems, 30) }.should.not.raise
159
+
160
+ @tc.should.be.kind_of Timecode
161
+ @tc.should.equal "08:04:24:24".tc(30)
162
+
163
+ elems.length.should.equal 0
55
164
  end
56
165
 
57
- def test_dissolve
166
+ specify "parse from a String" do
167
+ p = EDL::Parser.new
168
+ lambda{ @edl = p.parse File.read(SIMPLE_DISSOLVE) }.should.not.raise
169
+
170
+ @edl.should.be.kind_of EDL::List
171
+ @edl.length.should.equal 2
172
+ end
173
+
174
+ specify "parse from a File/IOish" do
175
+ p = EDL::Parser.new
176
+ lambda{ @edl = p.parse File.open(SIMPLE_DISSOLVE) }.should.not.raise
177
+
178
+ @edl.should.be.kind_of EDL::List
179
+ @edl.length.should.equal 2
180
+ end
181
+
182
+ specify "properly parse a dissolve" do
183
+ # TODO: reformulate
58
184
  p = EDL::Parser.new
59
- assert_nothing_raised{ @edl = p.parse File.open(SIMPLE_DISSOLVE) }
60
- assert_kind_of EDL::List, @edl
61
- assert_equal 2, @edl.events.length
185
+ lambda{ @edl = p.parse File.open(SIMPLE_DISSOLVE) }.should.not.raise
186
+
187
+ @edl.should.be.kind_of EDL::List
188
+ @edl.length.should.equal 2
62
189
 
63
- first = @edl.events[0]
64
- assert_kind_of EDL::Clip, first
190
+ first, second = @edl
191
+ first.should.be.kind_of EDL::Event
192
+ second.should.be.kind_of EDL::Event
65
193
 
66
- second = @edl.events[1]
67
- assert_kind_of EDL::Clip, second
68
- assert second.has_transition?
194
+ second.has_transition?.should.equal true
195
+ first.ends_with_transition?.should.equal true
196
+ second.ends_with_transition?.should.equal false
69
197
 
70
198
  no_trans = @edl.without_transitions
71
199
 
72
- assert_equal 2, no_trans.events.length
200
+ assert_equal 2, no_trans.length
73
201
  target_tc = (Timecode.parse('01:00:00:00') + 43)
74
- assert_equal target_tc, no_trans.events[0].rec_end_tc,
202
+ assert_equal target_tc, no_trans[0].rec_end_tc,
75
203
  "The incoming clip should have been extended by the length of the dissolve"
76
204
 
77
205
  target_tc = Timecode.parse('01:00:00:00')
78
- assert_equal target_tc, no_trans.events[1].rec_start_tc
206
+ assert_equal target_tc, no_trans[1].rec_start_tc
79
207
  "The outgoing clip should have been left in place"
80
208
  end
81
209
 
82
- def test_spliced
83
- p = EDL::Parser.new
84
- assert_nothing_raised{ @edl = p.parse(File.open(SPLICEME)) }
85
- assert_equal 3, @edl.events.length
86
-
87
- spliced = @edl.spliced
88
- assert_equal 1, spliced.events.length, "Should have been spliced to one event"
210
+ specify "return a spliced EDL if the sources allow" do
211
+ lambda{ @spliced = EDL::Parser.new.parse(File.open(SPLICEME)).spliced }.should.not.raise
212
+
213
+ @spliced.length.should.equal 1
214
+ @spliced[0].src_start_tc.should.equal '06:42:50:18'.tc
215
+ @spliced[0].src_end_tc.should.equal '06:42:52:16'.tc
89
216
  end
90
217
  end
91
218
 
92
- class TimewarpMatcherTest < Test::Unit::TestCase
93
-
94
- def test_parses_as_one_event
219
+ context "A TimewarpMatcher should" do
220
+
221
+ specify "not create any extra events when used within a Parser" do
95
222
  @edl = EDL::Parser.new.parse(File.open(SIMPLE_TIMEWARP))
96
- assert_kind_of EDL::List, @edl
97
- assert_equal 1, @edl.events.length
223
+ @edl.length.should.equal 1
98
224
  end
99
225
 
100
- def test_timewarp_attributes
101
- @edl = EDL::Parser.new.parse(File.open(SIMPLE_TIMEWARP))
102
- assert_kind_of EDL::List, @edl
103
- assert_equal 1, @edl.events.length
226
+ specify "properly describe a speedup" do
227
+ clip = EDL::Parser.new.parse(File.open(SIMPLE_TIMEWARP)).pop
228
+
229
+ tw = clip.timewarp
104
230
 
105
- clip = @edl.events[0]
106
- assert clip.has_timewarp?, "Should respond true to has_timewarp?"
107
- assert_not_nil clip.timewarp
108
- assert_kind_of EDL::Timewarp, clip.timewarp
231
+ tw.should.be.kind_of EDL::Timewarp
232
+ tw.source_used_upto.should.be > clip.src_end_tc
109
233
 
110
- assert clip.timewarp.actual_src_end_tc > clip.src_end_tc
111
- assert_equal "03:03:24:18", clip.timewarp.actual_src_end_tc.to_s
112
- assert_equal 124, clip.timewarp.actual_length_of_source
113
- assert !clip.timewarp.reverse?
234
+ tw.source_used_from.should.equal clip.src_start_tc
235
+ clip.timewarp.actual_length_of_source.should.equal 124
114
236
 
237
+ tw.reverse?.should.be false
115
238
  end
116
239
 
117
- def test_timwarp_slomo
118
- @edl = EDL::Parser.new.parse(File.open(SLOMO_TIMEWARP))
119
- clip = @edl.events[0]
120
- assert clip.has_timewarp?, "Should respond true to has_timewarp?"
121
- assert_not_nil clip.timewarp
122
- assert_kind_of EDL::Timewarp, clip.timewarp
240
+ specify "properly describe a slomo" do
241
+ clip = EDL::Parser.new.parse(File.open(SLOMO_TIMEWARP)).pop
123
242
 
124
- assert clip.timewarp.actual_src_end_tc < clip.src_end_tc
125
- assert_equal "03:03:19:24", clip.timewarp.actual_src_end_tc.to_s
126
- assert_equal 10, clip.length
127
- assert_equal 5, clip.timewarp.actual_length_of_source
128
- assert_equal 50, clip.timewarp.speed_in_percent.to_i
129
- assert !clip.timewarp.reverse?
243
+ clip.rec_length.should.equal 10
244
+ clip.src_length.should.equal 5
130
245
 
246
+ tw = clip.timewarp
247
+ tw.should.be.kind_of EDL::Timewarp
248
+
249
+ tw.source_used_upto.should.be < clip.src_end_tc
250
+ tw.source_used_upto.should.equal "03:03:19:24".tc
251
+ tw.speed_in_percent.to_i.should.equal 50
252
+ tw.actual_length_of_source.should.equal 5
253
+
254
+ tw.should.not.be.reverse
131
255
  end
256
+
132
257
  end
133
258
 
134
- class ReverseTimewarpTest < Test::Unit::TestCase
135
- def test_parse
136
- @edl = EDL::Parser.new.parse(File.open(REVERSE))
137
- assert_equal 1, @edl.events.length
259
+ context "A reverse timewarp EDL coming from Avid should" do
260
+
261
+ specify "be parsed properly" do
138
262
 
139
- clip = @edl.events[0]
140
- assert_equal 52, clip.length
263
+ clip = EDL::Parser.new.parse(File.open(AVID_REVERSE)).pop
264
+
265
+ clip.rec_length.should.equal 52
141
266
 
142
- assert clip.has_timewarp?, "Should respond true to has_timewarp?"
143
267
  tw = clip.timewarp
268
+ tw.actual_framerate.to_i.should.equal -25
269
+
270
+ tw.should.be.reverse
144
271
 
145
- assert_equal( -25, tw.actual_framerate.to_i)
146
- assert tw.reverse?
147
- assert_equal clip.length, tw.actual_length_of_source
148
- assert_equal clip.src_start_tc, tw.actual_src_end_tc
149
- assert_equal clip.src_start_tc - 52, tw.actual_src_start_tc
150
- assert_equal( -100, clip.timewarp.speed_in_percent.to_i)
272
+ tw.actual_length_of_source.should.equal 52
151
273
 
274
+ assert_equal 52, clip.src_length, "The src length should be computed the same as its just a reverse"
275
+ assert_equal -100.0, clip.timewarp.speed
152
276
  end
153
277
  end
154
278
 
155
- class EventMatcherTest < Test::Unit::TestCase
279
+ context "A Final Cut Pro originating reverse should" do
280
+ specify "be interpreted properly" do
281
+ e = EDL::Parser.new.parse(File.open(FCP_REVERSE)).pop
282
+
283
+ e.rec_length.should.equal 1000
284
+ e.src_length.should.equal 1000
285
+
286
+ e.rec_start_tc.should.equal "1h".tc
287
+ e.rec_end_tc.should.equal "1h 40s".tc
288
+
289
+ e.should.be.reverse
290
+ e.timewarp.should.not.be nil
291
+
292
+ tw = e.timewarp
293
+ tw.source_used_from.should.equal "1h".tc
294
+ tw.source_used_upto.should.equal "1h 40s".tc
295
+ end
296
+ end
297
+
298
+ context "EventMatcher should" do
156
299
 
157
300
  EVT_PATTERNS = [
158
301
  '020 008C V C 08:04:24:24 08:04:25:19 01:00:25:22 01:00:26:17',
@@ -164,140 +307,217 @@ class EventMatcherTest < Test::Unit::TestCase
164
307
  '025 GEN V D 025 00:00:55:10 00:00:58:11 01:00:29:19 01:00:32:20',
165
308
  ]
166
309
 
167
- def test_clip_generation_from_line
310
+ specify "produce an Event" do
168
311
  m = EDL::EventMatcher.new(25)
169
312
 
170
313
  clip = m.apply([],
171
314
  '020 008C V C 08:04:24:24 08:04:25:19 01:00:25:22 01:00:26:17'
172
315
  )
173
316
 
174
- assert_not_nil clip
175
- assert_kind_of EDL::Clip, clip
176
- assert_equal '020', clip.num
177
- assert_equal '008C', clip.reel
178
- assert_equal 'V', clip.track
179
- assert_equal '08:04:24:24', clip.src_start_tc.to_s
180
- assert_equal '08:04:25:19', clip.src_end_tc.to_s
181
- assert_equal '01:00:25:22', clip.rec_start_tc.to_s
182
- assert_equal '01:00:26:17', clip.rec_end_tc.to_s
183
- assert_equal '020 008C V C 08:04:24:24 08:04:25:19 01:00:25:22 01:00:26:17', clip.original_line
317
+ clip.should.be.kind_of EDL::Event
318
+
319
+ clip.num.should.equal "020"
320
+ clip.reel.should.equal "008C"
321
+ clip.track.should.equal "V"
322
+
323
+ clip.src_start_tc.should.equal '08:04:24:24'.tc
324
+
325
+ clip.src_end_tc.should.equal '08:04:25:19'.tc
326
+ clip.rec_start_tc.should.equal '01:00:25:22'.tc
327
+ clip.rec_end_tc.should.equal '01:00:26:17'.tc
328
+
329
+ clip.transition.should.be nil
330
+ clip.timewarp.should.be nil
331
+ clip.outgoing_transition_duration.should.be.zero
332
+
184
333
  end
185
334
 
186
- def test_dissolve_generation_from_line
335
+ specify "produce an Event with dissolve" do
187
336
  m = EDL::EventMatcher.new(25)
337
+
188
338
  dissolve = m.apply([],
189
339
  '025 GEN V D 025 00:00:55:10 00:00:58:11 01:00:29:19 01:00:32:20'
190
340
  )
191
- assert_not_nil dissolve
192
- assert_kind_of EDL::Clip, dissolve
193
- assert_equal '025', dissolve.num
194
- assert_equal 'GEN', dissolve.reel
195
- assert_equal 'V', dissolve.track
341
+ dissolve.should.be.kind_of EDL::Event
196
342
 
197
- assert dissolve.has_transition?
198
- assert_not_nil dissolve.transition
199
- assert_kind_of EDL::Dissolve, dissolve.transition
200
- assert_equal '025', dissolve.transition.duration
201
- end
202
-
203
- def test_wipe_generation_from_line
204
- m = EDL::EventMatcher.new(25)
205
- wipe = m.apply([],
206
- '025 GEN V W001 025 00:00:55:10 00:00:58:11 01:00:29:19 01:00:32:20'
207
- )
208
- assert_not_nil wipe
209
- assert_kind_of EDL::Clip, wipe
210
- assert wipe.generator?
211
- assert_equal '025', wipe.num
212
- assert_equal 'GEN', wipe.reel
213
- assert_equal 'V', wipe.track
343
+ dissolve.num.should.equal "025"
344
+ dissolve.reel.should.equal "GEN"
345
+ dissolve.track.should.equal "V"
346
+
347
+ dissolve.should.be.has_transition
214
348
 
215
- assert wipe.has_transition?
349
+ tr = dissolve.transition
216
350
 
217
- assert_not_nil wipe.transition
218
- assert_kind_of EDL::Wipe, wipe.transition
219
- assert_equal '025', wipe.transition.duration
220
- assert_equal '001', wipe.transition.smpte_wipe_index
351
+ tr.should.be.kind_of EDL::Dissolve
352
+ tr.duration.should.equal 25
221
353
  end
222
354
 
223
- def test_black_generation_from_line
355
+ specify "set flag on the previous event in the stack when a dissolve is encountered" do
224
356
  m = EDL::EventMatcher.new(25)
225
- black = m.apply([],
226
- '025 BL V C 00:00:00:00 00:00:00:00 01:00:29:19 01:00:29:19'
227
- )
228
-
229
- assert_not_nil black
357
+ previous_evt = flexmock
358
+ previous_evt.should_receive(:outgoing_transition_duration=).with(25).once
230
359
 
231
- assert black.black?, "Black should be black?"
232
- assert black.slug?, "Black should be slug?"
360
+ m.apply([previous_evt],
361
+ '025 GEN V D 025 00:00:55:10 00:00:58:11 01:00:29:19 01:00:32:20'
362
+ )
363
+ end
364
+
365
+ specify "generate a Wipe" do
366
+ m = EDL::EventMatcher.new(25)
367
+ wipe = m.apply([],
368
+ '025 GEN V W001 025 00:00:55:10 00:00:58:11 01:00:29:19 01:00:32:20'
369
+ )
233
370
 
234
- assert black.generator?, "Should be generator?"
235
- assert_equal '025', black.num
236
- assert_equal 'BL', black.reel
237
- assert_equal 'V', black.track
238
- assert_equal '025 BL V C 00:00:00:00 00:00:00:00 01:00:29:19 01:00:29:19', black.original_line
371
+ tr = wipe.transition
372
+ tr.should.be.kind_of EDL::Wipe
373
+ tr.duration.should.equal 25
374
+ tr.smpte_wipe_index.should.equal '001'
239
375
  end
240
376
 
241
- def test_matches_all_patterns
377
+ specify "match the widest range of patterns" do
242
378
  EVT_PATTERNS.each do | pat |
243
379
  assert EDL::EventMatcher.new(25).matches?(pat), "EventMatcher should match #{pat}"
244
380
  end
245
381
  end
382
+
383
+ specify "pass the framerate that it received upon instantiation to the Timecodes being created" do
384
+
385
+ m = EDL::EventMatcher.new(30)
386
+ clip = m.apply([],
387
+ '020 008C V C 08:04:24:24 08:04:25:19 01:00:25:22 01:00:26:17'
388
+ )
389
+ clip.rec_start_tc.fps.should.equal 30
390
+ clip.rec_end_tc.fps.should.equal 30
391
+ clip.src_start_tc.fps.should.equal 30
392
+ clip.src_end_tc.fps.should.equal 30
393
+ end
394
+ end
395
+
396
+ context "CommentMatcher should" do
397
+ specify "match a comment" do
398
+ line = "* COMMENT: PURE BULLSHIT"
399
+ assert EDL::CommentMatcher.new.matches?(line)
400
+ end
401
+
402
+ specify "apply the comment to the last clip on the stack" do
403
+ line = "* COMMENT: PURE BULLSHIT"
404
+
405
+ comments = []
406
+ mok_evt = flexmock
407
+
408
+ 2.times { mok_evt.should_receive(:comments).and_return(comments) }
409
+ 2.times { EDL::CommentMatcher.new.apply([mok_evt], line) }
410
+
411
+ mok_evt.comments.should.equal ["COMMENT: PURE BULLSHIT", "COMMENT: PURE BULLSHIT"]
412
+ end
246
413
  end
247
414
 
248
- class ClipNameMatcherTest < Test::Unit::TestCase
249
- def test_matches
415
+ context "ClipNameMatcher should" do
416
+ specify "match a clip name" do
250
417
  line = "* FROM CLIP NAME: TAPE_6-10.MOV"
251
- assert EDL::NameMatcher.new.matches?(line)
418
+ EDL::NameMatcher.new.matches?(line).should.equal true
419
+ end
420
+
421
+ specify "not match a simple comment" do
422
+ line = "* CRAP"
423
+ EDL::NameMatcher.new.matches?(line).should.equal false
252
424
  end
253
425
 
254
- def test_apply
426
+ specify "apply the name to the last event on the stack" do
255
427
  line = "* FROM CLIP NAME: TAPE_6-10.MOV"
428
+
256
429
  mok_evt = flexmock
430
+ comments = []
257
431
  mok_evt.should_receive(:clip_name=).with('TAPE_6-10.MOV').once
432
+ mok_evt.should_receive(:comments).and_return(comments).once
433
+
258
434
  EDL::NameMatcher.new.apply([mok_evt], line)
435
+ comments.should.equal ["FROM CLIP NAME: TAPE_6-10.MOV"]
259
436
  end
437
+
260
438
  end
261
439
 
262
- class EffectMatcherTest < Test::Unit::TestCase
263
- def test_matches
440
+ context "EffectMatcher should" do
441
+ specify "not match a simple comment" do
442
+ line = "* STUFF"
443
+ EDL::EffectMatcher.new.matches?(line).should.equal false
444
+ end
445
+
446
+ specify "match a dissolve name" do
264
447
  line = "* EFFECT NAME: CROSS DISSOLVE"
265
- assert EDL::EffectMatcher.new.matches?(line)
448
+ EDL::EffectMatcher.new.matches?(line).should.equal true
266
449
  end
267
-
268
- def test_apply
450
+
451
+ specify "apply the effect name to the transition of the last event on the stack" do
269
452
  line = "* EFFECT NAME: CROSS DISSOLVE"
270
453
  mok_evt, mok_transition = flexmock, flexmock
454
+ cmt = []
271
455
 
272
456
  mok_evt.should_receive(:transition).once.and_return(mok_transition)
457
+ mok_evt.should_receive(:comments).once.and_return(cmt)
458
+
273
459
  mok_transition.should_receive(:effect=).with("CROSS DISSOLVE").once
274
460
 
275
461
  EDL::EffectMatcher.new.apply([mok_evt], line)
462
+
463
+ cmt.should.equal ["EFFECT NAME: CROSS DISSOLVE"]
276
464
  end
465
+
277
466
  end
278
467
 
279
- class ComplexTest < Test::Unit::TestCase
280
- def test_parses_cleanly
468
+ context "A complex EDL passed via Parser should" do
469
+ specify "parse without errors" do
281
470
  assert_nothing_raised { EDL::Parser.new.parse(File.open(FORTY_FIVER)) }
282
471
  end
283
472
 
284
- def test_from_zero
473
+ # TODO: this does not belong here
474
+ specify "be properly rewritten from zero" do
285
475
  complex = EDL::Parser.new.parse(File.open(FORTY_FIVER))
286
-
287
476
  from_zero = complex.from_zero
288
- assert_equal '00:00:00:00', from_zero.events[0].rec_start_tc.to_s,
289
- "The starting timecode of the first event should have been shifted to zero"
290
- assert_equal '00:00:42:16', from_zero.events[-1].rec_end_tc.to_s,
291
- "The ending timecode of the last event should have been shifted 10 hours back"
477
+
478
+ # Should have the same number of events
479
+ from_zero.length.should.equal complex.length
480
+
481
+ from_zero[0].rec_start_tc.should.be.zero
482
+ from_zero[-1].rec_end_tc.should.equal '00:00:42:16'.tc
483
+ end
484
+ end
485
+
486
+ context "A FinalCutPro speedup with fade at the end should" do
487
+ specify "be parsed cleanly" do
488
+ list = EDL::Parser.new.parse(File.open(SPEEDUP_AND_FADEOUT))
489
+
490
+ list.length.should.equal 2
491
+
492
+ first_evt = list[0]
493
+
494
+ tw = first_evt.timewarp
495
+ tw.should.be.kind_of EDL::Timewarp
496
+
497
+ first_evt.rec_length.should.equal 689
498
+ first_evt.rec_length_with_transition.should.equal 714
499
+
500
+ tw.actual_length_of_source.should.equal 1000
501
+ tw.speed.should.equal 140
502
+
503
+ assert_equal 1000, first_evt.src_length
504
+
505
+ assert_equal "01:00:00:00", first_evt.capture_from_tc.to_s
506
+ assert_equal "01:00:40:00", first_evt.capture_to_tc.to_s
292
507
  end
293
508
  end
294
509
 
295
- # class GrabberTest < Test::Unit::TestCase
296
- # FILM = '/Users/julik/Downloads/HC_CORRECT-TCS_VIDEO1.edl.txt'
297
- # def test_cutter
298
- # complex = EDL::Parser.new.parse(File.open(FILM))
299
- # cutter = EDL::Grabber.new("/Users/julik/Desktop/Cutto/HC_CORRECT-TCS.mov")
300
- # cutter.ffmpeg_bin = '/opt/local/bin/ffmpeg'
301
- # cutter.grab(complex)
302
- # end
303
- # end
510
+ context "A FinalCutPro speedup and reverse with fade at the end should" do
511
+ specify "parse cleanly" do
512
+ first_evt = EDL::Parser.new.parse(File.open(SPEEDUP_REVERSE_AND_FADEOUT)).shift
513
+
514
+ first_evt.should.be.reverse
515
+
516
+ first_evt.rec_length.should.equal 689
517
+ first_evt.rec_length_with_transition.should.equal 714
518
+
519
+ tw = first_evt.timewarp
520
+ tw.source_used_from.should.equal "1h 1f".tc
521
+ tw.source_used_upto.should.equal "1h 40s".tc
522
+ end
523
+ end