bulldog 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/DESCRIPTION.txt +3 -0
- data/LICENSE +20 -0
- data/README.rdoc +18 -0
- data/Rakefile +64 -0
- data/VERSION +1 -0
- data/bulldog.gemspec +157 -0
- data/lib/bulldog.rb +95 -0
- data/lib/bulldog/attachment.rb +49 -0
- data/lib/bulldog/attachment/base.rb +167 -0
- data/lib/bulldog/attachment/has_dimensions.rb +94 -0
- data/lib/bulldog/attachment/image.rb +63 -0
- data/lib/bulldog/attachment/maybe.rb +229 -0
- data/lib/bulldog/attachment/none.rb +37 -0
- data/lib/bulldog/attachment/pdf.rb +63 -0
- data/lib/bulldog/attachment/unknown.rb +11 -0
- data/lib/bulldog/attachment/video.rb +143 -0
- data/lib/bulldog/error.rb +5 -0
- data/lib/bulldog/has_attachment.rb +214 -0
- data/lib/bulldog/interpolation.rb +73 -0
- data/lib/bulldog/missing_file.rb +12 -0
- data/lib/bulldog/processor.rb +5 -0
- data/lib/bulldog/processor/argument_tree.rb +116 -0
- data/lib/bulldog/processor/base.rb +124 -0
- data/lib/bulldog/processor/ffmpeg.rb +172 -0
- data/lib/bulldog/processor/image_magick.rb +134 -0
- data/lib/bulldog/processor/one_shot.rb +19 -0
- data/lib/bulldog/reflection.rb +234 -0
- data/lib/bulldog/saved_file.rb +19 -0
- data/lib/bulldog/stream.rb +186 -0
- data/lib/bulldog/style.rb +38 -0
- data/lib/bulldog/style_set.rb +101 -0
- data/lib/bulldog/tempfile.rb +28 -0
- data/lib/bulldog/util.rb +92 -0
- data/lib/bulldog/validations.rb +68 -0
- data/lib/bulldog/vector2.rb +18 -0
- data/rails/init.rb +9 -0
- data/script/console +8 -0
- data/spec/data/empty.txt +0 -0
- data/spec/data/test.jpg +0 -0
- data/spec/data/test.mov +0 -0
- data/spec/data/test.pdf +0 -0
- data/spec/data/test.png +0 -0
- data/spec/data/test2.jpg +0 -0
- data/spec/helpers/image_creation.rb +8 -0
- data/spec/helpers/temporary_directory.rb +25 -0
- data/spec/helpers/temporary_models.rb +76 -0
- data/spec/helpers/temporary_values.rb +102 -0
- data/spec/helpers/test_upload_files.rb +108 -0
- data/spec/helpers/time_travel.rb +20 -0
- data/spec/integration/data/test.jpg +0 -0
- data/spec/integration/lifecycle_hooks_spec.rb +213 -0
- data/spec/integration/processing_image_attachments.rb +72 -0
- data/spec/integration/processing_video_attachments_spec.rb +82 -0
- data/spec/integration/saving_an_attachment_spec.rb +31 -0
- data/spec/matchers/file_operations.rb +159 -0
- data/spec/spec_helper.rb +76 -0
- data/spec/unit/attachment/base_spec.rb +311 -0
- data/spec/unit/attachment/image_spec.rb +128 -0
- data/spec/unit/attachment/maybe_spec.rb +126 -0
- data/spec/unit/attachment/pdf_spec.rb +137 -0
- data/spec/unit/attachment/video_spec.rb +176 -0
- data/spec/unit/attachment_spec.rb +61 -0
- data/spec/unit/has_attachment_spec.rb +700 -0
- data/spec/unit/interpolation_spec.rb +108 -0
- data/spec/unit/processor/argument_tree_spec.rb +159 -0
- data/spec/unit/processor/ffmpeg_spec.rb +467 -0
- data/spec/unit/processor/image_magick_spec.rb +260 -0
- data/spec/unit/processor/one_shot_spec.rb +70 -0
- data/spec/unit/reflection_spec.rb +338 -0
- data/spec/unit/stream_spec.rb +234 -0
- data/spec/unit/style_set_spec.rb +44 -0
- data/spec/unit/style_spec.rb +51 -0
- data/spec/unit/validations_spec.rb +491 -0
- data/spec/unit/vector2_spec.rb +27 -0
- data/tasks/bulldog_tasks.rake +4 -0
- metadata +193 -0
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Interpolation do
|
4
|
+
use_model_class(:Thing, :photo_file_name => :string)
|
5
|
+
|
6
|
+
def interpolate(template)
|
7
|
+
Interpolation.interpolate(template, @thing, :photo, @style)
|
8
|
+
end
|
9
|
+
|
10
|
+
describe ".to_interpolate" do
|
11
|
+
it "should define a custom interpolation token" do
|
12
|
+
begin
|
13
|
+
Interpolation.to_interpolate(:custom){'VALUE'}
|
14
|
+
Thing.has_attachment :attachment do
|
15
|
+
style :output
|
16
|
+
path "dir/:custom.ext"
|
17
|
+
end
|
18
|
+
thing = Thing.new
|
19
|
+
thing.attachment.interpolate_path(:output).should == "dir/VALUE.ext"
|
20
|
+
ensure
|
21
|
+
Interpolation.reset
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe ".reset" do
|
27
|
+
it "should remove custom interpolations" do
|
28
|
+
Bulldog.to_interpolate(:custom){'VALUE'}
|
29
|
+
Bulldog::Interpolation.reset
|
30
|
+
Thing.has_attachment :attachment do
|
31
|
+
style :output
|
32
|
+
path "dir/:custom.ext"
|
33
|
+
end
|
34
|
+
thing = Thing.new
|
35
|
+
lambda{thing.attachment.interpolate_path(:output)}.should raise_error(Interpolation::Error)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "when the file name is not being stored" do
|
40
|
+
before do
|
41
|
+
Thing.has_attachment :photo do
|
42
|
+
style :small, {}
|
43
|
+
store_attributes :file_name => nil
|
44
|
+
end
|
45
|
+
@thing = Thing.new(:photo => test_image_file('test.jpg'))
|
46
|
+
@style = Thing.attachment_reflections[:photo].styles[:small]
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should interpolate :class as the plural class name" do
|
50
|
+
interpolate("a/:class/b").should == "a/things/b"
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should interpolate :id as the record ID" do
|
54
|
+
@thing.stubs(:id).returns(123)
|
55
|
+
interpolate("a/:id/b").should == "a/123/b"
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should interpolate :id_partition as the record ID split into 3 3-digit partitions, 0-padded" do
|
59
|
+
@thing.stubs(:id).returns(12345)
|
60
|
+
interpolate("a/:id_partition/b").should == "a/000/012/345/b"
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should interpolate :attachment as the attachment name" do
|
64
|
+
interpolate("a/:attachment/b").should == "a/photo/b"
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should interpolate :style as the style name" do
|
68
|
+
interpolate("a/:style/b").should == "a/small/b"
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should raise an error for :basename" do
|
72
|
+
lambda{interpolate("a/:basename/b")}.should raise_error(Interpolation::Error)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should raise an error for :extension" do
|
76
|
+
lambda{interpolate("a/:extension/b")}.should raise_error(Interpolation::Error)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should allow using braces for interpolating between symbol characters" do
|
80
|
+
@thing.stubs(:id).returns(5)
|
81
|
+
interpolate("a/x:{id}x/b").should == "a/x5x/b"
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should raise an error for an unrecognized interpolation key" do
|
85
|
+
lambda{interpolate(":invalid")}.should raise_error(Interpolation::Error)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "when the file name is being stored" do
|
90
|
+
before do
|
91
|
+
Thing.has_attachment :photo do
|
92
|
+
style :small, {}
|
93
|
+
store_attributes :file_name => :photo_file_name
|
94
|
+
end
|
95
|
+
|
96
|
+
@thing = Thing.new(:photo => test_image_file('test.jpg'))
|
97
|
+
@style = Thing.attachment_reflections[:photo].styles[:small]
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should interpolate :basename as the basename of the uploaded file" do
|
101
|
+
interpolate("a/:basename/b").should == "a/test.jpg/b"
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should interpolate :extension as the extension of the uploaded file" do
|
105
|
+
interpolate("a/:extension/b").should == "a/jpg/b"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Processor::ArgumentTree do
|
4
|
+
Tree = Processor::ArgumentTree
|
5
|
+
Node = Processor::ArgumentTree::Node
|
6
|
+
|
7
|
+
before do
|
8
|
+
@a = Style.new(:a)
|
9
|
+
@b = Style.new(:b)
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "initially" do
|
13
|
+
before do
|
14
|
+
@tree = Tree.new(StyleSet[@a, @b])
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should have only the root node" do
|
18
|
+
@tree.root.should be_a(Tree::Node)
|
19
|
+
@tree.root.children.should be_empty
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should have all heads pointing to the root node" do
|
23
|
+
root = @tree.root
|
24
|
+
[@a, @b].map{|style| @tree.heads[style]}.should == [root, root]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "#add" do
|
29
|
+
describe "when the tree is empty" do
|
30
|
+
before do
|
31
|
+
@tree = Tree.new(StyleSet[@a, @b])
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should create a new node for the given style and arguments" do
|
35
|
+
@tree.add(@a, ['a', 'b'])
|
36
|
+
@tree.root.children.should have(1).nodes
|
37
|
+
@tree.root.children.first.styles.should == [@a]
|
38
|
+
@tree.root.children.first.arguments.should == ['a', 'b']
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "when there is a node under the head for the given arguments" do
|
43
|
+
before do
|
44
|
+
#
|
45
|
+
# root --- one --- two
|
46
|
+
# ^ ^
|
47
|
+
# a b
|
48
|
+
#
|
49
|
+
@tree = Tree.new(StyleSet[@a])
|
50
|
+
@one = Node.new([@a, @b], ['one', '1'])
|
51
|
+
@two = Node.new([@a], ['two', '2'])
|
52
|
+
@tree.root.children << @one
|
53
|
+
@one.children << @two
|
54
|
+
@tree.heads[@a] = @two
|
55
|
+
@tree.heads[@b] = @one
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should advance the head to that node" do
|
59
|
+
@tree.add(@b, ['two', '2'])
|
60
|
+
@tree.heads[@b].should equal(@two)
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should add the style to the new head node" do
|
64
|
+
@tree.add(@b, ['two', '2'])
|
65
|
+
@tree.heads[@b].should equal(@two)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "when there is no node under the head for the given arguments" do
|
70
|
+
before do
|
71
|
+
#
|
72
|
+
# root --- one
|
73
|
+
# ^
|
74
|
+
# a,b
|
75
|
+
#
|
76
|
+
@tree = Tree.new(StyleSet[@a])
|
77
|
+
one = Node.new([@a, @b], ['one', '1'])
|
78
|
+
@tree.root.children << one
|
79
|
+
@tree.heads[@a] = one
|
80
|
+
@tree.heads[@b] = one
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should create a new child node and advance the head to it" do
|
84
|
+
old_head = @tree.heads[@a]
|
85
|
+
@tree.add(@a, ['two', '2'])
|
86
|
+
@tree.heads[@a].should == old_head.children.first
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should set the arguments of the new node to the given arguments" do
|
90
|
+
@tree.add(@a, ['two', '2'])
|
91
|
+
@tree.heads[@a].arguments.should == ['two', '2']
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should add the style to the new head node" do
|
95
|
+
@tree.add(@a, ['two', '2'])
|
96
|
+
@tree.heads[@a].styles.should == [@a]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe "#arguments" do
|
102
|
+
describe "for a nonbranching tree" do
|
103
|
+
it "should return just the output file if there are no operations to perform" do
|
104
|
+
tree = Tree.new(StyleSet[@a])
|
105
|
+
tree.output(@a, 'A.jpg')
|
106
|
+
tree.arguments.should == ['A.jpg']
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should return the operator arguments strung together with the output file at the end" do
|
110
|
+
tree = Tree.new(StyleSet[@a])
|
111
|
+
tree.add(@a, ['-flip'])
|
112
|
+
tree.add(@a, ['-flop'])
|
113
|
+
tree.output(@a, 'A.jpg')
|
114
|
+
tree.arguments.should == ['-flip', '-flop', 'A.jpg']
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should use a -write argument for all but the last output file" do
|
118
|
+
tree = Tree.new(StyleSet[@a, @b])
|
119
|
+
tree.add(@a, ['-flip'])
|
120
|
+
tree.add(@b, ['-flip'])
|
121
|
+
tree.output(@a, 'A.jpg')
|
122
|
+
tree.output(@b, 'B.jpg')
|
123
|
+
tree.arguments.should == ['-flip', '-write', 'A.jpg', 'B.jpg']
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
describe "for a branching tree" do
|
128
|
+
it "should clone all but the last level" do
|
129
|
+
tree = Tree.new(StyleSet[@a, @b])
|
130
|
+
tree.add(@a, ['-auto-orient'])
|
131
|
+
tree.add(@b, ['-auto-orient'])
|
132
|
+
tree.add(@a, ['-flip'])
|
133
|
+
tree.add(@b, ['-flop'])
|
134
|
+
tree.output(@a, 'A.jpg')
|
135
|
+
tree.output(@b, 'B.jpg')
|
136
|
+
tree.arguments.should == ['-auto-orient', '(', '+clone', '-flip', '-write', 'A.jpg', '+delete', ')', '-flop', 'B.jpg']
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe "#each_callback" do
|
142
|
+
it "should yield callbacks, along with the styles they apply to, in the order they appear in the tree" do
|
143
|
+
#
|
144
|
+
# * one (1)
|
145
|
+
# * two (2) [:a]
|
146
|
+
# * three
|
147
|
+
# * four (4) [:b]
|
148
|
+
#
|
149
|
+
tokens = []
|
150
|
+
tree = Tree.new(StyleSet[@a, @b])
|
151
|
+
tree.add(@a, ['one']){tokens << 1}
|
152
|
+
tree.add(@a, ['two']){tokens << 2}
|
153
|
+
|
154
|
+
tree.add(@b, ['one']){tokens << 1}
|
155
|
+
tree.add(@b, ['three']){tokens << 2}
|
156
|
+
tree.add(@b, ['four']){tokens << 4}
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,467 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Processor::Ffmpeg do
|
4
|
+
use_model_class(:Thing,
|
5
|
+
:video_file_name => :string,
|
6
|
+
:frame_file_name => :string)
|
7
|
+
|
8
|
+
before do
|
9
|
+
spec = self
|
10
|
+
Thing.class_eval do
|
11
|
+
has_attachment :video do
|
12
|
+
path "#{spec.temporary_directory}/video.:style.:extension"
|
13
|
+
end
|
14
|
+
|
15
|
+
has_attachment :frame do
|
16
|
+
path "#{spec.temporary_directory}/frame.:style.:extension"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
thing = Thing.create(:video => test_video_file('test.mov'))
|
21
|
+
@thing = Thing.find(thing.id)
|
22
|
+
end
|
23
|
+
|
24
|
+
def ffmpeg
|
25
|
+
Bulldog::Processor::Ffmpeg.ffmpeg_command
|
26
|
+
end
|
27
|
+
|
28
|
+
def original_video_path
|
29
|
+
"#{temporary_directory}/video.original.mov"
|
30
|
+
end
|
31
|
+
|
32
|
+
def output_video_path
|
33
|
+
"#{temporary_directory}/video.output.mov"
|
34
|
+
end
|
35
|
+
|
36
|
+
def original_frame_path
|
37
|
+
"#{temporary_directory}/frame.original.jpg"
|
38
|
+
end
|
39
|
+
|
40
|
+
def configure(attachment_name, &block)
|
41
|
+
Thing.attachment_reflections[attachment_name].configure(&block)
|
42
|
+
end
|
43
|
+
|
44
|
+
def video_style(name, attributes={})
|
45
|
+
configure(:video) do
|
46
|
+
style name, attributes
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def process_video(options={}, &block)
|
51
|
+
configure(:video) do
|
52
|
+
process(options.merge(:on => :event, :with => :ffmpeg), &block)
|
53
|
+
end
|
54
|
+
@thing.video.process(:event)
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "when a simple conversion is performed" do
|
58
|
+
before do
|
59
|
+
video_style :output
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should run ffmpeg" do
|
63
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-y', output_video_path)
|
64
|
+
process_video
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should log the command run" do
|
68
|
+
log_path = "#{temporary_directory}/log"
|
69
|
+
open(log_path, 'w') do |file|
|
70
|
+
Bulldog.logger = Logger.new(file)
|
71
|
+
process_video
|
72
|
+
end
|
73
|
+
File.read(log_path).should include("[Bulldog] Running: #{ffmpeg}")
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "#encode" do
|
78
|
+
it "should force an encode" do
|
79
|
+
video_style :output
|
80
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-y', output_video_path)
|
81
|
+
process_video{encode}
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should allow overriding style attributes from parameters" do
|
85
|
+
video_style :output, :video_codec => 'libx264'
|
86
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-vcodec', 'libtheora', '-y', output_video_path)
|
87
|
+
process_video{encode(:video_codec => 'libtheora')}
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe "#record_frame" do
|
92
|
+
before do
|
93
|
+
@thing.video.stubs(:duration).returns(20)
|
94
|
+
end
|
95
|
+
|
96
|
+
describe "when no attachment to assign to is given" do
|
97
|
+
it "should force a frame record" do
|
98
|
+
video_style :frame, :format => 'png'
|
99
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-vframes', '1', '-ss', '10', '-f', 'image2', '-vcodec', 'png', '-y', "#{temporary_directory}/video.frame.png")
|
100
|
+
process_video{record_frame}
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should allow overriding style attributes from parameters" do
|
104
|
+
video_style :frame, :format => 'png', :position => 5
|
105
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-vframes', '1', '-ss', '15', '-f', 'image2', '-vcodec', 'mjpeg', '-y', "#{temporary_directory}/video.frame.png")
|
106
|
+
process_video{record_frame(:position => 15, :codec => 'mjpeg')}
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should yield the path to the block if one is given" do
|
110
|
+
video_style :frame, :format => 'jpg'
|
111
|
+
spec = self
|
112
|
+
block_run = false
|
113
|
+
Bulldog.expects(:run).once.returns('')
|
114
|
+
process_video do
|
115
|
+
record_frame do |path|
|
116
|
+
block_run = true
|
117
|
+
spec.instance_eval do
|
118
|
+
path.should == "#{temporary_directory}/video.frame.jpg"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
block_run.should be_true
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
describe "when an attachment to assign to is given" do
|
127
|
+
it "should output the file to the specified attachment's original path" do
|
128
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-vframes', '1', '-ss', '10', '-f', 'image2', '-vcodec', 'mjpeg', '-y', original_frame_path)
|
129
|
+
process_video :styles => [:original] do
|
130
|
+
record_frame(:format => 'jpg', :assign_to => :frame)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should assign the file to the specified attachment" do
|
135
|
+
process_video :styles => [:original] do
|
136
|
+
record_frame(:format => 'jpg', :assign_to => :frame)
|
137
|
+
end
|
138
|
+
@thing.frame.should_not be_blank
|
139
|
+
end
|
140
|
+
|
141
|
+
it "should yield the written path to any block passed, in the context of the processor" do
|
142
|
+
context = nil
|
143
|
+
argument = nil
|
144
|
+
process_video :styles => [:original] do
|
145
|
+
record_frame(:format => 'jpg', :assign_to => :frame) do |path|
|
146
|
+
context = self
|
147
|
+
argument = path
|
148
|
+
end
|
149
|
+
end
|
150
|
+
context.should be_a(Processor::Ffmpeg)
|
151
|
+
argument.should == original_frame_path
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
describe "using style attributes" do
|
156
|
+
def frame_path(format)
|
157
|
+
"#{temporary_directory}/video.output.#{format}"
|
158
|
+
end
|
159
|
+
|
160
|
+
it "should record a frame at the given position" do
|
161
|
+
video_style :output, :format => 'png'
|
162
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-vframes', '1', '-ss', '19', '-f', 'image2', '-vcodec', 'png', '-y', frame_path('png'))
|
163
|
+
process_video{record_frame(:position => 19)}
|
164
|
+
end
|
165
|
+
|
166
|
+
it "should default to recording a frame at the halfway point" do
|
167
|
+
video_style :output, :format => 'png'
|
168
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-vframes', '1', '-ss', '10', '-f', 'image2', '-vcodec', 'png', '-y', frame_path('png'))
|
169
|
+
process_video{record_frame}
|
170
|
+
end
|
171
|
+
|
172
|
+
it "should record a frame at the halfway point if the given position is out of bounds" do
|
173
|
+
video_style :output, :format => 'png'
|
174
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-vframes', '1', '-ss', '21', '-f', 'image2', '-vcodec', 'png', '-y', frame_path('png'))
|
175
|
+
process_video{record_frame(:position => 21)}
|
176
|
+
end
|
177
|
+
|
178
|
+
it "should use the specified codec if given" do
|
179
|
+
video_style :output, :format => 'png'
|
180
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-vframes', '1', '-ss', '10', '-f', 'image2', '-vcodec', 'mjpeg', '-y', frame_path('png'))
|
181
|
+
process_video{record_frame(:codec => 'mjpeg')}
|
182
|
+
end
|
183
|
+
|
184
|
+
it "should use the mjpeg codec by default for jpg images" do
|
185
|
+
video_style :output, :format => 'jpg'
|
186
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-vframes', '1', '-ss', '10', '-f', 'image2', '-vcodec', 'mjpeg', '-y', frame_path('jpg'))
|
187
|
+
process_video{record_frame}
|
188
|
+
end
|
189
|
+
|
190
|
+
it "should use the mjpeg codec by default for jpeg images" do
|
191
|
+
video_style :output, :format => 'jpeg'
|
192
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-vframes', '1', '-ss', '10', '-f', 'image2', '-vcodec', 'mjpeg', '-y', frame_path('jpeg'))
|
193
|
+
process_video{record_frame}
|
194
|
+
end
|
195
|
+
|
196
|
+
it "should use the png codec by default for png images" do
|
197
|
+
video_style :output, :format => 'png'
|
198
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-vframes', '1', '-ss', '10', '-f', 'image2', '-vcodec', 'png', '-y', frame_path('png'))
|
199
|
+
process_video{record_frame}
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
describe "encoding style attributes" do
|
205
|
+
describe "video" do
|
206
|
+
it "should interpret '30fps' as a frame rate of 30fps" do
|
207
|
+
video_style :output, :video => '30fps'
|
208
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-r', '30', '-y', output_video_path)
|
209
|
+
process_video{encode}
|
210
|
+
end
|
211
|
+
|
212
|
+
it "should interpret '30FPS' as a frame rate of 30fps" do
|
213
|
+
video_style :output, :video => '30FPS'
|
214
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-r', '30', '-y', output_video_path)
|
215
|
+
process_video{encode}
|
216
|
+
end
|
217
|
+
|
218
|
+
it "should interpret 628kbps as a video bit rate of 628kbps" do
|
219
|
+
video_style :output, :video => '628kbps'
|
220
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-b', '628k', '-y', output_video_path)
|
221
|
+
process_video{encode}
|
222
|
+
end
|
223
|
+
|
224
|
+
it "should interpret any other word as a video codec" do
|
225
|
+
video_style :output, :video => 'libx264'
|
226
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-vcodec', 'libx264', '-y', output_video_path)
|
227
|
+
process_video{encode}
|
228
|
+
end
|
229
|
+
|
230
|
+
it "should combine multiple attributes of the video stream as given" do
|
231
|
+
video_style :output, :video => 'libx264 30fps 628kbps'
|
232
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-vcodec', 'libx264', '-r', '30', '-b', '628k', '-y', output_video_path)
|
233
|
+
process_video{encode}
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
describe "audio" do
|
238
|
+
it "should interpret '44100Hz' as a sampling frequency of 44100Hz" do
|
239
|
+
video_style :output, :audio => '44100Hz'
|
240
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-ar', '44100', '-y', output_video_path)
|
241
|
+
process_video{encode}
|
242
|
+
end
|
243
|
+
|
244
|
+
it "should interpret '44100hz' as a sampling frequency of 44100Hz" do
|
245
|
+
video_style :output, :audio => '44100hz'
|
246
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-ar', '44100', '-y', output_video_path)
|
247
|
+
process_video{encode}
|
248
|
+
end
|
249
|
+
|
250
|
+
it "should interpret '64kbps' as a sampling frequency of 64kbps" do
|
251
|
+
video_style :output, :audio => '64kbps'
|
252
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-ab', '64k', '-y', output_video_path)
|
253
|
+
process_video{encode}
|
254
|
+
end
|
255
|
+
|
256
|
+
it "should interpret 'mono' as 1 channel" do
|
257
|
+
video_style :output, :audio => 'mono'
|
258
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-ac', '1', '-y', output_video_path)
|
259
|
+
process_video{encode}
|
260
|
+
end
|
261
|
+
|
262
|
+
it "should interpret 'stereo' as 2 channels" do
|
263
|
+
video_style :output, :audio => 'stereo'
|
264
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-ac', '2', '-y', output_video_path)
|
265
|
+
process_video{encode}
|
266
|
+
end
|
267
|
+
|
268
|
+
it "should interpret any other word as an audio codec" do
|
269
|
+
video_style :output, :audio => 'libfaac'
|
270
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-acodec', 'libfaac', '-y', output_video_path)
|
271
|
+
process_video{encode}
|
272
|
+
end
|
273
|
+
|
274
|
+
it "should combine multiple attributes of the audio stream as given" do
|
275
|
+
video_style :output, :audio => 'libfaac 44100Hz 64kbps'
|
276
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-acodec', 'libfaac', '-ar', '44100', '-ab', '64k', '-y', output_video_path)
|
277
|
+
process_video{encode}
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
describe "video_codec" do
|
282
|
+
it "should set the video codec" do
|
283
|
+
video_style :output, :video_codec => 'libx264'
|
284
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-vcodec', 'libx264', '-y', output_video_path)
|
285
|
+
process_video{encode}
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
describe "frame_rate" do
|
290
|
+
it "should set the frame rate" do
|
291
|
+
video_style :output, :frame_rate => 30
|
292
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-r', '30', '-y', output_video_path)
|
293
|
+
process_video{encode}
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
describe "video_bit_rate" do
|
298
|
+
it "should set the video bit rate" do
|
299
|
+
video_style :output, :video_bit_rate => '64k'
|
300
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-b', '64k', '-y', output_video_path)
|
301
|
+
process_video{encode}
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
describe "audio_codec" do
|
306
|
+
it "should set the audio codec" do
|
307
|
+
video_style :output, :audio_codec => 'libfaac'
|
308
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-acodec', 'libfaac', '-y', output_video_path)
|
309
|
+
process_video{encode}
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
describe "sampling_rate" do
|
314
|
+
it "should set the sampling rate" do
|
315
|
+
video_style :output, :sampling_rate => 44100
|
316
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-ar', '44100', '-y', output_video_path)
|
317
|
+
process_video{encode}
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
describe "audio_bit_rate" do
|
322
|
+
it "should set the audio bit rate" do
|
323
|
+
video_style :output, :audio_bit_rate => '64k'
|
324
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-ab', '64k', '-y', output_video_path)
|
325
|
+
process_video{encode}
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
describe "channels" do
|
330
|
+
it "should set the number of channels" do
|
331
|
+
video_style :output, :channels => 2
|
332
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-ac', '2', '-y', output_video_path)
|
333
|
+
process_video{encode}
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
describe "video_preset" do
|
338
|
+
it "should set a video preset" do
|
339
|
+
video_style :output, :video_preset => 'one'
|
340
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-vpre', 'one', '-y', output_video_path)
|
341
|
+
process_video{encode}
|
342
|
+
end
|
343
|
+
|
344
|
+
it "should allow setting more than one video preset" do
|
345
|
+
video_style :output, :video_preset => ['one', 'two']
|
346
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-vpre', 'one', '-vpre', 'two', '-y', output_video_path)
|
347
|
+
process_video{encode}
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
describe "audio_preset" do
|
352
|
+
it "should set a audio preset" do
|
353
|
+
video_style :output, :audio_preset => 'one'
|
354
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-apre', 'one', '-y', output_video_path)
|
355
|
+
process_video{encode}
|
356
|
+
end
|
357
|
+
|
358
|
+
it "should allow setting more than one audio preset" do
|
359
|
+
video_style :output, :audio_preset => ['one', 'two']
|
360
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-apre', 'one', '-apre', 'two', '-y', output_video_path)
|
361
|
+
process_video{encode}
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
describe "subtitle_preset" do
|
366
|
+
it "should set a subtitle preset" do
|
367
|
+
video_style :output, :subtitle_preset => 'one'
|
368
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-spre', 'one', '-y', output_video_path)
|
369
|
+
process_video{encode}
|
370
|
+
end
|
371
|
+
|
372
|
+
it "should allow setting more than one subtitle preset" do
|
373
|
+
video_style :output, :subtitle_preset => ['one', 'two']
|
374
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-spre', 'one', '-spre', 'two', '-y', output_video_path)
|
375
|
+
process_video{encode}
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
describe "size" do
|
380
|
+
it "should set the video size" do
|
381
|
+
video_style :output, :size => '400x300'
|
382
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-s', '400x300', '-y', output_video_path)
|
383
|
+
process_video{encode}
|
384
|
+
end
|
385
|
+
|
386
|
+
it "should maintain the original aspect ratio" do
|
387
|
+
video_style :output, :size => '600x600'
|
388
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-s', '600x450', '-y', output_video_path)
|
389
|
+
process_video{encode}
|
390
|
+
end
|
391
|
+
|
392
|
+
it "should maintain the original aspect ratio when the style size is overridden"
|
393
|
+
end
|
394
|
+
|
395
|
+
describe "num_channels" do
|
396
|
+
it "should set the number of channels" do
|
397
|
+
video_style :output, :channels => 2
|
398
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-ac', '2', '-y', output_video_path)
|
399
|
+
process_video{encode}
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
describe "deinterlaced" do
|
404
|
+
it "should set the deinterlace flag" do
|
405
|
+
video_style :output, :deinterlaced => true
|
406
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-deinterlace', '-y', output_video_path)
|
407
|
+
process_video{encode}
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
describe "pixel_format" do
|
412
|
+
it "should set the pixel format" do
|
413
|
+
video_style :output, :pixel_format => 'yuv420p'
|
414
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-pix_fmt', 'yuv420p', '-y', output_video_path)
|
415
|
+
process_video{encode}
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
describe "b_strategy" do
|
420
|
+
it "should set the b-strategy" do
|
421
|
+
video_style :output, :b_strategy => 1
|
422
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-b_strategy', '1', '-y', output_video_path)
|
423
|
+
process_video{encode}
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
describe "buffer_size" do
|
428
|
+
it "should set the video buffer verifier buffer size" do
|
429
|
+
video_style :output, :buffer_size => '2M'
|
430
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-bufsize', '2M', '-y', output_video_path)
|
431
|
+
process_video{encode}
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
435
|
+
describe "coder" do
|
436
|
+
it "should set the coder" do
|
437
|
+
video_style :output, :coder => 'ac'
|
438
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-coder', 'ac', '-y', output_video_path)
|
439
|
+
process_video{encode}
|
440
|
+
end
|
441
|
+
end
|
442
|
+
|
443
|
+
describe "verbosity" do
|
444
|
+
it "should set the verbosity" do
|
445
|
+
video_style :output, :verbosity => 1
|
446
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-v', '1', '-y', output_video_path)
|
447
|
+
process_video{encode}
|
448
|
+
end
|
449
|
+
end
|
450
|
+
|
451
|
+
describe "flags" do
|
452
|
+
it "should set the flags" do
|
453
|
+
video_style :output, :flags => '+loop'
|
454
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-flags', '+loop', '-y', output_video_path)
|
455
|
+
process_video{encode}
|
456
|
+
end
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
describe "#use_threads" do
|
461
|
+
it "should set the number of threads" do
|
462
|
+
video_style :output
|
463
|
+
Bulldog.expects(:run).once.with(ffmpeg, '-i', original_video_path, '-threads', '2', '-y', output_video_path)
|
464
|
+
process_video{use_threads 2}
|
465
|
+
end
|
466
|
+
end
|
467
|
+
end
|