vtools 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.
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