handbrake 0.1.0 → 0.2.0

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.
@@ -1,3 +1,10 @@
1
+ 0.2.0
2
+ =====
3
+
4
+ - Support overwrite detection and behaviors for the
5
+ {HandBrake::CLI#output}.
6
+ - Support "atomic" output mode in {HandBrake::CLI#output}.
7
+
1
8
  0.1.0
2
9
  =====
3
10
 
data/README.md CHANGED
@@ -126,6 +126,13 @@ To put it more technically, the chain object returned from each
126
126
  intermediate configuration step returns an independent copy of the
127
127
  configuration chain.
128
128
 
129
+ ### Output options
130
+
131
+ By default, the `output` execution method behaves just like invoking
132
+ HandBrakeCLI directly. `handbrake.rb` also supports a couple of
133
+ additional behaviors, including overwrite detection and atomic output
134
+ file creation. See {HandBrake::CLI#output} for more details.
135
+
129
136
  Additional resources
130
137
  --------------------
131
138
 
@@ -8,4 +8,10 @@ module HandBrake
8
8
  autoload :Titles, 'handbrake/titles'
9
9
  autoload :Title, 'handbrake/titles'
10
10
  autoload :Chapter, 'handbrake/titles'
11
+
12
+ ##
13
+ # The exception thrown when a file exists that shouldn't.
14
+ #
15
+ # @see CLI#output
16
+ class FileExistsError < StandardError; end;
11
17
  end
@@ -59,11 +59,88 @@ module HandBrake
59
59
  # Performs a conversion. This method immediately begins the
60
60
  # transcoding process; set all other options first.
61
61
  #
62
+ # @param [String] filename the desired name for the final output
63
+ # file
64
+ # @param [Hash] options additional options to control the behavior
65
+ # of the output process
66
+ # @option options [Boolean,:ignore] :overwrite (true) determines
67
+ # the behavior if the desired output file already exists. If
68
+ # `true`, the file is replaced. If `false`, an exception is
69
+ # thrown. If `:ignore`, the file is skipped; i.e., HandBrakeCLI
70
+ # is not invoked.
71
+ # @option options [Boolean] :atomic (false) provides a
72
+ # pseudo-atomic mode for transcoded output. If true, the
73
+ # transcode will go into a temporary file and only be copied to
74
+ # the specified filename if it completes. The temporary filename
75
+ # is the target filename with `.handbraking` inserted before the
76
+ # extension. Any `:overwrite` checking will be applied to the
77
+ # target filename both before and after the transcode happens
78
+ # (the temporary file will always be overwritten). This option
79
+ # is intended to aid in writing automatically resumable batch
80
+ # scripts.
81
+ #
62
82
  # @return [void]
63
- def output(filename)
64
- run('--output', filename)
83
+ def output(filename, options={})
84
+ overwrite = options.delete :overwrite
85
+ case overwrite
86
+ when true, nil
87
+ # no special behavior
88
+ when false
89
+ raise FileExistsError, filename if File.exist?(filename)
90
+ when :ignore
91
+ if File.exist?(filename)
92
+ trace "Ignoring transcode to #{filename.inspect} because it already exists"
93
+ return
94
+ end
95
+ else
96
+ raise "Unsupported value for :overwrite: #{overwrite.inspect}"
97
+ end
98
+
99
+ atomic = options.delete :atomic
100
+ interim_filename =
101
+ if atomic
102
+ partial_filename(filename)
103
+ else
104
+ filename
105
+ end
106
+
107
+ unless options.empty?
108
+ raise "Unknown options for output: #{options.keys.inspect}"
109
+ end
110
+
111
+ run('--output', interim_filename)
112
+
113
+ if filename != interim_filename
114
+ replace =
115
+ if File.exist?(filename)
116
+ trace "#{filename.inspect} showed up during transcode"
117
+ case overwrite
118
+ when false
119
+ raise FileExistsError, filename
120
+ when :ignore
121
+ trace "- will leave #{filename.inspect} as is; copy #{interim_filename.inspect} manually if you want to replace it"
122
+ false
123
+ else
124
+ trace '- will replace with new transcode'
125
+ true
126
+ end
127
+ else
128
+ true
129
+ end
130
+ FileUtils.mv interim_filename, filename if replace
131
+ end
65
132
  end
66
133
 
134
+ def partial_filename(name)
135
+ if File.basename(name).index '.'
136
+ dot_at = name.rindex '.'
137
+ name.dup.insert dot_at, '.handbraking'
138
+ else
139
+ name + '.handbraking'
140
+ end
141
+ end
142
+ private :partial_filename
143
+
67
144
  ##
68
145
  # Performs a title scan. Unlike HandBrakeCLI, if you do not
69
146
  # specify a title, this method will return information for all
@@ -136,6 +213,10 @@ module HandBrake
136
213
  end
137
214
  end
138
215
 
216
+ def trace(msg)
217
+ $stderr.puts msg if trace?
218
+ end
219
+
139
220
  ##
140
221
  # Copies this CLI instance and appends another command line switch
141
222
  # plus optional arguments.
@@ -1,5 +1,5 @@
1
1
  module HandBrake
2
2
  ##
3
3
  # The current version
4
- VERSION = "0.1.0"
4
+ VERSION = '0.2.0'
5
5
  end
@@ -71,14 +71,167 @@ module HandBrake
71
71
  end
72
72
 
73
73
  it 'works' do
74
- lambda { cli.update }.should_not raise_error
74
+ lambda { cli.preset_list }.should_not raise_error
75
75
  end
76
76
  end
77
77
 
78
78
  describe '#output' do
79
+ let(:filename) { File.join(tmpdir, 'foo.m4v') }
80
+
81
+ def it_should_have_run(expected_fn=filename)
82
+ runner.actual_arguments.should == ['--output', expected_fn]
83
+ end
84
+
85
+ def it_should_not_have_run
86
+ runner.actual_arguments.should be_nil
87
+ end
88
+
79
89
  it 'uses the --output argument' do
80
- cli.output('/foo/bar.m4v')
81
- runner.actual_arguments.should == %w(--output /foo/bar.m4v)
90
+ cli.output(filename)
91
+ runner.actual_arguments.should == ['--output', filename]
92
+ end
93
+
94
+ it 'fails for an unknown option' do
95
+ lambda { cli.output(filename, :quux => 'zap') }.
96
+ should raise_error('Unknown options for output: [:quux]')
97
+ end
98
+
99
+ describe ':overwrite' do
100
+ context 'true' do
101
+ it 'executes when the file does not exist' do
102
+ cli.output(filename, :overwrite => true)
103
+ it_should_have_run
104
+ end
105
+
106
+ it 'executes when the file exists' do
107
+ FileUtils.touch filename
108
+ cli.output(filename, :overwrite => true)
109
+ it_should_have_run
110
+ end
111
+
112
+ it 'is the default' do
113
+ FileUtils.touch filename
114
+ cli.output(filename)
115
+ it_should_have_run
116
+ end
117
+ end
118
+
119
+ context 'false' do
120
+ it 'executes when the file does not exist' do
121
+ cli.output(filename, :overwrite => false)
122
+ it_should_have_run
123
+ end
124
+
125
+ it 'throws an exception when the file does exist' do
126
+ FileUtils.touch filename
127
+ lambda { cli.output(filename, :overwrite => false) }.
128
+ should raise_error(HandBrake::FileExistsError)
129
+ end
130
+ end
131
+
132
+ context ':ignore' do
133
+ it 'executes when the file does not exist' do
134
+ cli.output(filename, :overwrite => :ignore)
135
+ it_should_have_run
136
+ end
137
+
138
+ it 'does nothing when the file does exist' do
139
+ FileUtils.touch filename
140
+ cli.output(filename, :overwrite => :ignore)
141
+ it_should_not_have_run
142
+ end
143
+ end
144
+
145
+ context 'other value' do
146
+ it 'throws an exception' do
147
+ lambda { cli.output(filename, :overwrite => 'flip') }.
148
+ should raise_error('Unsupported value for :overwrite: "flip"')
149
+ end
150
+ end
151
+ end
152
+
153
+ describe ':atomic' do
154
+ context 'false' do
155
+ it 'executes normally' do
156
+ cli.output(filename, :atomic => false)
157
+ it_should_have_run
158
+ end
159
+
160
+ it 'is the default' do
161
+ cli.output(filename)
162
+ it_should_have_run
163
+ end
164
+ end
165
+
166
+ context 'true' do
167
+ let(:expected_temporary_file) { File.join(tmpdir, "foo.handbraking.m4v") }
168
+
169
+ it 'outputs to the temporary file with ".handbraking" inserted before the extension' do
170
+ cli.output(filename, :atomic => true)
171
+ it_should_have_run(expected_temporary_file)
172
+ end
173
+
174
+ it 'appends .handbraking if the output file does not have an extension' do
175
+ cli.output(File.join(tmpdir('a.b.c'), 'foo'), :atomic => true)
176
+ it_should_have_run(File.join(tmpdir('a.b.c'), 'foo.handbraking'))
177
+ end
178
+
179
+ it 'copies the output to the desired filename' do
180
+ cli.output(filename, :atomic => true)
181
+ File.read(filename).should == 'This is the file created by --output'
182
+ end
183
+
184
+ it 'removes the temporary file' do
185
+ cli.output(filename, :atomic => true)
186
+ File.exist?(expected_temporary_file).should be_false
187
+ end
188
+
189
+ it 'overwrites the temporary file if it exists' do
190
+ FileUtils.touch expected_temporary_file
191
+ cli.output(filename, :atomic => true)
192
+ it_should_have_run(expected_temporary_file)
193
+ end
194
+
195
+ describe 'and :overwrite' do
196
+ describe 'false' do
197
+ it 'throws an exception if the target filename exists initially' do
198
+ FileUtils.touch filename
199
+ lambda { cli.output(filename, :atomic => true, :overwrite => false) }.
200
+ should raise_error(HandBrake::FileExistsError)
201
+ end
202
+
203
+ it 'throws an exception if the target filename exists after output' do
204
+ runner.behavior = lambda { FileUtils.touch filename }
205
+ lambda { cli.output(filename, :atomic => true, :overwrite => false) }.
206
+ should raise_error(HandBrake::FileExistsError)
207
+ end
208
+ end
209
+
210
+ describe ':ignore' do
211
+ it 'does nothing if the target filename exists initially' do
212
+ FileUtils.touch filename
213
+ cli.output(filename, :atomic => true, :overwrite => :ignore)
214
+ it_should_not_have_run
215
+ end
216
+
217
+ it 'does not copy the output if the target filename exists after encoding' do
218
+ runner.behavior = lambda {
219
+ File.open(filename, 'w') { |f| f.write 'Other process result' }
220
+ }
221
+ cli.output(filename, :atomic => true, :overwrite => :ignore)
222
+ File.read(filename).should == 'Other process result'
223
+ end
224
+
225
+ it 'does not remove the temporary output if the target filename exists after encoding' do
226
+ runner.behavior = lambda {
227
+ File.open(filename, 'w') { |f| f.write 'Other process result' }
228
+ }
229
+ cli.output(filename, :atomic => true, :overwrite => :ignore)
230
+ File.exist?(expected_temporary_file).should be_true
231
+ end
232
+ end
233
+ end
234
+ end
82
235
  end
83
236
  end
84
237
 
@@ -2,38 +2,29 @@ require 'bundler'
2
2
  Bundler.setup
3
3
 
4
4
  require 'rspec'
5
+ require 'fileutils'
5
6
 
6
7
  $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
7
8
 
8
9
  require 'handbrake'
9
10
 
10
11
  RSpec.configure do |config|
11
- # Captures everything printed to stdout during the block
12
- # and returns it as a string.
13
- def capture_stdout
14
- old_stdout, $stdout = $stdout, StringIO.new
15
- begin
16
- yield
17
- $stdout.string
18
- ensure
19
- $stdout = old_stdout
20
- end
21
- end
22
-
23
12
  def tmpdir(sub=nil)
24
13
  @tmpdir ||= begin
25
14
  dirname = File.expand_path("../tmp", __FILE__)
26
- mkdir_p dirname
15
+ FileUtils.mkdir_p dirname
27
16
  dirname
28
17
  end
29
18
  if sub
30
19
  full = File.join(@tmpdir, sub)
31
- mkdir_p full
20
+ FileUtils.mkdir_p full
32
21
  full
33
22
  else
34
23
  @tmpdir
35
24
  end
36
25
  end
26
+
27
+ config.after { FileUtils.rm_rf @tmpdir if @tmpdir }
37
28
  end
38
29
 
39
30
  module HandBrake
@@ -42,10 +33,22 @@ module HandBrake
42
33
  # A stub implementation of the runner for HandBrake::CLI.
43
34
  class StaticRunner
44
35
  attr_accessor :output, :status
36
+ ##
37
+ # A lambda that specifies desired side effects of execution or
38
+ # simulates things happening outside the execution but
39
+ # simultaneous with it.
40
+ attr_accessor :behavior
45
41
  attr_reader :actual_arguments
46
42
 
47
43
  def run(args)
48
44
  @actual_arguments = args
45
+ if i = args.index('--output')
46
+ fn = args[i + 1]
47
+ File.open(fn, 'w') { |f| f.write 'This is the file created by --output' }
48
+ end
49
+ if behavior
50
+ behavior.call
51
+ end
49
52
  HandBrake::CLI::RunnerResult.new(output, status)
50
53
  end
51
54
 
metadata CHANGED
@@ -1,96 +1,68 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: handbrake
3
- version: !ruby/object:Gem::Version
4
- hash: 27
5
- prerelease: false
6
- segments:
7
- - 0
8
- - 1
9
- - 0
10
- version: 0.1.0
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ prerelease:
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Rhett Sutphin
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2011-06-15 00:00:00 -05:00
19
- default_executable:
20
- dependencies:
21
- - !ruby/object:Gem::Dependency
12
+ date: 2011-08-07 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
22
15
  name: rubytree
23
- prerelease: false
24
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: &2153799900 !ruby/object:Gem::Requirement
25
17
  none: false
26
- requirements:
18
+ requirements:
27
19
  - - ~>
28
- - !ruby/object:Gem::Version
29
- hash: 61
30
- segments:
31
- - 0
32
- - 8
33
- - 1
20
+ - !ruby/object:Gem::Version
34
21
  version: 0.8.1
35
22
  type: :runtime
36
- version_requirements: *id001
37
- - !ruby/object:Gem::Dependency
38
- name: rspec
39
23
  prerelease: false
40
- requirement: &id002 !ruby/object:Gem::Requirement
24
+ version_requirements: *2153799900
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec
27
+ requirement: &2153799400 !ruby/object:Gem::Requirement
41
28
  none: false
42
- requirements:
29
+ requirements:
43
30
  - - ~>
44
- - !ruby/object:Gem::Version
45
- hash: 9
46
- segments:
47
- - 2
48
- - 5
49
- version: "2.5"
31
+ - !ruby/object:Gem::Version
32
+ version: '2.5'
50
33
  type: :development
51
- version_requirements: *id002
52
- - !ruby/object:Gem::Dependency
53
- name: rake
54
34
  prerelease: false
55
- requirement: &id003 !ruby/object:Gem::Requirement
35
+ version_requirements: *2153799400
36
+ - !ruby/object:Gem::Dependency
37
+ name: rake
38
+ requirement: &2153798940 !ruby/object:Gem::Requirement
56
39
  none: false
57
- requirements:
40
+ requirements:
58
41
  - - ~>
59
- - !ruby/object:Gem::Version
60
- hash: 59
61
- segments:
62
- - 0
63
- - 9
64
- - 0
42
+ - !ruby/object:Gem::Version
65
43
  version: 0.9.0
66
44
  type: :development
67
- version_requirements: *id003
68
- - !ruby/object:Gem::Dependency
69
- name: yard
70
45
  prerelease: false
71
- requirement: &id004 !ruby/object:Gem::Requirement
46
+ version_requirements: *2153798940
47
+ - !ruby/object:Gem::Dependency
48
+ name: yard
49
+ requirement: &2153798480 !ruby/object:Gem::Requirement
72
50
  none: false
73
- requirements:
51
+ requirements:
74
52
  - - ~>
75
- - !ruby/object:Gem::Version
76
- hash: 3
77
- segments:
78
- - 0
79
- - 7
80
- - 0
53
+ - !ruby/object:Gem::Version
81
54
  version: 0.7.0
82
55
  type: :development
83
- version_requirements: *id004
84
- description: A lightweight literate ruby wrapper for HandBrakeCLI, the command-line interface for the HandBrake video transcoder.
85
- email:
56
+ prerelease: false
57
+ version_requirements: *2153798480
58
+ description: A lightweight literate ruby wrapper for HandBrakeCLI, the command-line
59
+ interface for the HandBrake video transcoder.
60
+ email:
86
61
  - rhett@detailedbalance.net
87
62
  executables: []
88
-
89
63
  extensions: []
90
-
91
64
  extra_rdoc_files: []
92
-
93
- files:
65
+ files:
94
66
  - .gitignore
95
67
  - .rvmrc
96
68
  - .travis.yml
@@ -112,41 +84,31 @@ files:
112
84
  - spec/handbrake/titles_spec.rb
113
85
  - spec/handbrake/version_spec.rb
114
86
  - spec/spec_helper.rb
115
- has_rdoc: true
116
87
  homepage: https://github.com/rsutphin/handbrake.rb
117
88
  licenses: []
118
-
119
89
  post_install_message:
120
90
  rdoc_options: []
121
-
122
- require_paths:
91
+ require_paths:
123
92
  - lib
124
- required_ruby_version: !ruby/object:Gem::Requirement
93
+ required_ruby_version: !ruby/object:Gem::Requirement
125
94
  none: false
126
- requirements:
127
- - - ">="
128
- - !ruby/object:Gem::Version
129
- hash: 3
130
- segments:
131
- - 0
132
- version: "0"
133
- required_rubygems_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
134
100
  none: false
135
- requirements:
136
- - - ">="
137
- - !ruby/object:Gem::Version
138
- hash: 3
139
- segments:
140
- - 0
141
- version: "0"
101
+ requirements:
102
+ - - ! '>='
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
142
105
  requirements: []
143
-
144
106
  rubyforge_project:
145
- rubygems_version: 1.3.7
107
+ rubygems_version: 1.8.6
146
108
  signing_key:
147
109
  specification_version: 3
148
110
  summary: A ruby wrapper for HandBrakeCLI
149
- test_files:
111
+ test_files:
150
112
  - spec/handbrake/cli_spec.rb
151
113
  - spec/handbrake/sample-preset-list.out
152
114
  - spec/handbrake/sample-titles-scan.err