edl 0.1.6 → 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/{README.rdoc → LICENSE.txt} +0 -41
- data/README.md +48 -0
- data/Rakefile +5 -9
- data/edl.gemspec +16 -20
- data/lib/edl/version.rb +1 -1
- data/spec/fixtures/files/45S_SAMPLE.EDL +48 -0
- data/spec/fixtures/files/FCP_REVERSE.EDL +9 -0
- data/spec/fixtures/files/KEY_TRANSITION.EDL +9 -0
- data/spec/fixtures/files/PLATES.EDL +1 -0
- data/spec/fixtures/files/REEL_IS_CLIP.txt +8 -0
- data/spec/fixtures/files/REVERSE.EDL +3 -0
- data/spec/fixtures/files/SIMPLE_DISSOLVE.EDL +9 -0
- data/spec/fixtures/files/SPEEDUP_AND_FADEOUT.EDL +11 -0
- data/spec/fixtures/files/SPEEDUP_REVERSE_AND_FADEOUT.EDL +11 -0
- data/spec/fixtures/files/SPLICEME.EDL +5 -0
- data/spec/fixtures/files/TIMEWARP.EDL +5 -0
- data/spec/fixtures/files/TIMEWARP_HALF.EDL +5 -0
- data/spec/fixtures/files/TRAILER_EDL.edl +0 -0
- data/spec/fixtures/files/edl_mixed_line_endings.edl +12 -0
- data/spec/lib/edl/parser_spec.rb +12 -0
- data/spec/lib/edl_spec.rb +739 -0
- data/spec/spec_helper.rb +30 -0
- metadata +26 -7
- data/.ruby-version +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aa1756eb9a1a1e9d9c8703e5033741622df904c9
|
4
|
+
data.tar.gz: 513d2a6560317fa3fb5988d202219543bf8e0ebe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 85afdd18d25b711ab61971ac813a79c202a6aa84363fdc47c50de4ca8525e322ca675a22e0002004482420075626ee8ea6dda7088c2d49070687bc6f8efc64d9
|
7
|
+
data.tar.gz: e6f3893517e8fc68311816f3b40119ab10d347662f6bb4f6708f78f353a94c74f6b79ffb8683d20b78e671ec11f1dda93a96ae5ecb942ac3532d0ff0bb429fde
|
data/{README.rdoc → LICENSE.txt}
RENAMED
@@ -1,44 +1,3 @@
|
|
1
|
-
Work with EDL files from Ruby
|
2
|
-
http://en.wikipedia.org/wiki/Edit_decision_list
|
3
|
-
|
4
|
-
The library assists in parsing [EDL files](http://en.wikipedia.org/wiki/Edit_decision_list) in CMX 3600 format.
|
5
|
-
You can use it to generate capture lists, inspect needed video segments for the assembled program
|
6
|
-
and display edit timelines. Together with the depix you could write your own "blind"
|
7
|
-
conform utility in about 10 minutes, no joke.
|
8
|
-
|
9
|
-
== SYNOPSIS:
|
10
|
-
|
11
|
-
require 'rubygems'
|
12
|
-
require 'edl'
|
13
|
-
|
14
|
-
list = EDL::Parser.new(fps=25).parse(File.open('OFFLINE.EDL'))
|
15
|
-
list.events.each do | evt |
|
16
|
-
evt.clip_name #=> Boat_Trip_Take1
|
17
|
-
evt.capture_from_tc #=> 01:20:22:10
|
18
|
-
evt.capture_to_tc #=> 01:20:26:15, accounts for outgoing transition AND M2 timewarps
|
19
|
-
end
|
20
|
-
|
21
|
-
== REQUIREMENTS:
|
22
|
-
|
23
|
-
* Timecode gem (sudo gem install timecode)
|
24
|
-
|
25
|
-
== PROBLEMS:
|
26
|
-
|
27
|
-
There is currently no support for:
|
28
|
-
|
29
|
-
* drop-frame TC
|
30
|
-
* audio
|
31
|
-
* split edits
|
32
|
-
* key effects
|
33
|
-
|
34
|
-
Some reverse/timewarp combinations can produce source dificiencies of 1 frame
|
35
|
-
|
36
|
-
== INSTALL:
|
37
|
-
|
38
|
-
* sudo gem install edl
|
39
|
-
|
40
|
-
== LICENSE:
|
41
|
-
|
42
1
|
(The MIT License)
|
43
2
|
|
44
3
|
Copyright (c) 2008 Julik Tarkhanov <me@julik.nl>
|
data/README.md
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
Work with EDL files from Ruby
|
2
|
+
http://en.wikipedia.org/wiki/Edit_decision_list
|
3
|
+
|
4
|
+
The library assists in parsing [EDL files](http://en.wikipedia.org/wiki/Edit_decision_list) in CMX 3600 format.
|
5
|
+
You can use it to generate capture lists, inspect needed video segments for the assembled program
|
6
|
+
and display edit timelines. When used together with [depix](https://github.com/guerilla-di/depix) you could write your own "blind"
|
7
|
+
conform utility in about 10 minutes, no joke.
|
8
|
+
|
9
|
+
## Basic usage
|
10
|
+
|
11
|
+
```
|
12
|
+
require 'rubygems'
|
13
|
+
require 'edl'
|
14
|
+
|
15
|
+
list = EDL::Parser.new(fps=25).parse(File.open('OFFLINE.EDL'))
|
16
|
+
list.events.each do | evt |
|
17
|
+
evt.clip_name #=> Boat_Trip_Take1
|
18
|
+
evt.capture_from_tc #=> 01:20:22:10
|
19
|
+
evt.capture_to_tc #=> 01:20:26:15, accounts for outgoing transition AND M2 timewarps
|
20
|
+
end
|
21
|
+
```
|
22
|
+
|
23
|
+
## Requirements
|
24
|
+
|
25
|
+
* Timecode gem (sudo gem install timecode)
|
26
|
+
|
27
|
+
## Currently unsupportedl EDL features:
|
28
|
+
|
29
|
+
There is currently no support for:
|
30
|
+
|
31
|
+
* drop-frame TC
|
32
|
+
* audio
|
33
|
+
* split edits
|
34
|
+
* key effects
|
35
|
+
|
36
|
+
Some reverse/timewarp combinations can produce source dificiencies of 1 frame
|
37
|
+
|
38
|
+
## Installation
|
39
|
+
|
40
|
+
Add the following to your project `Gemfile`:
|
41
|
+
|
42
|
+
```
|
43
|
+
gem 'edl'
|
44
|
+
```
|
45
|
+
|
46
|
+
## License
|
47
|
+
|
48
|
+
See LICENSE.txt
|
data/Rakefile
CHANGED
@@ -1,15 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'rubygems'
|
4
|
-
require '
|
4
|
+
require './lib/edl.rb'
|
5
5
|
require 'rake/testtask'
|
6
|
+
require "bundler/gem_tasks"
|
7
|
+
require 'rspec/core/rake_task'
|
6
8
|
|
7
|
-
|
8
|
-
require 'rspec/core/rake_task'
|
9
|
+
RSpec::Core::RakeTask.new(:spec)
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
task default: :spec
|
13
|
-
rescue LoadError
|
14
|
-
# no rspec available
|
15
|
-
end
|
11
|
+
task default: :spec
|
data/edl.gemspec
CHANGED
@@ -6,18 +6,26 @@ $LOAD_PATH.push File.expand_path('../lib', __FILE__)
|
|
6
6
|
require 'edl/version'
|
7
7
|
|
8
8
|
Gem::Specification.new do |s|
|
9
|
+
s.required_rubygems_version = ">= 1.2.0"
|
10
|
+
|
9
11
|
s.name = 'edl'
|
10
12
|
s.version = EDL::VERSION
|
11
|
-
|
12
|
-
s.required_rubygems_version = Gem::Requirement.new('>= 0') if s.respond_to? :required_rubygems_version=
|
13
|
-
s.authors = ['Julik Tarkhanov']
|
13
|
+
s.authors = ['Julik Tarkhanov', 'Philipp Großelfinger']
|
14
14
|
s.date = '2014-03-24'
|
15
15
|
s.email = 'me@julik.nl'
|
16
16
|
s.extra_rdoc_files = [
|
17
|
-
'README.
|
17
|
+
'README.md'
|
18
18
|
]
|
19
19
|
|
20
|
-
|
20
|
+
# Prevent pushing this gem to RubyGemspec.org. To allow pushes either set the 'allowed_push_host'
|
21
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
22
|
+
if s.respond_to?(:metadata)
|
23
|
+
s.metadata['allowed_push_host'] = "https://rubygems.org"
|
24
|
+
else
|
25
|
+
raise "RubyGems 2.0 or newer is required to protect against public gem pushespec."
|
26
|
+
end
|
27
|
+
|
28
|
+
s.files = `git ls-files -z`.split("\x0")
|
21
29
|
s.bindir = 'exe'
|
22
30
|
s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
31
|
s.require_paths = ['lib']
|
@@ -27,19 +35,7 @@ Gem::Specification.new do |s|
|
|
27
35
|
s.rubygems_version = '2.0.3'
|
28
36
|
s.summary = 'Parser for EDL (Edit Decision List) files'
|
29
37
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0')
|
34
|
-
s.add_runtime_dependency 'timecode', '>= 0'
|
35
|
-
s.add_development_dependency 'rake', '>= 0'
|
36
|
-
s.add_development_dependency 'rspec', '~> 3.5'
|
37
|
-
else
|
38
|
-
s.add_dependency 'timecode', '>= 0'
|
39
|
-
s.add_dependency 'rake', '>= 0'
|
40
|
-
end
|
41
|
-
else
|
42
|
-
s.add_dependency 'timecode', '>= 0'
|
43
|
-
s.add_dependency 'rake', '>= 0'
|
44
|
-
end
|
38
|
+
s.add_runtime_dependency 'timecode', '>= 0'
|
39
|
+
s.add_development_dependency 'rake', '>= 0'
|
40
|
+
s.add_development_dependency 'rspec', '~> 3.5'
|
45
41
|
end
|
data/lib/edl/version.rb
CHANGED
@@ -0,0 +1,48 @@
|
|
1
|
+
TITLE: EDIT 15 [14-11-08] 45 SEC EDL
|
2
|
+
001 106771 V C 04:00:57:05 04:00:58:10 10:00:00:00 10:00:01:05
|
3
|
+
002 106771 V C 04:03:44:06 04:03:45:08 10:00:01:05 10:00:02:07
|
4
|
+
003 106770 V C 00:06:55:13 00:06:57:01 10:00:02:07 10:00:03:20
|
5
|
+
004 106771 V C 03:16:35:05 03:16:36:05 10:00:03:20 10:00:04:20
|
6
|
+
005 106770 V C 00:13:03:17 00:13:04:20 10:00:04:20 10:00:05:23
|
7
|
+
006 106771 V C 03:11:43:02 03:11:44:04 10:00:05:23 10:00:07:00
|
8
|
+
007 106857 V C 07:18:40:24 07:18:41:17 10:00:07:00 10:00:07:18
|
9
|
+
008 106857 V C 07:22:25:19 07:22:27:02 10:00:07:18 10:00:09:01
|
10
|
+
009 106863 V C 13:12:46:23 13:12:48:11 10:00:09:01 10:00:10:14
|
11
|
+
010 106857 V C 08:00:32:00 08:00:33:10 10:00:10:14 10:00:11:24
|
12
|
+
011 106857 V C 08:05:36:18 08:05:37:19 10:00:11:24 10:00:13:00
|
13
|
+
012 106857 V C 07:04:47:06 07:04:50:13 10:00:13:00 10:00:16:07
|
14
|
+
M2 106857 012.5 07:04:47:06
|
15
|
+
013 106863 V C 10:11:06:07 10:11:08:00 10:00:16:07 10:00:18:00
|
16
|
+
014 106863 V C 10:09:16:21 10:09:18:11 10:00:18:00 10:00:19:15
|
17
|
+
015 106857 V C 07:06:02:17 07:06:04:07 10:00:19:15 10:00:21:05
|
18
|
+
016 106857 V C 09:07:18:07 09:07:20:16 10:00:21:05 10:00:23:14
|
19
|
+
M2 106857 002.5 09:07:18:07
|
20
|
+
017 106863 V C 11:02:46:06 11:02:47:06 10:00:23:14 10:00:24:14
|
21
|
+
018 106863 V C 11:10:17:04 11:10:17:20 10:00:24:14 10:00:25:05
|
22
|
+
019 106863 V C 12:06:09:18 12:06:10:16 10:00:25:05 10:00:26:03
|
23
|
+
020 106863 V C 12:10:49:17 12:10:51:02 10:00:26:03 10:00:27:13
|
24
|
+
021 106815 V C 05:01:22:21 05:01:24:05 10:00:27:13 10:00:28:22
|
25
|
+
022 106815 V C 06:01:35:03 06:01:36:18 10:00:28:22 10:00:30:12
|
26
|
+
023 106815 V C 05:13:38:19 05:13:40:10 10:00:30:12 10:00:32:03
|
27
|
+
M2 106815 -025.0 05:13:38:19
|
28
|
+
024 106857 V C 09:13:03:09 09:13:05:11 10:00:32:03 10:00:34:05
|
29
|
+
M2 106857 -025.0 09:13:03:09
|
30
|
+
025 106857 V C 09:13:01:07 09:13:01:07 10:00:34:05 10:00:34:05
|
31
|
+
025 106771 V D 022 03:11:55:00 03:11:56:17 10:00:34:05 10:00:35:22
|
32
|
+
M2 106857 -025.0 09:13:01:07
|
33
|
+
* BLEND, DISSOLVE
|
34
|
+
026 106771 V C 03:11:56:17 03:11:56:17 10:00:35:22 10:00:35:22
|
35
|
+
026 106771 V D 012 03:06:30:09 03:06:31:19 10:00:35:22 10:00:37:07
|
36
|
+
* BLEND, DISSOLVE
|
37
|
+
027 106863 V C 13:15:29:20 13:15:31:02 10:00:37:07 10:00:38:14
|
38
|
+
028 106771 V C 03:15:40:22 03:15:42:11 10:00:38:14 10:00:40:03
|
39
|
+
029 106863 V C 13:15:51:02 13:15:52:08 10:00:40:03 10:00:41:09
|
40
|
+
030 106863 V C 13:15:52:08 13:15:52:08 10:00:41:09 10:00:41:09
|
41
|
+
030 BL V D 032 00:00:00:00 00:00:01:07 10:00:41:09 10:00:42:16
|
42
|
+
* BLEND, DISSOLVE
|
43
|
+
>>> SOURCE 106770 106770 4919975f.793b8d33
|
44
|
+
>>> SOURCE 106771 106771 4919a179.79630563
|
45
|
+
>>> SOURCE 106815 106815 4919b521.79afcb70
|
46
|
+
>>> SOURCE 106857 106857 4919cbb9.7a080d9d
|
47
|
+
>>> SOURCE 106863 106863 4919e0c3.7a5a3cfc
|
48
|
+
|
@@ -0,0 +1,9 @@
|
|
1
|
+
TITLE: FOR_TEST
|
2
|
+
FCM: DROP FRAME
|
3
|
+
|
4
|
+
002 AX V K B 00:01:06:06 00:01:08:03 01:00:04:10 01:00:06:07
|
5
|
+
002 AX V K 000 00:00:03:08 00:00:03:08 01:00:04:10 01:00:04:10
|
6
|
+
* FROM CLIP NAME: foobarz_0118_023-APPLE PRORES WITH ALPHA.MOV
|
7
|
+
* COMMENT:
|
8
|
+
* KEY CLIP NAME: foobarz_0118_026-APPLE PRORES WITH ALPHA.MOV
|
9
|
+
* CLIP FILTER: COLOR CORRECTOR 3-WAY
|
@@ -0,0 +1 @@
|
|
1
|
+
TITLE: PLATES.
|
@@ -0,0 +1,8 @@
|
|
1
|
+
0001 KASS1 A1234V C 00:00:00:00 00:00:12:24 10:00:00:00 10:00:12:24
|
2
|
+
* REEL RC100009 IS CLIP 28661.mov
|
3
|
+
0002 KASS1 A1234V C 00:00:00:00 00:00:13:03 10:00:12:24 10:00:26:02
|
4
|
+
* REEL RC100003 IS CLIP 28655.mov
|
5
|
+
0003 KASS1 A1234V C 00:00:00:00 00:00:15:06 10:00:26:02 10:00:41:08
|
6
|
+
* REEL RC100005 IS CLIP 28657.mov
|
7
|
+
0004 KASS1 A1234V C 00:00:00:00 00:00:16:06 10:00:41:08 10:00:57:14
|
8
|
+
* REEL RC100010 IS CLIP 28662.mov
|
@@ -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
|
+
|
Binary file
|
@@ -0,0 +1,12 @@
|
|
1
|
+
describe EDL::Parser do
|
2
|
+
describe '#parse' do
|
3
|
+
it 'reports error when frame exceeds fps' do
|
4
|
+
Timecode.add_custom_framerate!(12.0)
|
5
|
+
parser = EDL::Parser.new(12)
|
6
|
+
parser.parse(File.read('spec/fixtures/files/45S_SAMPLE.EDL'))
|
7
|
+
expect(parser.errors.first).to match(
|
8
|
+
/There can be no more than 12\.0 frames \@12\.0, got 13/
|
9
|
+
)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,739 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
TRAILER_EDL = 'spec/fixtures/files/TRAILER_EDL.edl'
|
4
|
+
SIMPLE_DISSOLVE = 'spec/fixtures/files/SIMPLE_DISSOLVE.EDL'
|
5
|
+
SPLICEME = 'spec/fixtures/files/SPLICEME.EDL'
|
6
|
+
SIMPLE_TIMEWARP = 'spec/fixtures/files/TIMEWARP.EDL'
|
7
|
+
SLOMO_TIMEWARP = 'spec/fixtures/files/TIMEWARP_HALF.EDL'
|
8
|
+
FORTY_FIVER = 'spec/fixtures/files/45S_SAMPLE.EDL'
|
9
|
+
AVID_REVERSE = 'spec/fixtures/files/REVERSE.EDL'
|
10
|
+
SPEEDUP_AND_FADEOUT = 'spec/fixtures/files/SPEEDUP_AND_FADEOUT.EDL'
|
11
|
+
SPEEDUP_REVERSE_AND_FADEOUT = 'spec/fixtures/files/SPEEDUP_REVERSE_AND_FADEOUT.EDL'
|
12
|
+
FCP_REVERSE = 'spec/fixtures/files/FCP_REVERSE.EDL'
|
13
|
+
PLATES = 'spec/fixtures/files/PLATES.EDL'
|
14
|
+
KEY = 'spec/fixtures/files/KEY_TRANSITION.EDL'
|
15
|
+
CLIP_NAMES = 'spec/fixtures/files/REEL_IS_CLIP.txt'
|
16
|
+
MIXED_LINEBREAKS = 'spec/fixtures/files/edl_mixed_line_endings.edl'
|
17
|
+
|
18
|
+
class String
|
19
|
+
def tc(fps = Timecode::DEFAULT_FPS)
|
20
|
+
Timecode.parse(self, fps)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe EDL do
|
25
|
+
describe 'An Event' do
|
26
|
+
it 'support hash initialization' do
|
27
|
+
evt = EDL::Event.new(src_start_tc: '01:00:00:00'.tc)
|
28
|
+
expect('01:00:00:00'.tc).to eq evt.src_start_tc
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'support block initialization' do
|
32
|
+
evt = EDL::Event.new do |e|
|
33
|
+
e.src_start_tc = '01:00:00:04'.tc
|
34
|
+
end
|
35
|
+
expect('01:00:00:04'.tc).to eq evt.src_start_tc
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'respond to ends_with_transition? with false if outgoing_transition_duration is zero' do
|
39
|
+
evt = EDL::Event.new
|
40
|
+
evt.outgoing_transition_duration = 0
|
41
|
+
expect(evt.ends_with_transition?).to be_falsey
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'respond to ends_with_transition? with true if outgoing_transition_duration set above zero' do
|
45
|
+
evt = EDL::Event.new
|
46
|
+
evt.outgoing_transition_duration = 24
|
47
|
+
expect(evt.ends_with_transition?).to be_truthy
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'respond to has_timewarp? with false if no timewarp assigned' do
|
51
|
+
evt = EDL::Event.new(timewarp: nil)
|
52
|
+
expect(evt.has_timewarp?).to be_falsey
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'respond to has_timewarp? with true if a timewarp is assigned' do
|
56
|
+
evt = EDL::Event.new(timewarp: true)
|
57
|
+
expect(evt.has_timewarp?).to be_truthy
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'report rec_length as a difference of record timecodes' do
|
61
|
+
evt = EDL::Event.new(rec_start_tc: '1h'.tc, rec_end_tc: '1h 10s 2f'.tc)
|
62
|
+
expect('10s 2f'.tc.to_i).to eq evt.rec_length
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'report rec_length_with_transition as a difference of record timecodes if no transition set' do
|
66
|
+
evt = EDL::Event.new(rec_start_tc: '1h'.tc, rec_end_tc: '1h 10s 2f'.tc, outgoing_transition_duration: 0)
|
67
|
+
expect('10s 2f'.tc.to_i).to eq evt.rec_length_with_transition
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'add transition length to rec_length_with_transition if a transition is set' do
|
71
|
+
evt = EDL::Event.new(rec_start_tc: '1h'.tc, rec_end_tc: '1h 10s 2f'.tc, outgoing_transition_duration: 10)
|
72
|
+
expect('10s 2f'.tc.to_i + 10).to eq evt.rec_length_with_transition
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'return a default array for comments' do
|
76
|
+
expect(EDL::Event.new.comments).to be_a(Enumerable)
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'respond false to has_transition? if incoming transition is set' do
|
80
|
+
expect(EDL::Event.new(transition: nil).has_transition?).to be_falsey
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'respond true to has_transition? if incoming transition is set' do
|
84
|
+
expect(EDL::Event.new(transition: true).has_transition?).to be_truthy
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'respond true to black? if reel is BL' do
|
88
|
+
expect(EDL::Event.new(reel: 'BL')).to be_black
|
89
|
+
expect(EDL::Event.new(reel: '001')).to_not be_black
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'respond true to generator? if reel is BL or AX' do
|
93
|
+
expect(EDL::Event.new(reel: 'BL')).to be_generator
|
94
|
+
expect(EDL::Event.new(reel: 'AX')).to be_generator
|
95
|
+
expect(EDL::Event.new(reel: '001')).to_not be_generator
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'report src_length as rec_length_with_transition' do
|
99
|
+
e = EDL::Event.new(rec_start_tc: '2h'.tc, rec_end_tc: '2h 2s'.tc)
|
100
|
+
expect('2s'.tc.to_i).to eq e.src_length
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'support line_number' do
|
104
|
+
expect(EDL::Event.new.line_number).to be_nil
|
105
|
+
expect(3).to eq EDL::Event.new(line_number: 3).line_number
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'support capture_length as an alias to src_length' do
|
109
|
+
tw = double
|
110
|
+
expect(tw).to receive(:actual_length_of_source).twice.and_return(:something)
|
111
|
+
e = EDL::Event.new(timewarp: tw)
|
112
|
+
expect(e.capture_length).to eq e.src_length
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'delegate src_length to the timewarp if it is there' do
|
116
|
+
tw = double
|
117
|
+
expect(tw).to receive(:actual_length_of_source).and_return(:something)
|
118
|
+
e = EDL::Event.new(timewarp: tw)
|
119
|
+
expect(:something).to eq e.src_length
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'report reverse? and reversed? based on the timewarp' do
|
123
|
+
e = EDL::Event.new(timewarp: nil)
|
124
|
+
expect(e).to_not be_reverse
|
125
|
+
expect(e).to_not be_reversed
|
126
|
+
|
127
|
+
tw = double
|
128
|
+
expect(tw).to receive(:reverse?).twice.and_return(true)
|
129
|
+
|
130
|
+
e = EDL::Event.new(timewarp: tw)
|
131
|
+
expect(e).to be_reverse
|
132
|
+
expect(e).to be_reversed
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'report speed as 100 percent without a timewarp' do
|
136
|
+
e = EDL::Event.new
|
137
|
+
expect(100.0).to eq e.speed
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'consult the timewarp for speed' do
|
141
|
+
tw = double
|
142
|
+
expect(tw).to receive(:speed).and_return(:something)
|
143
|
+
|
144
|
+
e = EDL::Event.new(timewarp: tw)
|
145
|
+
expect(:something).to eq e.speed
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'report false for starts_with_transition? if transision is nil' do
|
149
|
+
expect(EDL::Event.new.starts_with_transition?).to be_falsey
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'report zero for incoming_transition_duration if transision is nil' do
|
153
|
+
expect(EDL::Event.new.incoming_transition_duration).to be_zero
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'report true for starts_with_transition? if transision is not nil' do
|
157
|
+
e = EDL::Event.new transition: true
|
158
|
+
expect(e.starts_with_transition?).to be_truthy
|
159
|
+
end
|
160
|
+
|
161
|
+
it "consult the transition for incoming_transition_duration if it's present" do
|
162
|
+
tr = double
|
163
|
+
expect(tr).to receive(:duration).and_return(:something)
|
164
|
+
|
165
|
+
e = EDL::Event.new(transition: tr)
|
166
|
+
expect(:something).to eq e.incoming_transition_duration
|
167
|
+
end
|
168
|
+
|
169
|
+
it 'report capture_from_tc as the source start without a timewarp' do
|
170
|
+
e = EDL::Event.new(src_start_tc: '1h'.tc)
|
171
|
+
expect('1h'.tc).to eq e.capture_from_tc
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'consult the timewarp for capture_from_tc if a timewarp is there' do
|
175
|
+
tw = double
|
176
|
+
expect(tw).to receive(:source_used_from).and_return(:something)
|
177
|
+
|
178
|
+
e = EDL::Event.new(timewarp: tw)
|
179
|
+
expect(:something).to eq e.capture_from_tc
|
180
|
+
end
|
181
|
+
|
182
|
+
it 'report capture_to_tc as record length plus transition when no timewarp present' do
|
183
|
+
e = EDL::Event.new(src_end_tc: '1h 10s'.tc, outgoing_transition_duration: 2)
|
184
|
+
expect('1h 10s 2f'.tc).to eq e.capture_to_tc
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'report capture_to_and_including_tc as record length plus transition when no timewarp present' do
|
188
|
+
e = EDL::Event.new(src_end_tc: '1h 10s'.tc, outgoing_transition_duration: 2)
|
189
|
+
expect('1h 10s 1f'.tc).to eq e.capture_to_and_including_tc
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'consult the timewarp for capture_to_tc if timewarp is present' do
|
193
|
+
tw = double
|
194
|
+
expect(tw).to receive(:source_used_upto).and_return(:something)
|
195
|
+
|
196
|
+
e = EDL::Event.new(timewarp: tw)
|
197
|
+
expect(:something).to eq e.capture_to_tc
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
describe 'A Parser' do
|
202
|
+
it 'store the passed framerate' do
|
203
|
+
p = EDL::Parser.new(45)
|
204
|
+
expect(45).to eq p.fps
|
205
|
+
end
|
206
|
+
|
207
|
+
it 'return matchers tuned with the passed framerate' do
|
208
|
+
p = EDL::Parser.new(30)
|
209
|
+
matchers = p.get_matchers
|
210
|
+
event_matcher = matchers.find { |e| e.is_a?(EDL::EventMatcher) }
|
211
|
+
expect(30).to eq event_matcher.fps
|
212
|
+
end
|
213
|
+
|
214
|
+
it 'create a Timecode from stringified elements' do
|
215
|
+
elems = %w[08 04 24 24]
|
216
|
+
expect do
|
217
|
+
@tc = EDL::Parser.timecode_from_line_elements(elems, 30)
|
218
|
+
end.to_not raise_error
|
219
|
+
|
220
|
+
expect(@tc).to be_a(Timecode)
|
221
|
+
expect('08:04:24:24'.tc(30)).to eq @tc
|
222
|
+
|
223
|
+
expect(elems).to be_empty
|
224
|
+
end
|
225
|
+
|
226
|
+
it 'parse from a String' do
|
227
|
+
p = EDL::Parser.new
|
228
|
+
expect do
|
229
|
+
@edl = p.parse File.read(SIMPLE_DISSOLVE)
|
230
|
+
end.to_not raise_error
|
231
|
+
|
232
|
+
expect(@edl).to be_a(EDL::List)
|
233
|
+
expect(@edl.length).to eq 2
|
234
|
+
end
|
235
|
+
|
236
|
+
it 'parse from a File/IOish' do
|
237
|
+
p = EDL::Parser.new
|
238
|
+
expect do
|
239
|
+
@edl = p.parse File.open(SIMPLE_DISSOLVE)
|
240
|
+
end.to_not raise_error
|
241
|
+
|
242
|
+
expect(@edl).to be_a(EDL::List)
|
243
|
+
expect(@edl.length).to eq 2
|
244
|
+
end
|
245
|
+
|
246
|
+
it 'properly parse a dissolve' do
|
247
|
+
# TODO: reformulate
|
248
|
+
p = EDL::Parser.new
|
249
|
+
@edl = p.parse File.open(SIMPLE_DISSOLVE)
|
250
|
+
|
251
|
+
first, second = @edl
|
252
|
+
|
253
|
+
expect(first).to be_a(EDL::Event)
|
254
|
+
expect(second).to be_a(EDL::Event)
|
255
|
+
|
256
|
+
expect(second.has_transition?).to be_truthy
|
257
|
+
expect(first.ends_with_transition?).to be_truthy
|
258
|
+
expect(second.ends_with_transition?).to be_falsey
|
259
|
+
|
260
|
+
no_trans = @edl.without_transitions
|
261
|
+
|
262
|
+
expect(2).to eq no_trans.length
|
263
|
+
target_tc = (Timecode.parse('01:00:00:00') + 43)
|
264
|
+
expect(target_tc).to eq(no_trans[0].rec_end_tc),
|
265
|
+
'The iitshould have been extended by the length of the dissolve'
|
266
|
+
|
267
|
+
target_tc = Timecode.parse('01:00:00:00')
|
268
|
+
expect(target_tc).to eq no_trans[1].rec_start_tc
|
269
|
+
'The oitshould have been left in place'
|
270
|
+
end
|
271
|
+
|
272
|
+
it 'return a spliced EDL if the sources allow' do
|
273
|
+
@spliced = EDL::Parser.new.parse(File.open(SPLICEME)).spliced
|
274
|
+
|
275
|
+
expect(1).to eq @spliced.length
|
276
|
+
evt = @spliced[0]
|
277
|
+
|
278
|
+
expect('06:42:50:18'.tc).to eq evt.src_start_tc
|
279
|
+
expect('06:42:52:16'.tc).to eq evt.src_end_tc
|
280
|
+
end
|
281
|
+
|
282
|
+
it 'not apply any Matchers if a match is found' do
|
283
|
+
p = EDL::Parser.new
|
284
|
+
m1 = double
|
285
|
+
expect(m1).to receive(:matches?).with('plop').and_return(true)
|
286
|
+
expect(m1).to receive(:apply)
|
287
|
+
|
288
|
+
expect(p).to receive(:get_matchers).and_return([m1, m1])
|
289
|
+
result = p.parse('plop')
|
290
|
+
expect(result).to be_empty
|
291
|
+
end
|
292
|
+
|
293
|
+
it 'register line numbers of the detected events' do
|
294
|
+
p = EDL::Parser.new
|
295
|
+
events = p.parse(File.open(SPLICEME))
|
296
|
+
|
297
|
+
expect(4).to eq events[0].line_number
|
298
|
+
expect(5).to eq events[1].line_number
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
describe 'A TimewarpMatcher' do
|
303
|
+
it 'not create any extra events when used within a Parser' do
|
304
|
+
@edl = EDL::Parser.new.parse(File.open(SIMPLE_TIMEWARP))
|
305
|
+
expect(1).to eq @edl.length
|
306
|
+
end
|
307
|
+
|
308
|
+
it 'properly describe a speedup' do
|
309
|
+
clip = EDL::Parser.new.parse(File.open(SIMPLE_TIMEWARP)).pop
|
310
|
+
|
311
|
+
tw = clip.timewarp
|
312
|
+
|
313
|
+
expect(tw).to be_a(EDL::Timewarp)
|
314
|
+
expect(tw.source_used_upto).to be > clip.src_end_tc
|
315
|
+
|
316
|
+
expect(clip.src_start_tc).to eq tw.source_used_from
|
317
|
+
expect(124).to eq clip.timewarp.actual_length_of_source
|
318
|
+
expect(tw).to_not be_reverse
|
319
|
+
end
|
320
|
+
|
321
|
+
it 'properly describe a slomo' do
|
322
|
+
clip = EDL::Parser.new.parse(File.open(SLOMO_TIMEWARP)).pop
|
323
|
+
|
324
|
+
expect(10).to eq clip.rec_length
|
325
|
+
expect(5).to eq clip.src_length
|
326
|
+
|
327
|
+
tw = clip.timewarp
|
328
|
+
|
329
|
+
expect(tw.source_used_upto).to be < clip.src_end_tc
|
330
|
+
|
331
|
+
expect('03:03:19:24'.tc).to eq tw.source_used_upto
|
332
|
+
|
333
|
+
expect(50).to eq tw.speed_in_percent.to_i
|
334
|
+
expect(5).to eq tw.actual_length_of_source
|
335
|
+
expect(tw).to_not be_reverse
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
describe 'A reverse timewarp EDL coming from Avid' do
|
340
|
+
it 'be parsed properly' do
|
341
|
+
clip = EDL::Parser.new.parse(File.open(AVID_REVERSE)).pop
|
342
|
+
|
343
|
+
expect(52).to eq clip.rec_length
|
344
|
+
|
345
|
+
tw = clip.timewarp
|
346
|
+
|
347
|
+
expect(-25).to eq tw.actual_framerate.to_i
|
348
|
+
expect(tw).to be_reverse
|
349
|
+
expect(52).to eq tw.actual_length_of_source
|
350
|
+
|
351
|
+
expect(-100.0).to eq(clip.timewarp.speed), 'should be computed the same as its just a reverse'
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
describe 'EDL with clip reels in comments' do
|
356
|
+
it 'parse clip names into the reel field' do
|
357
|
+
clips = EDL::Parser.new.parse(File.open(CLIP_NAMES))
|
358
|
+
# flunk "This still has to be finalized"
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
describe 'A Final Cut Pro originating reverse' do
|
363
|
+
it 'be interpreted properly' do
|
364
|
+
e = EDL::Parser.new.parse(File.open(FCP_REVERSE)).pop
|
365
|
+
|
366
|
+
expect(1000).to eq e.rec_length
|
367
|
+
expect(1000).to eq e.src_length
|
368
|
+
|
369
|
+
expect('1h'.tc).to eq e.rec_start_tc
|
370
|
+
expect('1h 40s'.tc).to eq e.rec_end_tc
|
371
|
+
|
372
|
+
expect(e).to be_reverse
|
373
|
+
expect(e.timewarp).to_not be_nil
|
374
|
+
|
375
|
+
tw = e.timewarp
|
376
|
+
|
377
|
+
expect(-100).to eq tw.speed
|
378
|
+
expect(e.speed).to eq tw.speed
|
379
|
+
|
380
|
+
expect('1h'.tc).to eq tw.source_used_from
|
381
|
+
expect('1h 40s'.tc).to eq tw.source_used_upto
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
# describe "An edit with keyer transition" do
|
386
|
+
# itould "parse correctly" do
|
387
|
+
# events = EDL::Parser.new.parse(File.open(KEY))
|
388
|
+
# expect(2).to eq events.length
|
389
|
+
# flunk "Key transition processing is not reliable yet - no reference"
|
390
|
+
# end
|
391
|
+
# end
|
392
|
+
|
393
|
+
describe 'EventMatcher' do
|
394
|
+
EVT_PATTERNS = [
|
395
|
+
'020 008C V C 08:04:24:24 08:04:25:19 01:00:25:22 01:00:26:17',
|
396
|
+
'021 009 V C 00:39:04:21 00:39:05:09 01:00:26:17 01:00:27:05',
|
397
|
+
'022 008C V C 08:08:01:23 08:08:02:18 01:00:27:05 01:00:28:00',
|
398
|
+
'023 008C V C 08:07:30:02 08:07:30:21 01:00:28:00 01:00:28:19',
|
399
|
+
'024 AX V C 00:00:00:00 00:00:01:00 01:00:28:19 01:00:29:19',
|
400
|
+
'025 BL V C 00:00:00:00 00:00:00:00 01:00:29:19 01:00:29:19',
|
401
|
+
'025 GEN V D 025 00:00:55:10 00:00:58:11 01:00:29:19 01:00:32:20',
|
402
|
+
'002 REDACTED V C 03:09:00:13 03:09:55:19 01:00:43:12 01:01:38:18',
|
403
|
+
# '0004 KASS1 A1234V C 00:00:00:00 00:00:16:06 10:00:41:08 10:00:57:14'
|
404
|
+
].freeze
|
405
|
+
|
406
|
+
# ituld 'handle the event with multiple audio tracks' do
|
407
|
+
# m = EDL::EventMatcher.new(25)
|
408
|
+
#
|
409
|
+
# clip = m.apply([],
|
410
|
+
# '0004 KASS1 A1234V C 00:00:00:00 00:00:16:06 10:00:41:08 10:00:57:14'
|
411
|
+
# )
|
412
|
+
# expect(clip).to be_a(EDL::Event)
|
413
|
+
# expect("A1234").to eq clip.track
|
414
|
+
# end
|
415
|
+
|
416
|
+
it 'produce an Event' do
|
417
|
+
m = EDL::EventMatcher.new(25)
|
418
|
+
|
419
|
+
clip = m.apply([],
|
420
|
+
'020 008C V C 08:04:24:24 08:04:25:19 01:00:25:22 01:00:26:17')
|
421
|
+
|
422
|
+
expect(clip).to be_a(EDL::Event)
|
423
|
+
|
424
|
+
expect('020').to eq clip.num
|
425
|
+
expect('008C').to eq clip.reel
|
426
|
+
expect('V').to eq clip.track
|
427
|
+
|
428
|
+
expect('08:04:24:24'.tc).to eq clip.src_start_tc
|
429
|
+
|
430
|
+
expect('08:04:25:19'.tc).to eq clip.src_end_tc
|
431
|
+
expect('01:00:25:22'.tc).to eq clip.rec_start_tc
|
432
|
+
expect('01:00:26:17'.tc).to eq clip.rec_end_tc
|
433
|
+
|
434
|
+
expect(clip.transition).to be_nil
|
435
|
+
expect(clip.timewarp).to be_nil
|
436
|
+
expect(clip.outgoing_transition_duration).to be_zero
|
437
|
+
end
|
438
|
+
|
439
|
+
it 'produce an Event when reel has dots and output a warning' do
|
440
|
+
m = EDL::EventMatcher.new(25)
|
441
|
+
|
442
|
+
# flexmock($stderr).should_receive(:puts).with("Reel name \"TIRED_EDITOR.MOV\" contains dots or spaces, beware.")
|
443
|
+
|
444
|
+
clip = m.apply([],
|
445
|
+
'020 TIREDEDITOR.MOV V C 08:04:24:24 08:04:25:19 01:00:25:22 01:00:26:17')
|
446
|
+
|
447
|
+
expect(clip).to be_a(EDL::Event)
|
448
|
+
|
449
|
+
expect('020').to eq clip.num
|
450
|
+
expect('TIREDEDITOR.MOV').to eq clip.reel
|
451
|
+
expect('V').to eq clip.track
|
452
|
+
|
453
|
+
expect('08:04:24:24'.tc).to eq clip.src_start_tc
|
454
|
+
end
|
455
|
+
|
456
|
+
it 'produce an Event when reel has asterisks' do
|
457
|
+
m = EDL::EventMatcher.new(25)
|
458
|
+
|
459
|
+
clip = m.apply([],
|
460
|
+
'047 *TIRED*EDITOR* V C 00:00:38:15 00:00:39:08 01:06:37:03 01:06:37:20 ')
|
461
|
+
|
462
|
+
expect(clip).to be_a(EDL::Event)
|
463
|
+
|
464
|
+
expect('047').to eq clip.num
|
465
|
+
expect('*TIRED*EDITOR*').to eq clip.reel
|
466
|
+
expect('V').to eq clip.track
|
467
|
+
|
468
|
+
expect('00:00:38:15'.tc).to eq clip.src_start_tc
|
469
|
+
end
|
470
|
+
|
471
|
+
it 'produce an Event with dissolve' do
|
472
|
+
m = EDL::EventMatcher.new(25)
|
473
|
+
|
474
|
+
dissolve = m.apply([],
|
475
|
+
'025 GEN V D 025 00:00:55:10 00:00:58:11 01:00:29:19 01:00:32:20')
|
476
|
+
expect(dissolve).to be_a(EDL::Event)
|
477
|
+
|
478
|
+
expect('025').to eq dissolve.num
|
479
|
+
expect('GEN').to eq dissolve.reel
|
480
|
+
expect('V').to eq dissolve.track
|
481
|
+
expect(dissolve.has_transition?).to be_truthy
|
482
|
+
|
483
|
+
tr = dissolve.transition
|
484
|
+
|
485
|
+
expect(tr).to be_a(EDL::Dissolve)
|
486
|
+
expect(25).to eq tr.duration
|
487
|
+
end
|
488
|
+
|
489
|
+
it 'produce a vanilla Event with proper source length' do
|
490
|
+
# This one has EXACTLY 4 frames of source
|
491
|
+
m = EDL::EventMatcher.new(25)
|
492
|
+
clip = m.apply([], '001 GEN V C 00:01:00:00 00:01:00:04 01:00:00:00 01:00:00:04')
|
493
|
+
expect(clip).to be_a(EDL::Event)
|
494
|
+
expect(4).to eq clip.src_length
|
495
|
+
end
|
496
|
+
|
497
|
+
it 'set flag on the previous event in the stack when a dissolve is encountered' do
|
498
|
+
m = EDL::EventMatcher.new(25)
|
499
|
+
previous_evt = double
|
500
|
+
expect(previous_evt).to receive(:outgoing_transition_duration=).with(25)
|
501
|
+
|
502
|
+
m.apply([previous_evt],
|
503
|
+
'025 GEN V D 025 00:00:55:10 00:00:58:11 01:00:29:19 01:00:32:20')
|
504
|
+
end
|
505
|
+
|
506
|
+
it 'generate a Wipe' do
|
507
|
+
m = EDL::EventMatcher.new(25)
|
508
|
+
wipe = m.apply([],
|
509
|
+
'025 GEN V W001 025 00:00:55:10 00:00:58:11 01:00:29:19 01:00:32:20')
|
510
|
+
|
511
|
+
tr = wipe.transition
|
512
|
+
expect(tr).to be_a(EDL::Wipe)
|
513
|
+
expect(25).to eq tr.duration
|
514
|
+
expect('001').to eq tr.smpte_wipe_index
|
515
|
+
end
|
516
|
+
|
517
|
+
EVT_PATTERNS.each do |pat|
|
518
|
+
it "match #{pat.inspect}" do
|
519
|
+
expect(EDL::EventMatcher.new(25).matches?(pat)).to be_truthy
|
520
|
+
end
|
521
|
+
end
|
522
|
+
|
523
|
+
it 'pass the framerate that it received upon instantiation to the Timecodes being created' do
|
524
|
+
m = EDL::EventMatcher.new(30)
|
525
|
+
clip = m.apply([],
|
526
|
+
'020 008C V C 08:04:24:24 08:04:25:19 01:00:25:22 01:00:26:17')
|
527
|
+
expect(30).to eq clip.rec_start_tc.fps
|
528
|
+
expect(30).to eq clip.rec_end_tc.fps
|
529
|
+
expect(30).to eq clip.src_start_tc.fps
|
530
|
+
expect(30).to eq clip.src_end_tc.fps
|
531
|
+
end
|
532
|
+
end
|
533
|
+
|
534
|
+
describe 'CommentMatcher' do
|
535
|
+
it 'match a comment' do
|
536
|
+
line = '* COMMENT: PURE GARBAGE'
|
537
|
+
expect(EDL::CommentMatcher.new.matches?(line)).to be_truthy
|
538
|
+
end
|
539
|
+
|
540
|
+
it 'match a comment that that contains an asterisk' do
|
541
|
+
line = '* COMMENT: PURE *GARBAGE*'
|
542
|
+
expect(EDL::CommentMatcher.new.matches?(line)).to be_truthy
|
543
|
+
end
|
544
|
+
|
545
|
+
it 'apply the comment to the last clip on the stack' do
|
546
|
+
line = '* COMMENT: PURE GARBAGE'
|
547
|
+
|
548
|
+
comments = []
|
549
|
+
mok_evt = double
|
550
|
+
|
551
|
+
expect(mok_evt).to receive(:comments).exactly(3).times.and_return(comments)
|
552
|
+
2.times { EDL::CommentMatcher.new.apply([mok_evt], line) }
|
553
|
+
|
554
|
+
expect(['* COMMENT: PURE GARBAGE', '* COMMENT: PURE GARBAGE']).to eq mok_evt.comments
|
555
|
+
end
|
556
|
+
end
|
557
|
+
|
558
|
+
describe 'FallbackMatcher' do
|
559
|
+
it 'match anything' do
|
560
|
+
line = 'SOME'
|
561
|
+
expect(EDL::FallbackMatcher.new.matches?(line)).to be_truthy
|
562
|
+
|
563
|
+
line = 'OR ANOTHER '
|
564
|
+
expect(EDL::FallbackMatcher.new.matches?(line)).to be_truthy
|
565
|
+
end
|
566
|
+
|
567
|
+
it 'not match whitespace' do
|
568
|
+
line = "\s\s\s\r\n\r"
|
569
|
+
expect(EDL::FallbackMatcher.new.matches?(line)).to be_falsey
|
570
|
+
end
|
571
|
+
|
572
|
+
it 'append the matched content to comments' do
|
573
|
+
e = double
|
574
|
+
cmts = []
|
575
|
+
expect(e).to receive(:comments).twice.and_return(cmts)
|
576
|
+
|
577
|
+
EDL::FallbackMatcher.new.apply([e], 'FOOBAR')
|
578
|
+
expect(['FOOBAR']).to eq cmts
|
579
|
+
|
580
|
+
EDL::FallbackMatcher.new.apply([e], 'FINAL CUT PRO REEL: 006-I REPLACED BY: 006I')
|
581
|
+
expect(['FOOBAR', 'FINAL CUT PRO REEL: 006-I REPLACED BY: 006I']).to eq cmts
|
582
|
+
end
|
583
|
+
|
584
|
+
it 'raise an ApplyError if no clip is on the stack' do
|
585
|
+
expect do
|
586
|
+
EDL::FallbackMatcher.new.apply([], 'FINAL CUT PRO REEL: 006-I REPLACED BY: 006I')
|
587
|
+
end.to raise_error(EDL::Matcher::ApplyError)
|
588
|
+
end
|
589
|
+
end
|
590
|
+
|
591
|
+
describe 'ClipNameMatcher' do
|
592
|
+
it 'match a clip name' do
|
593
|
+
line = '* FROM CLIP NAME: TAPE_6-10.MOV'
|
594
|
+
expect(EDL::NameMatcher.new.matches?(line)).to be_truthy
|
595
|
+
end
|
596
|
+
|
597
|
+
it 'match a clip name without space after star' do
|
598
|
+
line = '*FROM CLIP NAME: TAPE_6-10.MOV'
|
599
|
+
expect(EDL::NameMatcher.new.matches?(line)).to be_truthy
|
600
|
+
end
|
601
|
+
|
602
|
+
it 'match a clip name containing an asterisk' do
|
603
|
+
line = '* FROM CLIP NAME: 18B_1*'
|
604
|
+
expect(EDL::NameMatcher.new.matches?(line)).to be_truthy
|
605
|
+
end
|
606
|
+
|
607
|
+
it 'not match a simple comment' do
|
608
|
+
line = '* JUNK'
|
609
|
+
expect(EDL::NameMatcher.new.matches?(line)).to be_falsey
|
610
|
+
end
|
611
|
+
|
612
|
+
it 'apply the name to the last event on the stack' do
|
613
|
+
line = '* FROM CLIP NAME: TAPE_6-10.MOV'
|
614
|
+
|
615
|
+
mok_evt = double
|
616
|
+
comments = []
|
617
|
+
expect(mok_evt).to receive(:clip_name=).with('TAPE_6-10.MOV')
|
618
|
+
expect(mok_evt).to receive(:comments).and_return(comments)
|
619
|
+
|
620
|
+
EDL::NameMatcher.new.apply([mok_evt], line)
|
621
|
+
expect(['* FROM CLIP NAME: TAPE_6-10.MOV']).to eq comments
|
622
|
+
end
|
623
|
+
end
|
624
|
+
|
625
|
+
describe 'EffectMatcher' do
|
626
|
+
it 'not match a simple comment' do
|
627
|
+
line = '* STUFF'
|
628
|
+
expect(EDL::EffectMatcher.new.matches?(line)).to be_falsey
|
629
|
+
end
|
630
|
+
|
631
|
+
it 'match a dissolve name' do
|
632
|
+
line = '* EFFECT NAME: CROSS DISSOLVE'
|
633
|
+
expect(EDL::EffectMatcher.new.matches?(line)).to be_truthy
|
634
|
+
end
|
635
|
+
|
636
|
+
it 'match a dissolve name without space after the asterisk' do
|
637
|
+
line = '*EFFECT NAME: CROSS DISSOLVE'
|
638
|
+
expect(EDL::EffectMatcher.new.matches?(line)).to be_truthy
|
639
|
+
end
|
640
|
+
|
641
|
+
it 'match a dissolve name containing an asterisk' do
|
642
|
+
line = '* EFFECT NAME: *CROSS DISSOLVE'
|
643
|
+
expect(EDL::EffectMatcher.new.matches?(line)).to be_truthy
|
644
|
+
end
|
645
|
+
|
646
|
+
it 'apply the effect name to the transition of the last event on the stack' do
|
647
|
+
line = '* EFFECT NAME: CROSS DISSOLVE'
|
648
|
+
mok_evt = double
|
649
|
+
mok_transition = double
|
650
|
+
cmt = []
|
651
|
+
|
652
|
+
expect(mok_evt).to receive(:transition).and_return(mok_transition)
|
653
|
+
expect(mok_evt).to receive(:comments).and_return(cmt)
|
654
|
+
|
655
|
+
expect(mok_transition).to receive(:effect=).with('CROSS DISSOLVE')
|
656
|
+
|
657
|
+
EDL::EffectMatcher.new.apply([mok_evt], line)
|
658
|
+
|
659
|
+
expect(['* EFFECT NAME: CROSS DISSOLVE']).to eq cmt
|
660
|
+
end
|
661
|
+
end
|
662
|
+
|
663
|
+
describe 'An EDL with mixed line breaks' do
|
664
|
+
it 'parse without errors' do
|
665
|
+
list = EDL::Parser.new.parse(File.open(MIXED_LINEBREAKS))
|
666
|
+
expect(['* A', '* B', '* C', '* D', '* E', '* F', '* G']).to eq list[0].comments
|
667
|
+
end
|
668
|
+
end
|
669
|
+
|
670
|
+
describe 'A complex EDL passed via Parser' do
|
671
|
+
it 'parse without errors' do
|
672
|
+
expect { EDL::Parser.new.parse(File.open(FORTY_FIVER)) }.to_not raise_error
|
673
|
+
end
|
674
|
+
|
675
|
+
it 'parse the EDL with \\r line breaks properly' do
|
676
|
+
evts = EDL::Parser.new.parse(File.read(PLATES))
|
677
|
+
expect(3).to eq evts.length
|
678
|
+
end
|
679
|
+
|
680
|
+
# TODO: this does not belong here
|
681
|
+
it 'be properly rewritten from zero' do
|
682
|
+
complex = EDL::Parser.new.parse(File.open(FORTY_FIVER))
|
683
|
+
from_zero = complex.from_zero
|
684
|
+
|
685
|
+
expect(complex.length).to eq(from_zero.length), 'Should have the same number of events'
|
686
|
+
|
687
|
+
expect(from_zero[0].rec_start_tc).to be_zero
|
688
|
+
expect('00:00:42:16'.tc).to eq from_zero[-1].rec_end_tc
|
689
|
+
end
|
690
|
+
end
|
691
|
+
|
692
|
+
describe 'A FinalCutPro speedup with fade at the end' do
|
693
|
+
it 'be parsed cleanly' do
|
694
|
+
list = EDL::Parser.new.parse(File.open(SPEEDUP_AND_FADEOUT))
|
695
|
+
|
696
|
+
expect(2).to eq list.length
|
697
|
+
|
698
|
+
first_evt = list[0]
|
699
|
+
|
700
|
+
tw = first_evt.timewarp
|
701
|
+
expect(tw).to be_a(EDL::Timewarp)
|
702
|
+
|
703
|
+
expect(689).to eq first_evt.rec_length
|
704
|
+
expect(714).to eq first_evt.rec_length_with_transition
|
705
|
+
|
706
|
+
expect(1000).to eq tw.actual_length_of_source
|
707
|
+
expect(140).to eq tw.speed
|
708
|
+
|
709
|
+
expect(1000).to eq first_evt.src_length
|
710
|
+
|
711
|
+
expect('01:00:00:00').to eq first_evt.capture_from_tc.to_s
|
712
|
+
expect('01:00:40:00').to eq first_evt.capture_to_tc.to_s
|
713
|
+
end
|
714
|
+
end
|
715
|
+
|
716
|
+
describe 'In the trailer EDL the event 4' do
|
717
|
+
it 'not have too many comments' do
|
718
|
+
evts = EDL::Parser.new.parse(File.open(TRAILER_EDL))
|
719
|
+
evt = evts[6]
|
720
|
+
expect(5).to eq evt.comments.length
|
721
|
+
end
|
722
|
+
end
|
723
|
+
|
724
|
+
describe 'A FinalCutPro speedup and reverse with fade at the end' do
|
725
|
+
it 'parse cleanly' do
|
726
|
+
first_evt = EDL::Parser.new.parse(File.open(SPEEDUP_REVERSE_AND_FADEOUT)).shift
|
727
|
+
|
728
|
+
expect(first_evt).to be_reverse
|
729
|
+
|
730
|
+
expect(689).to eq first_evt.rec_length
|
731
|
+
expect(714).to eq first_evt.rec_length_with_transition
|
732
|
+
|
733
|
+
tw = first_evt.timewarp
|
734
|
+
|
735
|
+
expect('1h 1f'.tc).to eq tw.source_used_from
|
736
|
+
expect('1h 40s'.tc).to eq tw.source_used_upto
|
737
|
+
end
|
738
|
+
end
|
739
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'edl'
|
4
|
+
|
5
|
+
RSpec.configure do |config|
|
6
|
+
config.filter_run focus: true
|
7
|
+
config.run_all_when_everything_filtered = true
|
8
|
+
# rspec-expectations config goes here. You can use an alternate
|
9
|
+
# assertion/expectation library such as wrong or the stdlib/minitest
|
10
|
+
# assertions if you prefer.
|
11
|
+
config.expect_with :rspec do |expectations|
|
12
|
+
# This option will default to `true` in RSpec 4. It makes the `description`
|
13
|
+
# and `failure_message` of custom matchers include text for helper methods
|
14
|
+
# defined using `chain`, e.g.:
|
15
|
+
# be_bigger_than(2).and_smaller_than(4).description
|
16
|
+
# # => "be bigger than 2 and smaller than 4"
|
17
|
+
# ...rather than:
|
18
|
+
# # => "be bigger than 2"
|
19
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
20
|
+
end
|
21
|
+
|
22
|
+
# rspec-mocks config goes here. You can use an alternate test double
|
23
|
+
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
24
|
+
config.mock_with :rspec do |mocks|
|
25
|
+
# Prevents you from mocking or stubbing a method that does not exist on
|
26
|
+
# a real object. This is generally recommended, and will default to
|
27
|
+
# `true` in RSpec 4.
|
28
|
+
mocks.verify_partial_doubles = true
|
29
|
+
end
|
30
|
+
end
|
metadata
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: edl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Julik Tarkhanov
|
8
|
+
- Philipp Großelfinger
|
8
9
|
autorequire:
|
9
10
|
bindir: exe
|
10
11
|
cert_chain: []
|
@@ -57,16 +58,16 @@ email: me@julik.nl
|
|
57
58
|
executables: []
|
58
59
|
extensions: []
|
59
60
|
extra_rdoc_files:
|
60
|
-
- README.
|
61
|
+
- README.md
|
61
62
|
files:
|
62
63
|
- ".gitignore"
|
63
64
|
- ".rspec"
|
64
65
|
- ".ruby-gemset"
|
65
|
-
- ".ruby-version"
|
66
66
|
- ".travis.yml"
|
67
67
|
- Gemfile
|
68
68
|
- History.txt
|
69
|
-
-
|
69
|
+
- LICENSE.txt
|
70
|
+
- README.md
|
70
71
|
- Rakefile
|
71
72
|
- edl.gemspec
|
72
73
|
- illustr/edl-explain.ai
|
@@ -79,9 +80,27 @@ files:
|
|
79
80
|
- lib/edl/timewarp.rb
|
80
81
|
- lib/edl/transition.rb
|
81
82
|
- lib/edl/version.rb
|
83
|
+
- spec/fixtures/files/45S_SAMPLE.EDL
|
84
|
+
- spec/fixtures/files/FCP_REVERSE.EDL
|
85
|
+
- spec/fixtures/files/KEY_TRANSITION.EDL
|
86
|
+
- spec/fixtures/files/PLATES.EDL
|
87
|
+
- spec/fixtures/files/REEL_IS_CLIP.txt
|
88
|
+
- spec/fixtures/files/REVERSE.EDL
|
89
|
+
- spec/fixtures/files/SIMPLE_DISSOLVE.EDL
|
90
|
+
- spec/fixtures/files/SPEEDUP_AND_FADEOUT.EDL
|
91
|
+
- spec/fixtures/files/SPEEDUP_REVERSE_AND_FADEOUT.EDL
|
92
|
+
- spec/fixtures/files/SPLICEME.EDL
|
93
|
+
- spec/fixtures/files/TIMEWARP.EDL
|
94
|
+
- spec/fixtures/files/TIMEWARP_HALF.EDL
|
95
|
+
- spec/fixtures/files/TRAILER_EDL.edl
|
96
|
+
- spec/fixtures/files/edl_mixed_line_endings.edl
|
97
|
+
- spec/lib/edl/parser_spec.rb
|
98
|
+
- spec/lib/edl_spec.rb
|
99
|
+
- spec/spec_helper.rb
|
82
100
|
homepage: http://guerilla-di.org/edl
|
83
101
|
licenses: []
|
84
|
-
metadata:
|
102
|
+
metadata:
|
103
|
+
allowed_push_host: https://rubygems.org
|
85
104
|
post_install_message:
|
86
105
|
rdoc_options: []
|
87
106
|
require_paths:
|
@@ -95,10 +114,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
95
114
|
requirements:
|
96
115
|
- - ">="
|
97
116
|
- !ruby/object:Gem::Version
|
98
|
-
version:
|
117
|
+
version: 1.2.0
|
99
118
|
requirements: []
|
100
119
|
rubyforge_project:
|
101
|
-
rubygems_version: 2.
|
120
|
+
rubygems_version: 2.4.5.2
|
102
121
|
signing_key:
|
103
122
|
specification_version: 4
|
104
123
|
summary: Parser for EDL (Edit Decision List) files
|
data/.ruby-version
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
2.4.1
|