bulldog 0.0.1
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.
- 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
|