bulldog 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. data/.gitignore +2 -0
  2. data/DESCRIPTION.txt +3 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +18 -0
  5. data/Rakefile +64 -0
  6. data/VERSION +1 -0
  7. data/bulldog.gemspec +157 -0
  8. data/lib/bulldog.rb +95 -0
  9. data/lib/bulldog/attachment.rb +49 -0
  10. data/lib/bulldog/attachment/base.rb +167 -0
  11. data/lib/bulldog/attachment/has_dimensions.rb +94 -0
  12. data/lib/bulldog/attachment/image.rb +63 -0
  13. data/lib/bulldog/attachment/maybe.rb +229 -0
  14. data/lib/bulldog/attachment/none.rb +37 -0
  15. data/lib/bulldog/attachment/pdf.rb +63 -0
  16. data/lib/bulldog/attachment/unknown.rb +11 -0
  17. data/lib/bulldog/attachment/video.rb +143 -0
  18. data/lib/bulldog/error.rb +5 -0
  19. data/lib/bulldog/has_attachment.rb +214 -0
  20. data/lib/bulldog/interpolation.rb +73 -0
  21. data/lib/bulldog/missing_file.rb +12 -0
  22. data/lib/bulldog/processor.rb +5 -0
  23. data/lib/bulldog/processor/argument_tree.rb +116 -0
  24. data/lib/bulldog/processor/base.rb +124 -0
  25. data/lib/bulldog/processor/ffmpeg.rb +172 -0
  26. data/lib/bulldog/processor/image_magick.rb +134 -0
  27. data/lib/bulldog/processor/one_shot.rb +19 -0
  28. data/lib/bulldog/reflection.rb +234 -0
  29. data/lib/bulldog/saved_file.rb +19 -0
  30. data/lib/bulldog/stream.rb +186 -0
  31. data/lib/bulldog/style.rb +38 -0
  32. data/lib/bulldog/style_set.rb +101 -0
  33. data/lib/bulldog/tempfile.rb +28 -0
  34. data/lib/bulldog/util.rb +92 -0
  35. data/lib/bulldog/validations.rb +68 -0
  36. data/lib/bulldog/vector2.rb +18 -0
  37. data/rails/init.rb +9 -0
  38. data/script/console +8 -0
  39. data/spec/data/empty.txt +0 -0
  40. data/spec/data/test.jpg +0 -0
  41. data/spec/data/test.mov +0 -0
  42. data/spec/data/test.pdf +0 -0
  43. data/spec/data/test.png +0 -0
  44. data/spec/data/test2.jpg +0 -0
  45. data/spec/helpers/image_creation.rb +8 -0
  46. data/spec/helpers/temporary_directory.rb +25 -0
  47. data/spec/helpers/temporary_models.rb +76 -0
  48. data/spec/helpers/temporary_values.rb +102 -0
  49. data/spec/helpers/test_upload_files.rb +108 -0
  50. data/spec/helpers/time_travel.rb +20 -0
  51. data/spec/integration/data/test.jpg +0 -0
  52. data/spec/integration/lifecycle_hooks_spec.rb +213 -0
  53. data/spec/integration/processing_image_attachments.rb +72 -0
  54. data/spec/integration/processing_video_attachments_spec.rb +82 -0
  55. data/spec/integration/saving_an_attachment_spec.rb +31 -0
  56. data/spec/matchers/file_operations.rb +159 -0
  57. data/spec/spec_helper.rb +76 -0
  58. data/spec/unit/attachment/base_spec.rb +311 -0
  59. data/spec/unit/attachment/image_spec.rb +128 -0
  60. data/spec/unit/attachment/maybe_spec.rb +126 -0
  61. data/spec/unit/attachment/pdf_spec.rb +137 -0
  62. data/spec/unit/attachment/video_spec.rb +176 -0
  63. data/spec/unit/attachment_spec.rb +61 -0
  64. data/spec/unit/has_attachment_spec.rb +700 -0
  65. data/spec/unit/interpolation_spec.rb +108 -0
  66. data/spec/unit/processor/argument_tree_spec.rb +159 -0
  67. data/spec/unit/processor/ffmpeg_spec.rb +467 -0
  68. data/spec/unit/processor/image_magick_spec.rb +260 -0
  69. data/spec/unit/processor/one_shot_spec.rb +70 -0
  70. data/spec/unit/reflection_spec.rb +338 -0
  71. data/spec/unit/stream_spec.rb +234 -0
  72. data/spec/unit/style_set_spec.rb +44 -0
  73. data/spec/unit/style_spec.rb +51 -0
  74. data/spec/unit/validations_spec.rb +491 -0
  75. data/spec/unit/vector2_spec.rb +27 -0
  76. data/tasks/bulldog_tasks.rake +4 -0
  77. 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