vtools 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/INSTALL +0 -0
  2. data/LICENSE +20 -0
  3. data/README.md +131 -0
  4. data/Rakefile +29 -0
  5. data/bin/vtools +22 -0
  6. data/doc/CONFIG.md +36 -0
  7. data/doc/HOOKS.md +37 -0
  8. data/doc/LIB_EXAMPLE.md +109 -0
  9. data/extconf.rb +7 -0
  10. data/lib/vtools.rb +79 -0
  11. data/lib/vtools/config.rb +91 -0
  12. data/lib/vtools/convert_options.rb +155 -0
  13. data/lib/vtools/converter.rb +98 -0
  14. data/lib/vtools/errors.rb +21 -0
  15. data/lib/vtools/handler.rb +43 -0
  16. data/lib/vtools/harvester.rb +71 -0
  17. data/lib/vtools/job.rb +48 -0
  18. data/lib/vtools/options.rb +101 -0
  19. data/lib/vtools/shared_methods.rb +131 -0
  20. data/lib/vtools/storage.rb +67 -0
  21. data/lib/vtools/thumbnailer.rb +93 -0
  22. data/lib/vtools/thumbs_options.rb +80 -0
  23. data/lib/vtools/version.rb +6 -0
  24. data/lib/vtools/version.rb~ +4 -0
  25. data/lib/vtools/video.rb +158 -0
  26. data/setup.rb +1585 -0
  27. data/spec/config_spec.rb +142 -0
  28. data/spec/convert_options_spec.rb +284 -0
  29. data/spec/converter_spec.rb +167 -0
  30. data/spec/errors_spec.rb +39 -0
  31. data/spec/fixtures/outputs/file_with_iso-8859-1.txt +35 -0
  32. data/spec/fixtures/outputs/file_with_no_audio.txt +18 -0
  33. data/spec/fixtures/outputs/file_with_non_supported_audio.txt +29 -0
  34. data/spec/fixtures/outputs/file_with_start_value.txt +19 -0
  35. data/spec/fixtures/outputs/file_with_surround_sound.txt +19 -0
  36. data/spec/handler_spec.rb +81 -0
  37. data/spec/harvester_spec.rb +189 -0
  38. data/spec/job_spec.rb +130 -0
  39. data/spec/options_spec.rb +52 -0
  40. data/spec/shared_methods_spec.rb +351 -0
  41. data/spec/spec_helper.rb +20 -0
  42. data/spec/storage_spec.rb +106 -0
  43. data/spec/thumbnailer_spec.rb +178 -0
  44. data/spec/thumbs_options_spec.rb +159 -0
  45. data/spec/video_spec.rb +274 -0
  46. data/vtools.gemspec +29 -0
  47. metadata +177 -0
