edl 0.1.6 → 0.1.7
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.
- 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
|