@@ -0,0 +1,178 @@
1
+ require "spec_helper"
2
+ require "thumbnailer"
3
+
4
+ describe VTools::Thumbnailer do
5
+
6
+ let(:video) { double (nil) }
7
+
8
+ # hooks
9
+ before do
10
+ @thumbnailer = VTools::Thumbnailer.new video
11
+ end
12
+
13
+ # specs
14
+ context "#run" do
15
+
16
+ before do
17
+ @output_file = nil
18
+ @options = {}
19
+ end
20
+
21
+ def prepare_thumbnailer error_strings = []
22
+ # prepare output lines
23
+ index = 0
24
+ # output iterator (raise error once per item)
25
+ Open3.stub!(:popen3).and_return do |*args, block|
26
+ (io = StringIO.new(error_strings[index].to_s)).rewind
27
+ block.yield(nil, io, nil)
28
+ index += 1
29
+ end
30
+
31
+ @options.stub(:to_s) { "test.options" }
32
+
33
+ video.stub(:thumbs_options) { @options }
34
+ video.stub(:name) { "video.name" }
35
+ video.stub(:path) { "video/path" }
36
+ @converter.instance_variable_set( :@video, video )
37
+
38
+ VTools::CONFIG[:thumb_binary] = "tested.thumbnailer"
39
+ @output_file = "/#{video.name}_"
40
+
41
+ VTools.should_receive(:fix_encoding).exactly(@options[:thumb_count].to_i).times.and_return {|str| str}
42
+ VTools::Handler.should_receive(:exec).with(:before_thumb, video, @options)
43
+ @thumbnailer.should_receive(:generate_path)
44
+ end
45
+
46
+ it "creates thumbnails without postfix" do
47
+
48
+ @options = {:thumb_count => 3, :thumb_start_point => 0}
49
+ prepare_thumbnailer
50
+
51
+ thumbs_array = [
52
+ {:path => "#{@output_file}0.jpg", :offset => 0},
53
+ {:path => "#{@output_file}1.jpg", :offset => 1},
54
+ {:path => "#{@output_file}2.jpg", :offset => 2},
55
+ ]
56
+
57
+ thumbs_array.each do |index|
58
+ VTools::Handler.should_receive(:exec).with(:in_thumb, video, index)
59
+ end
60
+
61
+ @thumbnailer.should_receive(:set_point).exactly(3).times.and_return { |sec| sec }
62
+ @thumbnailer.should_receive(:time_offset).exactly(3).times.and_return { |sec| (sec.is_a?(Hash) ? sec[:thumb_start_point] : sec) }
63
+ VTools::Handler.should_receive(:exec).with(:thumb_success, video, thumbs_array)
64
+
65
+ @thumbnailer.run.should == thumbs_array
66
+ end
67
+
68
+ it "creates thumb with postfix and offset" do
69
+
70
+ @options = {:thumb_count => 1, :thumb_start_point => 3, :postfix => "test.postfix"}
71
+ prepare_thumbnailer
72
+
73
+ thumbs_array = [ {:path => "#{@output_file}test.postfix.jpg", :offset => 3} ]
74
+
75
+ VTools::Handler.should_receive(:exec).with(:in_thumb, video, thumbs_array[0])
76
+
77
+ @thumbnailer.should_receive(:set_point).once.and_return { |sec| sec }
78
+ @thumbnailer.should_receive(:time_offset).once.and_return { |sec| sec[:thumb_start_point] }
79
+ VTools::Handler.should_receive(:exec).with(:thumb_success, video, thumbs_array)
80
+
81
+ @thumbnailer.run.should == thumbs_array
82
+ end
83
+
84
+ it "creates thumbs via options[:t]" do
85
+ @options = {:t => 12, :thumb_count => 1}
86
+ prepare_thumbnailer
87
+
88
+ thumbs_array = [ {:path => "#{@output_file}12.jpg", :offset => 12} ]
89
+
90
+ VTools::Handler.stub(:exec)
91
+ @thumbnailer.should_not_receive(:set_point)
92
+ @thumbnailer.should_receive(:time_offset).with(12).once.and_return { |sec| sec }
93
+
94
+ @thumbnailer.run.should == thumbs_array
95
+ end
96
+
97
+ it "creates 2 of 3 thumbs" do
98
+ @options = {:thumb_count => 3, :thumb_start_point => 0}
99
+ prepare_thumbnailer ["thumbnailer error"]
100
+
101
+ thumbs_array = [
102
+ {:path => "#{@output_file}1.jpg", :offset => 1},
103
+ {:path => "#{@output_file}2.jpg", :offset => 2},
104
+ ]
105
+
106
+ VTools::Handler.should_receive(:exec).with(:thumb_error, video, " Errors: thumbnailer error (/video.name_0.jpg). ")
107
+ thumbs_array.each do |index|
108
+ VTools::Handler.should_receive(:exec).with(:in_thumb, video, index)
109
+ end
110
+
111
+ @thumbnailer.should_receive(:set_point).exactly(3).times.and_return { |sec| sec }
112
+ @thumbnailer.should_receive(:time_offset).exactly(2).times.and_return { |sec| (sec.is_a?(Hash) ? sec[:thumb_start_point] : sec) }
113
+
114
+ @thumbnailer.run.should == thumbs_array
115
+ end
116
+
117
+ it "completely fails on thumb creation" do
118
+ @options = {:thumb_count => 2, :thumb_start_point => 0}
119
+ prepare_thumbnailer ["thumbnailer error", "thumbnailer error 2"]
120
+
121
+
122
+ VTools::Handler.should_receive(:exec).with(
123
+ :thumb_error, video,
124
+ " Errors: thumbnailer error (/video.name_0.jpg);thumbnailer error 2 (/video.name_1.jpg). "
125
+ )
126
+
127
+ @thumbnailer.should_not_receive(:time_offset)
128
+ @thumbnailer.should_receive(:set_point).exactly(2).times.and_return { |sec| sec }
129
+
130
+ expect { @thumbnailer.run }.to raise_error VTools::ProcessError, /Thumbnailer error:/
131
+ end
132
+ end
133
+
134
+ context "#set_point" do
135
+
136
+ context "generates percent point from hash" do
137
+ it "indicated in percent" do
138
+ config = {:thumb_start_point => "12.2%"}
139
+ @thumbnailer.method(:set_point).call(config).should == 12
140
+ end
141
+
142
+ it "indicated in seconds" do
143
+ config = { :thumb_start_point => 122 }
144
+ @thumbnailer.should_receive(:time_offset).with("122")
145
+
146
+ @thumbnailer.method(:set_point).call(config)
147
+ end
148
+ end
149
+
150
+ it "generates percent point from integer" do
151
+ @thumbnailer.instance_variable_set(:@total, 3) # total count
152
+ @thumbnailer.method(:set_point).call(1).should == 33 # first pic
153
+ @thumbnailer.method(:set_point).call(2).should == 66
154
+ @thumbnailer.method(:set_point).call(3).should == 100 # last pic
155
+ end
156
+
157
+ it "returns default" do
158
+ @thumbnailer.method(:set_point).call("invalid").should == 0
159
+ end
160
+ end
161
+
162
+ context "#time_offset" do
163
+
164
+ let(:video) {v = double nil; v.stub(:duration){120.3}; v}
165
+
166
+ before { @thumbnailer.instance_variable_set(:@video, video) }
167
+
168
+ it "calculates offset from valid shift value" do
169
+ @thumbnailer.method(:time_offset).call(10).should == "00:00:10"
170
+ @thumbnailer.method(:time_offset).call(23.2).should == "00:00:23"
171
+ end
172
+
173
+ it "shift value is invalid returns video duration" do
174
+ @thumbnailer.method(:time_offset).call(123).should == "00:02:00"
175
+ @thumbnailer.method(:time_offset).call(130).should == "00:02:00"
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,159 @@
1
+ require "spec_helper"
2
+ require "thumbs_options"
3
+
4
+ describe VTools::ThumbsOptions do
5
+
6
+ # hooks
7
+ before :all do
8
+ @options_base = VTools::ThumbsOptions.new({})
9
+ end
10
+
11
+ before :each do
12
+ @options = @options_base.dup
13
+ end
14
+
15
+ # specs
16
+ context "#[]=" do
17
+
18
+ it "places quality" do
19
+ # place initial value
20
+ @options.merge!({:q => 10})
21
+
22
+ # set new values
23
+ @options[:q] = 5
24
+ @options[:q].should == 5
25
+ @options[:quality].should == 5
26
+ # set new values
27
+ @options[:quality] = 7
28
+ @options[:q].should == 7
29
+ @options[:quality].should == 7
30
+ end
31
+
32
+ it "places width" do
33
+ # place initial value
34
+ @options.merge!({:width => 256})
35
+
36
+ # set new values
37
+ @options[:s] = 128
38
+ @options[:s].should == 128
39
+ @options[:width].should == 128
40
+
41
+ @options[:width] = 360
42
+ @options[:s].should == 360
43
+ @options[:width].should == 360
44
+ end
45
+
46
+ it "places time" do
47
+ # :time, :t
48
+ # place initial value
49
+ @options.merge!({:t => 150})
50
+
51
+ # set new values
52
+ @options[:t] = 45
53
+ @options[:t].should == 45
54
+ @options[:time].should == 45
55
+
56
+ @options[:time] = 89
57
+ @options[:t].should == 89
58
+ @options[:time].should == 89
59
+ end
60
+
61
+ it "places other" do
62
+ @options[:a] = 123
63
+ @options[:a].should == 123
64
+ end
65
+ end
66
+
67
+ context "#to_s" do
68
+
69
+ # [:thumb_count, :thumb_start_point, :quality, :width, :time, :postfix]
70
+ # skps ignored values
71
+ it "reates valid string representation" do
72
+
73
+ # still empty
74
+ @options.to_s.should == ""
75
+
76
+ # skips disallowed keywords
77
+ @options.merge! :thumb_count => 10,
78
+ :thumb_start_point => 25,
79
+ :quality => 5,
80
+ :width => 600,
81
+ :time => 123,
82
+ :postfix => "thumb",
83
+ :s => 640
84
+
85
+ @options.to_s.should == "-s 640"
86
+ end
87
+ end
88
+
89
+ context "#perform" do
90
+
91
+ it "converts valid data" do
92
+ values = { :quality => 5, :width => 600, :time => 123 }
93
+ @options.method(:perform).call values
94
+
95
+ values[:q].should == values[:quality]
96
+ values[:s].should == values[:width]
97
+ values[:t].should == values[:t]
98
+ end
99
+ end
100
+
101
+ context "#parse!" do
102
+
103
+ # set predefined data
104
+ VTools::CONFIG[:thumb_set][:w600] = [600, 8, 5, 0]
105
+
106
+ let :conf_hash do
107
+ { :s => 600, :q => 8, :thumb_count => 5, :thumb_start_point => 0 }
108
+ end
109
+
110
+ def make_stubs
111
+ @options.stub(:perform).and_return{ |hsh| hsh }
112
+ end
113
+
114
+ it "raises error on invlaid data" do
115
+ expect { @options.method(:parse!).call "123" }.to raise_error VTools::ConfigError
116
+ expect { @options.method(:parse!).call [] }.to raise_error VTools::ConfigError
117
+ end
118
+
119
+ it "accepts hash" do
120
+ make_stubs
121
+
122
+ @options.method(:parse!).call(:s => 600).should == { :s => 600,
123
+ :thumb_count=>0, :thumb_start_point=>0 }
124
+
125
+ @options.delete(:s)
126
+ @options.method(:parse!).call( :width => 600, :thumb_count=>2,
127
+ :thumb_start_point=>3 ).should == { :width => 600, :thumb_count=>2,
128
+ :thumb_start_point=>3 }
129
+ end
130
+
131
+ it "accepts string (predefined set)"do
132
+ make_stubs
133
+
134
+ @options.method(:parse!).call("w600").should include conf_hash
135
+ end
136
+
137
+ it "accepts mixed data" do
138
+ make_stubs
139
+ complex = conf_hash.dup
140
+ complex[:s] = 1024
141
+
142
+ @options.method(:parse!).call({:set => "w600", :s => 1024}).should include complex
143
+ end
144
+ end
145
+
146
+ context "#initialize" do
147
+
148
+ it "valid calls methods" do
149
+ config = { :width => 1024, :t => 7 }
150
+
151
+ VTools::ThumbsOptions.any_instance.should_receive(:parse!).with(config)
152
+
153
+ options = VTools::ThumbsOptions.new config
154
+
155
+ options[:thumb_count].should == 0
156
+ options[:thumb_start_point].should == 0
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,274 @@
1
+ require "spec_helper"
2
+ require "video"
3
+
4
+ describe VTools::Video do
5
+
6
+ # hooks
7
+ before do
8
+ VTools::CONFIG[:temp_dir] = ""
9
+ VTools::CONFIG[:PWD] = ""
10
+ VTools::Converter.stub!(:new) { nil }
11
+ VTools::Thumbnailer.stub!(:new) { nil }
12
+
13
+ @video = VTools::Video.new __FILE__
14
+ end
15
+
16
+ # specs
17
+ context "#to_json" do
18
+
19
+ # wtf with this rspec ?!
20
+ it "collects valid json" do
21
+
22
+ Open3.stub!(:popen3).and_return [nil, nil, StringIO.new("")]
23
+ @video.get_info
24
+
25
+ ignore = [:@convert_options, :@thumbs_options, :@converter, :@thumbnailer, :@uncertain_duration]
26
+ collect = {:path => nil, :name => nil, :duration => nil, :start => nil, :bitrate => nil, :video_stream => nil, :audio_stream => nil}
27
+
28
+ @video.instance_variables.inject({}) do |data, var|
29
+ data[ var[1..-1] ] = instance_variable_get(var) unless ignore.include? var.to_sym
30
+ data
31
+ end.should include collect
32
+ end
33
+ end
34
+
35
+ context "#create_thumbs" do
36
+
37
+ it "executes thumbnailer" do
38
+ thumb = double(nil)
39
+ @video.stub(:calculated_aspect_ratio).and_return { 1.2 }
40
+ @video.instance_variable_set(:@thumbnailer, thumb)
41
+
42
+ setup = { :thumb => "options" }
43
+ VTools::ThumbsOptions.should_receive(:new).with(setup.merge(:aspect => 1.2))
44
+ thumb.should_receive(:run)
45
+
46
+ @video.create_thumbs setup
47
+ end
48
+ end
49
+
50
+ context "#convert" do
51
+
52
+ it "executes converter" do
53
+ convert = double(nil)
54
+ @video.instance_variable_set(:@converter, convert)
55
+
56
+ setup = { :convert => "options" }
57
+ VTools::ConvertOptions.should_receive(:new).with(setup)
58
+ convert.should_receive(:run)
59
+
60
+ @video.convert setup
61
+ end
62
+ end
63
+
64
+ context "#get_info" do
65
+
66
+ let(:path) { "#{File.realpath(File.dirname(__FILE__))}/fixtures/outputs/" }
67
+
68
+ def stub_io
69
+ output_stub = StringIO.new(File.read(path))
70
+ Open3.stub!(:popen3).and_return([ nil, nil, output_stub ] )
71
+ @video.get_info
72
+ end
73
+
74
+ context "parses data valid" do
75
+
76
+ it "iso-8859-1" do
77
+ path << "file_with_iso-8859-1.txt"
78
+ stub_io
79
+
80
+ @video.should be_valid
81
+
82
+ @video.duration.should == 1482.6
83
+ @video.start.should == 0.0
84
+ @video.bitrate.should == 546
85
+
86
+ @video.video_bitrate.should == 480
87
+ @video.colorspace.should == "yuv420p"
88
+ @video.resolution.should == "1000x600"
89
+ @video.frame_rate.should == 25
90
+
91
+ @video.audio_sample_rate.should == 44100
92
+ @video.audio_bitrate.should == 64
93
+ @video.audio_channels.should == 1
94
+ @video.audio_codec.should == "aac"
95
+ end
96
+
97
+ it "non supported audio" do
98
+ path << "file_with_non_supported_audio.txt"
99
+ stub_io
100
+
101
+ @video.should_not be_valid
102
+ end
103
+
104
+ it "no audio" do
105
+ path << "file_with_no_audio.txt"
106
+ stub_io
107
+
108
+ @video.should be_valid
109
+ end
110
+
111
+ it "surround audio" do
112
+ path << "file_with_surround_sound.txt"
113
+ stub_io
114
+
115
+ @video.should be_valid
116
+ @video.audio_channels.should == 6
117
+ end
118
+
119
+ it "start value" do
120
+ path << "file_with_start_value.txt"
121
+ stub_io
122
+
123
+ @video.should be_valid
124
+ @video.start.should == 13.038000
125
+ end
126
+ end
127
+ end
128
+
129
+ context "#uncertain_duration?" do
130
+
131
+ it "returns uncertain_duration statement" do
132
+ @video.uncertain_duration?.should be
133
+
134
+ @video.instance_variable_set(:@uncertain_duration, false)
135
+
136
+ @video.uncertain_duration?.should_not be
137
+ end
138
+ end
139
+
140
+ context "#width" do
141
+
142
+ it "returns valid width" do
143
+ @video.stub(:resolution) { "1024x768" }
144
+
145
+ @video.width.should == 1024
146
+
147
+ @video.stub(:resolution) { "" }
148
+ @video.width.should == 0
149
+
150
+ @video.stub(:resolution) { nil }
151
+ expect { @video.width.should be nil }.to_not raise_error
152
+ end
153
+ end
154
+
155
+ context "#height" do
156
+
157
+ it "returns valid height" do
158
+ @video.stub(:resolution) { "1024x768" }
159
+
160
+ @video.height.should == 768
161
+
162
+ @video.stub(:resolution) { "" }
163
+ @video.height.should == 0
164
+
165
+ @video.stub(:resolution) { nil }
166
+ expect { @video.height.should be nil }.to_not raise_error
167
+ end
168
+ end
169
+
170
+ context "#calculated_aspect_ratio" do
171
+
172
+ it "returns dar statement" do
173
+ @video.stub(:dar) { "16:9" }
174
+ @video.calculated_aspect_ratio.should == (16.0/9.0)
175
+ end
176
+
177
+ it "returns width / height statement" do
178
+ @video.stub(:width) { 1024 }
179
+ @video.stub(:height) { 768 }
180
+
181
+ @video.calculated_aspect_ratio.should == (4.0/3.0)
182
+
183
+ @video.stub(:height) { nil }
184
+ @video.stub(:width) { "qwe" }
185
+
186
+ @video.calculated_aspect_ratio.should be nil
187
+
188
+ @video.stub(:height) { "asd" }
189
+
190
+ @video.calculated_aspect_ratio.should be nil
191
+ end
192
+ end
193
+
194
+ context "#size" do
195
+
196
+ it "responds with size" do
197
+ File.should_receive(:size).with( @video.instance_variable_get(:@path) )
198
+ @video.size
199
+ end
200
+ end
201
+
202
+ context "#audio_channels" do
203
+
204
+ it "returns valid channels count" do
205
+
206
+ @video.audio_channels.should_not be
207
+
208
+ @video.instance_variable_set(:@audio_channels, "5.1")
209
+ @video.audio_channels.should == 6
210
+
211
+ @video.instance_variable_set(:@audio_channels, "stereo")
212
+ @video.audio_channels.should == 2
213
+
214
+ @video.instance_variable_set(:@audio_channels, "mono")
215
+ @video.audio_channels.should == 1
216
+
217
+ @video.instance_variable_set(:@audio_channels, "3 channels")
218
+ @video.audio_channels.should == 3
219
+ end
220
+ end
221
+
222
+ context "#frame_rate" do
223
+
224
+ it "returns valid frame rate" do
225
+ @video.instance_variable_set(:@video_stream, "h264, yuv420p, 720x304, PAR 1:1 DAR 45:19, 23.98 tbr, 25fps, 1k tbn, 47.95 tbc")
226
+ @video.frame_rate.should == 25.0
227
+
228
+ @video.instance_variable_set(:@video_stream, "h264, yuv420p, 720x304, PAR 1:1 DAR 45:19, 23.98 tbr, 1k tbn, 47.95 tbc")
229
+ @video.frame_rate.should_not be
230
+ end
231
+ end
232
+
233
+ context "#set_path" do
234
+
235
+ it "sets valid path" do
236
+ VTools::CONFIG[:temp_dir] = ""
237
+ VTools::CONFIG[:PWD] = "pwd/dir"
238
+
239
+ @video.method(:set_path).call("test/path/").should == "pwd/dir/test/path/"
240
+
241
+ VTools::CONFIG[:temp_dir] = "temp/dir"
242
+ @video.method(:set_path).call("test/path/").should == "temp/dir/test/path/"
243
+
244
+ @video.method(:set_path).call("/test/path/").should == "/test/path/"
245
+
246
+ @video.method(:set_path).call("C://test/path/").should == "C://test/path/"
247
+ end
248
+ end
249
+
250
+ context "#initialize" do
251
+
252
+ it "crates instance valid" do
253
+ VTools::CONFIG[:PWD] = "/root"
254
+ File.stub!(:exists?) { true }
255
+ VTools::Converter.stub!(:new) { nil }
256
+ VTools::Thumbnailer.stub!(:new) { nil }
257
+
258
+ video = VTools::Video.new "test/path"
259
+
260
+ video.instance_variable_get(:@invalid).should be
261
+ video.instance_variable_get(:@uncertain_duration).should be
262
+ video.instance_variable_get(:@convert_options).should == {}
263
+ video.instance_variable_get(:@thumbs_options).should == {}
264
+
265
+ video.instance_variable_get(:@path).should == "/root/test/path"
266
+ video.instance_variable_get(:@name).should == "path"
267
+ end
268
+
269
+ it "raises error on nonexistent file" do
270
+ File.stub!(:exists?) { raise "test.error" }
271
+ expect { VTools::Video.new "nonextitent/path" }.to raise_error
272
+ end
273
+ end
274
+ end